'Creates and sends a request to the given server
Args:
destination (str): The remote server to send the HTTP request to.
method (str): HTTP method
path (str): The HTTP path
ignore_backoff (bool): true to ignore the historical backoff data
and try the request anyway.
backoff_on_404 (bool): Back off if we get a 404
Returns:
Deferred: resolves with the http response object on success.
Fails with ``HTTPRequestException``: if we get an HTTP response
code >= 300.
Fails with ``NotRetryingDestination`` if we are not yet ready
to retry this server.
(May also fail with plenty of other Exceptions for things like DNS
failures, connection failures, SSL failures.)'

@defer.inlineCallbacks
def _request(self, destination, method, path, body_callback, headers_dict={}, param_bytes='', query_bytes='', retry_on_dns_fail=True, timeout=None, long_retries=False, ignore_backoff=False, backoff_on_404=False):

    limiter = (yield synapse.util.retryutils.get_retry_limiter(destination, self.clock, self._store, backoff_on_404=backoff_on_404, ignore_backoff=ignore_backoff))
    destination = destination.encode('ascii')
    path_bytes = path.encode('ascii')
    with limiter:
        headers_dict['User-Agent'] = [self.version_string]
        headers_dict['Host'] = [destination]
        url_bytes = self._create_url(destination, path_bytes, param_bytes, query_bytes)
        txn_id = ('%s-O-%s' % (method, self._next_id))
        self._next_id = ((self._next_id + 1) % (sys.maxint - 1))
        outbound_logger.info('{%s}    [%s]    Sending    request:    %s    %s', txn_id, destination, method, url_bytes)
        if long_retries:
            retries_left = MAX_LONG_RETRIES
        else:
            retries_left = MAX_SHORT_RETRIES
        http_url_bytes = urlparse.urlunparse(('', '', path_bytes, param_bytes, query_bytes, ''))
        log_result = None
        try:
            while True:
                producer = None
                if body_callback:
                    producer = body_callback(method, http_url_bytes, headers_dict)
                try:
                    def send_request():
                        request_deferred = self.agent.request(method, url_bytes, Headers(headers_dict), producer)
                        return self.clock.time_bound_deferred(request_deferred, time_out=((timeout / 1000.0) if timeout else 60))
                    with logcontext.PreserveLoggingContext():
                        response = (yield send_request())
                    log_result = ('%d    %s' % (response.code, response.phrase))
                    break
                except Exception as e:
                    if ((not retry_on_dns_fail) and isinstance(e, DNSLookupError)):
                        logger.warn('DNS    Lookup    failed    to    %s    with    %s', destination, e)
                        log_result = ('DNS    Lookup    failed    to    %s    with    %s' % (destination, e))
                        raise
                    logger.warn('{%s}    Sending    request    failed    to    %s:    %s    %s:    %s    -    %s', txn_id, destination, method, url_bytes, type(e).__name__, _flatten_response_never_received(e))
                    log_result = ('%s    -    %s' % (type(e).__name__, _flatten_response_never_received(e)))
                    if (retries_left and (not timeout)):
                        if long_retries:
                            delay = (4 ** ((MAX_LONG_RETRIES + 1) - retries_left))
                            delay = min(delay, 60)
                            delay *= random.uniform(0.8, 1.4)
                        else:
                            delay = (0.5 * (2 ** (MAX_SHORT_RETRIES - retries_left)))
                            delay = min(delay, 2)
                            delay *= random.uniform(0.8, 1.4)
                        (yield sleep(delay))
                        retries_left -= 1
                    else:
                        raise
        finally:
            outbound_logger.info('{%s}    [%s]    Result:    %s', txn_id, destination, log_result)
        if (200 <= response.code < 300):
            pass
        else:
            with logcontext.PreserveLoggingContext():
                body = (yield readBody(response))
            raise HttpResponseException(response.code, response.phrase, body)
        defer.returnValue(response)
