class HttpLengthBody(): u""" http 实体类(Length 类型、同时支持关闭连接类型) 头部空行结束后就是实体,实体直接按长度结束,结束时并没有多余的换行。 """ def __init__(self, sock=None, length=None): u"""初始化""" if sock: self.sock = sock else: self.sock = BytesIO() self.length = length self.readed_length = 0 def _recv(self, size): if hasattr(self.sock, "recv"): return self.sock.recv(size) else: return self.sock.read(size) def recv(self, size): u"""读取 正常结束时返回空,非正常结束(连接断开)时引发异常。 """ if self.length is None: return self._recv(size) else: assert self.readed_length <= self.length if self.readed_length == self.length: return b"" else: remain_length = self.length - self.readed_length if size > remain_length: size = remain_length data = self.recv(size) self.readed_length += len(data) if not data and size != 0: raise Exception(u'连接异常关闭,数据未全部传输完成!') return data
class HttpChunkedBody(): u"""chunked 格式 body 提供带 chunked head 及不带 head 两种输出。 但是不提供gzip等解压操作。 """ def __init__(self, sock=None, chunked_head=False): u"""初始化 sock mysocket 实例或 BytesIO 实例,需要包含 readline 方法 chunked_head 是否输出 chunked head """ if sock: self.sock = sock else: self.sock = BytesIO() self.chunked_head = chunked_head self.chunked_length = None self.readed_length = 0 def _recv(self, size): if hasattr(self.sock, "recv"): return self.sock.recv(size) else: return self.sock.read(size) def recv_chunked_head(self): str_length = self.sock.readline() if not str_length: raise Exception(u'连接非正常结束!') self.chunked_length = int(str_length, 16) self.readed_length = 0 return str_length def recv(self, size): u"""读取 正常结束时返回空,非正常结束(连接断开)时引发异常。 """ res = BytesIO() if self.chunked_length == 0: # 读到结束块结束 # 尾头域另行读取 return res.getvalue() if self.chunked_length is None or \ self.chunked_length == self.readed_length: # 第一次读取 或 当前块读取结束 if self.chunked_head: res.write(self.recv_chunked_head()) data = self._recv(self.chunked_length - self.readed_length) if self.chunked_length != 0 and (not data): raise Exception(u'连接非正常结束!') self.readed_length += len(data) assert self.readed_length <= self.chunked_length res.write(data) if self.chunked_length == self.readed_length: # 每个块结束都有 \r\n,并且不包含在块长度里面。 end_line = (self.sock.readline(2)) if end_line[-1] != '\n': raise Exception(u'非正常的块结束标记. endline:%s' % end_line) if self.chunked_head: res.write(end_line) return res.getvalue()