Sends an HTTP request.

        This constructs a full URL, encodes and decodes HTTP bodies, and
        handles invalid responses in a pythonic way.

        @type method: string
        @param method: HTTP method to use
        @type path: string
        @param path: HTTP URL path
        @type query: list of two-tuples
        @param query: query arguments to pass to urllib.urlencode
        @type content: str or None
        @param content: HTTP body content

        @rtype: object
        @return: JSON-Decoded response

        @raises GanetiApiError: If an invalid response is returned

def request(self, method, path, query=None, content=None):
        """
        Sends an HTTP request.

        This constructs a full URL, encodes and decodes HTTP bodies, and
        handles invalid responses in a pythonic way.

        @type method: string
        @param method: HTTP method to use
        @type path: string
        @param path: HTTP URL path
        @type query: list of two-tuples
        @param query: query arguments to pass to urllib.urlencode
        @type content: str or None
        @param content: HTTP body content

        @rtype: object
        @return: JSON-Decoded response

        @raises GanetiApiError: If an invalid response is returned
        """

        if not path.startswith("/"):
            raise ClientError("Implementation error: Called with bad path %s"
                              % path)

        body = None

        if content is not None:
            data = self._json_encoder.encode(content)
            body = StringProducer(data)

        url = self._base_url + path

        if query:
            prepare_query(query)
            params = urlencode(query, doseq=True)
            url += "?%s" % params

        log.msg("Sending request to %s %s %s" % (url, self.headers, body),
                system="Gentleman")

        d = self._agent.request(method, url, headers=self.headers,
                                bodyProducer=body)

        protocol = JsonResponseProtocol(d)

        @d.addErrback
        def connectionFailed(failure):
            failure.trap(ConnectionRefusedError)
            raise GanetiApiError("Connection refused!")

        @d.addCallback
        def cb(response):
            if response.code != 200:
                raise NotOkayError(code=response.code)
            response.deliverBody(protocol)

        return protocol.getData()