'Request a given `action`.
Basically a wrapper around the connection
object\'s `request` that does some helpful pre-processing.
:type action: ``str``
:param action: A path. This can include arguments. If included,
any extra parameters are appended to the existing ones.
:type params: ``dict``
:param params: Optional mapping of additional parameters to send. If
None, leave as an empty ``dict``.
:type data: ``unicode``
:param data: A body of data to send with the request.
:type headers: ``dict``
:param headers: Extra headers to add to the request
None, leave as an empty ``dict``.
:type method: ``str``
:param method: An HTTP method such as "GET" or "POST".
:type raw: ``bool``
:param raw: True to perform a "raw" request aka only send the headers
and use the rawResponseCls class. This is used with
storage API when uploading a file.
:type stream: ``bool``
:param stream: True to return an iterator in Response.iter_content
and allow streaming of the response data
(for downloading large files)
:return: An :class:`Response` instance.
:rtype: :class:`Response` instance'

def request(self, action, params=None, data=None, headers=None, method='GET', raw=False, stream=False):

    if (params is None):
        params = {}
    else:
        params = copy.copy(params)
    if (headers is None):
        headers = {}
    else:
        headers = copy.copy(headers)
    retry_enabled = (os.environ.get('LIBCLOUD_RETRY_FAILED_HTTP_REQUESTS', False) or RETRY_FAILED_HTTP_REQUESTS)
    action = self.morph_action_hook(action)
    self.action = action
    self.method = method
    self.data = data
    params = self.add_default_params(params)
    if (self.cache_busting and (method == 'GET')):
        params = self._add_cache_busting_to_params(params=params)
    headers = self.add_default_headers(headers)
    headers.update({'User-Agent': self._user_agent()})
    headers.update({'Accept-Encoding': 'gzip,deflate'})
    port = int(self.port)
    if (port not in (80, 443)):
        headers.update({'Host': ('%s:%d' % (self.host, port))})
    else:
        headers.update({'Host': self.host})
    if data:
        data = self.encode_data(data)
    (params, headers) = self.pre_connect_hook(params, headers)
    if params:
        if ('?' in action):
            url = '&'.join((action, urlencode(params, doseq=True)))
        else:
            url = '?'.join((action, urlencode(params, doseq=True)))
    else:
        url = action
    if (self.connection is None):
        self.connect()
    try:
        if raw:
            self.connection.prepared_request(method=method, url=url, body=data, headers=headers, stream=stream)
        elif retry_enabled:
            retry_request = retry(timeout=self.timeout, retry_delay=self.retry_delay, backoff=self.backoff)
            retry_request(self.connection.request)(method=method, url=url, body=data, headers=headers, stream=stream)
        else:
            self.connection.request(method=method, url=url, body=data, headers=headers, stream=stream)
    except socket.gaierror:
        e = sys.exc_info()[1]
        message = str(e)
        errno = getattr(e, 'errno', None)
        if (errno == (-5)):
            class_name = self.__class__.__name__
            msg = ('%s.    Perhaps    "host"    Connection    class    attribute    (%s.connection)    is    set    to    an    invalid,    non-hostname    value    (%s)?' % (message, class_name, self.host))
            raise socket.gaierror(msg)
        self.reset_context()
        raise e
    except ssl.SSLError:
        e = sys.exc_info()[1]
        self.reset_context()
        raise ssl.SSLError(str(e))
    if raw:
        responseCls = self.rawResponseCls
        kwargs = {'connection': self, 'response': self.connection.getresponse()}
    else:
        responseCls = self.responseCls
        kwargs = {'connection': self, 'response': self.connection.getresponse()}
    try:
        response = responseCls(**kwargs)
    finally:
        self.reset_context()
    return response
