Forking what is left of ZeroNet and hopefully adding an AT Proto Frontend/Proxy
at main 101 lines 2.9 kB view raw
1import os 2import struct 3import io 4 5import msgpack 6import msgpack.fallback 7 8 9def msgpackHeader(size): 10 if size <= 2 ** 8 - 1: 11 return b"\xc4" + struct.pack("B", size) 12 elif size <= 2 ** 16 - 1: 13 return b"\xc5" + struct.pack(">H", size) 14 elif size <= 2 ** 32 - 1: 15 return b"\xc6" + struct.pack(">I", size) 16 else: 17 raise Exception("huge binary string") 18 19 20def stream(data, writer): 21 packer = msgpack.Packer(use_bin_type=True) 22 writer(packer.pack_map_header(len(data))) 23 for key, val in data.items(): 24 writer(packer.pack(key)) 25 if isinstance(val, io.IOBase): # File obj 26 max_size = os.fstat(val.fileno()).st_size - val.tell() 27 size = min(max_size, val.read_bytes) 28 bytes_left = size 29 writer(msgpackHeader(size)) 30 buff = 1024 * 64 31 while 1: 32 writer(val.read(min(bytes_left, buff))) 33 bytes_left = bytes_left - buff 34 if bytes_left <= 0: 35 break 36 else: # Simple 37 writer(packer.pack(val)) 38 return size 39 40 41class FilePart(object): 42 __slots__ = ("file", "read_bytes", "__class__") 43 44 def __init__(self, *args, **kwargs): 45 self.file = open(*args, **kwargs) 46 self.__enter__ == self.file.__enter__ 47 48 def __getattr__(self, attr): 49 return getattr(self.file, attr) 50 51 def __enter__(self, *args, **kwargs): 52 return self.file.__enter__(*args, **kwargs) 53 54 def __exit__(self, *args, **kwargs): 55 return self.file.__exit__(*args, **kwargs) 56 57 58# Don't try to decode the value of these fields as utf8 59bin_value_keys = ("hashfield_raw", "peers", "peers_ipv6", "peers_onion", "body", "sites", "bin") 60 61 62def objectDecoderHook(obj): 63 global bin_value_keys 64 back = {} 65 for key, val in obj: 66 if type(key) is bytes: 67 key = key.decode("utf8") 68 if key in bin_value_keys or type(val) is not bytes or len(key) >= 64: 69 back[key] = val 70 else: 71 back[key] = val.decode("utf8") 72 return back 73 74 75def getUnpacker(fallback=False, decode=True): 76 if fallback: # Pure Python 77 unpacker = msgpack.fallback.Unpacker 78 else: 79 unpacker = msgpack.Unpacker 80 81 extra_kwargs = {"max_buffer_size": 5 * 1024 * 1024} 82 if msgpack.version[0] >= 1: 83 extra_kwargs["strict_map_key"] = False 84 85 if decode: # Workaround for backward compatibility: Try to decode bin to str 86 unpacker = unpacker(raw=True, object_pairs_hook=objectDecoderHook, **extra_kwargs) 87 else: 88 unpacker = unpacker(raw=False, **extra_kwargs) 89 90 return unpacker 91 92 93def pack(data, use_bin_type=True): 94 return msgpack.packb(data, use_bin_type=use_bin_type) 95 96 97def unpack(data, decode=True): 98 unpacker = getUnpacker(decode=decode) 99 unpacker.feed(data) 100 return next(unpacker) 101