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)