1#-------------------------------------------------------------------------
2# Copyright (c) Microsoft.  All rights reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#   http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#--------------------------------------------------------------------------
15import base64
16import sys
17
18if sys.version_info < (3,):
19    from httplib import (
20        HTTP_PORT,
21        HTTPS_PORT,
22    )
23    from urllib2 import quote as url_quote
24else:
25    from http.client import (
26        HTTP_PORT,
27        HTTPS_PORT,
28    )
29    from urllib.parse import quote as url_quote
30
31from . import HTTPError, HTTPResponse
32from .._serialization import _get_data_bytes_or_stream_only
33
34class _HTTPClient(object):
35    '''
36    Takes the request and sends it to cloud service and returns the response.
37    '''
38
39    def __init__(self, protocol=None, session=None, timeout=None):
40        '''
41        :param str protocol:
42            http or https.
43        :param requests.Session request_session:
44            session object created with requests library (or compatible).
45        :param int timeout:
46            timeout for the http request, in seconds.
47        '''
48        self.protocol = protocol
49        self.session = session
50        self.timeout = timeout
51
52        # By default, requests adds an Accept:*/* and Accept-Encoding to the session,
53        # which causes issues with some Azure REST APIs. Removing these here gives us
54        # the flexibility to add it back on a case by case basis.
55        if 'Accept' in self.session.headers:
56            del self.session.headers['Accept']
57
58        if 'Accept-Encoding' in self.session.headers:
59            del self.session.headers['Accept-Encoding']
60
61        self.proxies = None
62
63    def set_proxy(self, host, port, user, password):
64        '''
65        Sets the proxy server host and port for the HTTP CONNECT Tunnelling.
66
67        Note that we set the proxies directly on the request later on rather than
68        using the session object as requests has a bug where session proxy is ignored
69        in favor of environment proxy. So, auth will not work unless it is passed
70        directly when making the request as this overrides both.
71
72        :param str host:
73            Address of the proxy. Ex: '192.168.0.100'
74        :param int port:
75            Port of the proxy. Ex: 6000
76        :param str user:
77            User for proxy authorization.
78        :param str password:
79            Password for proxy authorization.
80        '''
81        if user and password:
82            proxy_string = '{}:{}@{}:{}'.format(user, password, host, port)
83        else:
84            proxy_string = '{}:{}'.format(host, port)
85
86        self.proxies = {}
87        self.proxies['http'] = 'http://{}'.format(proxy_string)
88        self.proxies['https'] = 'https://{}'.format(proxy_string)
89
90    def perform_request(self, request):
91        '''
92        Sends an HTTPRequest to Azure Storage and returns an HTTPResponse. If
93        the response code indicates an error, raise an HTTPError.
94
95        :param HTTPRequest request:
96            The request to serialize and send.
97        :return: An HTTPResponse containing the parsed HTTP response.
98        :rtype: :class:`~azure.storage._http.HTTPResponse`
99        '''
100        # Verify the body is in bytes or either a file-like/stream object
101        if request.body:
102            request.body = _get_data_bytes_or_stream_only('request.body', request.body)
103
104        # Construct the URI
105        uri = self.protocol.lower() + '://' + request.host + request.path
106
107        # Send the request
108        response = self.session.request(request.method,
109                                        uri,
110                                        params=request.query,
111                                        headers=request.headers,
112                                        data=request.body or None,
113                                        timeout=self.timeout,
114                                        proxies=self.proxies)
115
116        # Parse the response
117        status = int(response.status_code)
118        respheaders = {}
119        for key, name in response.headers.items():
120            respheaders[key.lower()] = name
121
122        wrap = HTTPResponse(status, response.reason, respheaders, response.content)
123        response.close()
124
125        return wrap
126