class HttpClient(HttpSocket): def __init__(self, socket, address, server): HttpSocket.__init__(self, socket) self.address = address self.reader = SocketReader(self.socket) self.server = server self.version = HTTP_11 def __iter__(self): while True: request = self.recv() if request is None: break yield request def iter_wsgi(self): while True: environ = self.recv(True) if environ is None: break yield environ def close(self): try: if self.reader is not None: self.reader.close() except Exception: pass finally: self.reader = None HttpSocket.close(self) def recv(self, wsgi=False): try: stream = HttpStream(self.reader, kind=HTTP_REQUEST, parser_class=HttpParser, decompress=True) if bool(wsgi): environ = stream.wsgi_environ() environ['wsgi.url_scheme'] = guess_scheme(environ) environ['wsgi.input'] = stream.body_file() environ['wsgi.socket'] = self.socket return environ # BUG: # http-parser has an issue here, if we call 'method' before 'headers' # and invalid method name is returned... fields = stream.headers() method = stream.method() url = stream.url() version = stream.version() content = stream.body_file() url = urlparse(url) path = url.path query = parse_qs(url.query, keep_blank_values=True) fragment = url.fragment for k, v in six.iteritems(dict(query)): query[k] = parse_query_value(v) self.version = 'HTTP/%s.%s' % version return method, path, query, fragment, fields, content except NoMoreData: pass def send(self, status, fields=None, content=None, version=None): if fields is None: fields = { } elif not isinstance(fields, HttpFields): fields = HttpFields(fields) if content is None: content = b'' if version is None: version = self.version assert version in (HTTP_10, HTTP_11), "invalid http version: %s" % version fields.setdefault('Content-Length', six.text_type(len(content))) if self.server is not None: fields.setdefault('Server', self.server) if isinstance(status, int): status = '%s %s' % (status, reasons[status]) header = '' header += '%s %s\r\n' % (version, status) header += ''.join('%s: %s\r\n' % (k, v) for k, v in six.iteritems(fields)) header += '\r\n' header = header.encode('utf-8') return self.socket.sendall(header + content)
class HttpConnection(HttpSocket): def __init__(self, socket=None, host=None): HttpSocket.__init__(self, socket) self.version = HTTP_11 self.host = host self.reader = None if socket is None else SocketReader(self.socket) def connect(self, host, port='http', timeout=None, secure=None, **kwargs): assert self.socket is None, "http connection already established" if secure is None and port == 'https': secure = True self.socket = net.connect(host, port, timeout=timeout, secure=secure, **kwargs) self.reader = SocketReader(self.socket) self.host = host if port not in ('http', 'https'): self.host += ':%s' % port def close(self): try: if self.reader is not None: self.reader.close() except Exception: pass finally: self.reader = None self.host = None HttpSocket.close(self) def shutdown(self): self.socket.shutdown() def recv(self): try: stream = HttpStream(self.reader, kind=HTTP_RESPONSE, parser_class=HttpParser, decompress=True) status = stream.status_code() version = stream.version() fields = stream.headers() content = stream.body_file() self.version = 'HTTP/%s.%s' % version return status, fields, content except NoMoreData: pass def send(self, method, path, query=None, fragment=None, fields=None, content=None, version=None): if fields is None: fields = HttpFields() elif not isinstance(fields, HttpFields): fields = HttpFields(fields) if query is None: query = { } if content is None: content = b'' if version is None: version = self.version assert version in (HTTP_10, HTTP_11), "invalid http version: %s" % version fields.setdefault('Content-Length', six.text_type(len(content))) fields.setdefault('Host', self.host) for k, v in six.iteritems(dict(query)): query[k] = format_query_value(v) if six.PY3: query = urlencode(query, encoding='utf-8') else: query = urlencode(query) header = '' header += method header += ' ' header += urlunparse(('', '', path, '', query, fragment if bool(fragment) else '')) header += ' %s\r\n' % version header += ''.join('%s: %s\r\n' % (k, v) for k, v in six.iteritems(fields)) header += '\r\n' header = header.encode('utf-8') self.socket.sendall(header + content) def request(self, *args, **kwargs): self.send(*args, **kwargs) return self.recv() def delete(self, *args, **kwargs): return self.request('DELETE', *args, **kwargs) def get(self, *args, **kwargs): return self.request('GET', *args, **kwargs) def head(self, *args, **kwargs): return self.request('HEAD', *args, **kwargs) def options(self, *args, **kwargs): return self.request('OPTIONS', *args, **kwargs) def post(self, *args, **kwargs): return self.request('POST', *args, **kwargs) def put(self, *args, **kwargs): return self.request('PUT', *args, **kwargs)