def __init__(self, data): req_line, headers = data.split('\r\n', 1) # HTTPMessage has no proper __repr__, so let's use the dictionary dict = HTTPMessage(StringIO(headers)).dict.copy() # all header names in the UPnP specs are uppercase self.headers = {k.upper(): v for k, v in dict.items()} method = req_line.split(' ')[0] self._notify = method == 'NOTIFY' # Unique Service Name => Unique Device Name + Type usn = self.headers.get('USN') self._udn, self._type = split_usn(usn) if usn else (None, None)
class MockHttpLibResponse(BytesIO): def __init__(self, data): BytesIO.__init__(self, data) self.status = 200 self.version = 'HTTP/1.1' self.reason = 'OK' if PY2: self.msg = HTTPMessage(BytesIO('Content-Type: application/x-compressed\r\n')) else: self.msg = HTTPMessage() self.msg.add_header('Content-Type', 'application/x-compressed') def getheaders(self): return list(self.msg.items()) def isclosed(self): return self.closed
class MockHttpLibResponse(BytesIO): def __init__(self, data): BytesIO.__init__(self, data) self.status = 200 self.version = 'HTTP/1.1' self.reason = 'OK' if PY2: self.msg = HTTPMessage(BytesIO(b'Content-Type: application/x-compressed\r\n')) else: self.msg = HTTPMessage() self.msg.add_header('Content-Type', 'application/x-compressed') def getheaders(self): return list(self.msg.items()) def isclosed(self): return self.closed
class ReplayHTTPResponse(object): """ A replay response object, with just enough functionality to make the various HTTP/URL libraries out there happy. """ __text_content_types = ( 'text/', 'application/json', ) def __init__(self, replay_response, method=None): self.reason = replay_response['status']['message'] self.status = replay_response['status']['code'] self.version = None if 'body_text' in replay_response: # JSON decoder returns unicode, not str, so this needs to be # encoded to properly reproduce content off the wire. self._content = replay_response['body_text'].encode('utf8') elif 'body_quoted_printable' in replay_response: # quopri.decodestring returns str, which is correct for content off # the wire. self._content = quopri.decodestring(replay_response['body_quoted_printable']) else: # .decode('base64') returns str, which is correct for content off # the wire. self._content = replay_response['body'].decode('base64') self.fp = StringIO(self._content) msg_fp = StringIO('\r\n'.join('{}: {}'.format(h, v) for h, v in replay_response['headers'].iteritems())) self.msg = HTTPMessage(msg_fp) self.msg.fp = None # httplib does this, okay? length = self.msg.getheader('content-length') self.length = int(length) if length else None # Save method to handle HEAD specially as httplib does self._method = method @classmethod def make_replay_response(cls, response): """ Converts real response to replay_response dict which can be saved and/or used to initialize a ReplayHTTPResponse. """ replay_response = {} body = response.read() # undecoded byte string # Add body to replay_response. Try to use simple text, falling back to # quoted printable or base64 as required for binary responses. if response.getheader('content-type', '') \ .startswith(cls.__text_content_types): if response.getheader('content-encoding') in ['gzip', 'deflate']: # http://stackoverflow.com/questions/2695152 body = zlib.decompress(body, 16 + zlib.MAX_WBITS) del response.msg['content-encoding'] # decompression changes the length if 'content-length' in response.msg: response.msg['content-length'] = str(len(body)) try: # Store body directly as text if it will decode properly. body.decode('utf8') replay_response['body_text'] = body except UnicodeDecodeError: # Store body as quoted printable. # Remove unneccessary =\n pairs which make searching hard. # These exist for line-wrapping in email, which is entirely # pointless here. body_quoted_printable = quopri.encodestring(body) body_quoted_printable = body_quoted_printable.replace('=\n', '') replay_response['body_quoted_printable'] = body_quoted_printable else: replay_response['body'] = body.encode('base64') replay_response.update(dict( status=dict(code=response.status, message=response.reason), headers=dict(response.getheaders()))) return replay_response def close(self): self.fp = None def isclosed(self): return self.fp is None def read(self, amt=None): """ The important parts of HTTPResponse.read() """ if self.fp is None: return '' if self._method == 'HEAD': self.close() return '' if self.length is not None: amt = min(amt, self.length) # StringIO doesn't like read(None) s = self.fp.read() if amt is None else self.fp.read(amt) if not s: self.close() if self.length is not None: self.length -= len(s) if not self.length: self.close() return s def getheader(self, name, default=None): return self.msg.getheader(name, default) def getheaders(self): return self.msg.items()
class ReplayHTTPResponse(object): """ A replay response object, with just enough functionality to make the various HTTP/URL libraries out there happy. """ __text_content_types = ( 'text/', 'application/json', ) def __init__(self, replay_response, method=None): self.reason = replay_response['status']['message'] self.status = replay_response['status']['code'] self.version = None if 'body_quoted_printable' in replay_response: self._content = quopri.decodestring(replay_response['body_quoted_printable']) else: self._content = replay_response['body'].decode('base64') self.fp = StringIO(self._content) msg_fp = StringIO('\r\n'.join('{}: {}'.format(h, v) for h, v in replay_response['headers'].iteritems())) self.msg = HTTPMessage(msg_fp) self.msg.fp = None # httplib does this, okay? length = self.msg.getheader('content-length') self.length = int(length) if length else None # Save method to handle HEAD specially as httplib does self._method = method @classmethod def make_replay_response(cls, response): """ Converts real response to replay_response dict which can be saved and/or used to initialize a ReplayHTTPResponse. """ replay_response = {} body = response.read() # undecoded byte string # Add body to replay_response, either as quoted printable for # text responses or base64 for binary responses. if response.getheader('content-type', '') \ .startswith(cls.__text_content_types): if response.getheader('content-encoding') in ['gzip', 'deflate']: # http://stackoverflow.com/questions/2695152 body = zlib.decompress(body, 16 + zlib.MAX_WBITS) del response.msg['content-encoding'] # decompression changes the length if 'content-length' in response.msg: response.msg['content-length'] = str(len(body)) replay_response['body_quoted_printable'] = quopri.encodestring(body) else: replay_response['body'] = body.encode('base64') replay_response.update(dict( status=dict(code=response.status, message=response.reason), headers=dict(response.getheaders()))) return replay_response def close(self): self.fp = None def isclosed(self): return self.fp is None def read(self, amt=None): """ The important parts of HTTPResponse.read() """ if self.fp is None: return '' if self._method == 'HEAD': self.close() return '' if self.length is not None: amt = min(amt, self.length) # StringIO doesn't like read(None) s = self.fp.read() if amt is None else self.fp.read(amt) if not s: self.close() if self.length is not None: self.length -= len(s) if not self.length: self.close() return s def getheader(self, name, default=None): return self.msg.getheader(name, default) def getheaders(self): return self.msg.items()
class ReplayHTTPResponse(object): """ A replay response object, with just enough functionality to make the various HTTP/URL libraries out there happy. """ __text_content_types = ( 'text/', 'application/json', ) def __init__(self, replay_response, method=None): self.reason = replay_response['status']['message'] self.status = replay_response['status']['code'] self.version = None if 'body_quoted_printable' in replay_response: self._content = quopri.decodestring( replay_response['body_quoted_printable']) else: self._content = replay_response['body'].decode('base64') self.fp = StringIO(self._content) msg_fp = StringIO('\r\n'.join( '{}: {}'.format(h, v) for h, v in replay_response['headers'].iteritems())) self.msg = HTTPMessage(msg_fp) self.msg.fp = None # httplib does this, okay? length = self.msg.getheader('content-length') self.length = int(length) if length else None # Save method to handle HEAD specially as httplib does self._method = method @classmethod def make_replay_response(cls, response): """ Converts real response to replay_response dict which can be saved and/or used to initialize a ReplayHTTPResponse. """ replay_response = {} body = response.read() # undecoded byte string # Add body to replay_response, either as quoted printable for # text responses or base64 for binary responses. if response.getheader('content-type', '') \ .startswith(cls.__text_content_types): if response.getheader('content-encoding') in ['gzip', 'deflate']: # http://stackoverflow.com/questions/2695152 body = zlib.decompress(body, 16 + zlib.MAX_WBITS) del response.msg['content-encoding'] # decompression changes the length if 'content-length' in response.msg: response.msg['content-length'] = str(len(body)) replay_response['body_quoted_printable'] = quopri.encodestring( body) else: replay_response['body'] = body.encode('base64') replay_response.update( dict(status=dict(code=response.status, message=response.reason), headers=dict(response.getheaders()))) return replay_response def close(self): self.fp = None def isclosed(self): return self.fp is None def read(self, amt=None): """ The important parts of HTTPResponse.read() """ if self.fp is None: return '' if self._method == 'HEAD': self.close() return '' if self.length is not None: amt = min(amt, self.length) # StringIO doesn't like read(None) s = self.fp.read() if amt is None else self.fp.read(amt) if not s: self.close() if self.length is not None: self.length -= len(s) if not self.length: self.close() return s def getheader(self, name, default=None): return self.msg.getheader(name, default) def getheaders(self): return self.msg.items()