Пример #1
0
    def _new_conn(self):
        """
        Establish a new connection via the SOCKS proxy.
        """
        extra_kw = {}
        if self.source_address:
            extra_kw['source_address'] = self.source_address

        if self.socket_options:
            extra_kw['socket_options'] = self.socket_options

        try:
            conn = socks.create_connection(
                (self.host, self.port),
                proxy_type=self._socks_options['socks_version'],
                proxy_addr=self._socks_options['proxy_host'],
                proxy_port=self._socks_options['proxy_port'],
                proxy_username=self._socks_options['username'],
                proxy_password=self._socks_options['password'],
                timeout=self.timeout,
                **extra_kw
            )

        except SocketTimeout as e:
            raise ConnectTimeoutError(
                self, "Connection to %s timed out. (connect timeout=%s)" %
                (self.host, self.timeout))

        except socks.ProxyError as e:
            # This is fragile as hell, but it seems to be the only way to raise
            # useful errors here.
            if e.socket_err:
                error = e.socket_err
                if isinstance(error, SocketTimeout):
                    raise ConnectTimeoutError(
                        self,
                        "Connection to %s timed out. (connect timeout=%s)" %
                        (self.host, self.timeout)
                    )
                else:
                    raise NewConnectionError(
                        self,
                        "Failed to establish a new connection: %s" % error
                    )
            else:
                raise NewConnectionError(
                    self,
                    "Failed to establish a new connection: %s" % e
                )

        except SocketError as e:  # Defensive: PySocks should catch all these.
            raise NewConnectionError(
                self, "Failed to establish a new connection: %s" % e)

        return conn
Пример #2
0
def test_retries_on_transport(execute_mock):
    """Testing retries on the transport level

    This forces us to override low-level APIs because the retry mechanism on the urllib3 (which
    uses requests) is pretty low-level itself.
    """
    expected_retries = 3
    execute_mock.side_effect = NewConnectionError("Should be HTTPConnection",
                                                  "Fake connection error")
    transport = RequestsHTTPTransport(
        url="http://localhost:9999",
        retries=expected_retries,
    )
    client = Client(transport=transport)

    query = gql("""
    {
      myFavoriteFilm: film(id:"RmlsbToz") {
        id
        title
        episodeId
      }
    }
    """)
    with client:  # We're using the client as context manager
        with pytest.raises(Exception):
            client.execute(query)

    # This might look strange compared to the previous test, but making 3 retries
    # means you're actually doing 4 calls.
    assert execute_mock.call_count == expected_retries + 1
Пример #3
0
    def _new_conn(self):
        """Establish a socket connection and set nodelay settings on it.
        :return: New socket connection.
        """
        extra_kw = {}
        if self.source_address:
            extra_kw["source_address"] = self.source_address

        if self.socket_options:
            extra_kw["socket_options"] = self.socket_options

        try:
            # HACK(mattrobenolt): All of this is to replace this one line
            # to establish our own connection.
            conn = safe_create_connection((self._dns_host, self.port),
                                          self.timeout, **extra_kw)

        except SocketTimeout:
            raise ConnectTimeoutError(
                self, "Connection to %s timed out. (connect timeout=%s)" %
                (self.host, self.timeout))

        except SocketError as e:
            raise NewConnectionError(
                self, "Failed to establish a new connection: %s" % e)

        return conn
Пример #4
0
    def _new_conn(self):
        extra_kw = {}
        if self.source_address:
            extra_kw['source_address'] = self.source_address

        if self.socket_options:
            extra_kw['socket_options'] = self.socket_options

        custom_host = self.custom_ip if self.custom_ip else self._dns_host
        #if self.custom_ip:
        #    logging.debug('Using custom ip %s for host %s' % (self.custom_ip, self._dns_host))

        try:
            conn = connection.create_connection(
                (custom_host, self.port), self.timeout, **extra_kw)

        except SocketTimeout as e:
            raise ConnectTimeoutError(
                self, "Connection to %s timed out. (connect timeout=%s)" %
                (self.host, self.timeout))

        except SocketError as e:
            raise NewConnectionError(
                self, "Failed to establish a new connection: %s" % e)

        return conn
Пример #5
0
    def _new_conn(self):
        """
        Override a non-public member which is used to create the connection,
        yes, implementation detail, caveat emptor
        """

        extra_kw = {}

        if self.socket_options:
            extra_kw["socket_options"] = self.socket_options

        try:
            conn = create_connection((self.host.rstrip("."), self.port),
                                     self.timeout, **extra_kw)

        except SocketTimeout:
            raise ConnectTimeoutError(
                self,
                "Connection to %s timed out. (connect timeout=%s)" %
                (self.host, self.timeout),
            )

        except SocketError as e:
            raise NewConnectionError(
                self, "Failed to establish a new connection: %s" % e)

        return conn
Пример #6
0
    def test_execute_connection_error(self):
        from urllib3.exceptions import NewConnectionError

        cursor = mock.Mock()
        cursor.execute.side_effect = NewConnectionError(
            "Dummypool", message="Exception with sensitive data")
        with pytest.raises(SupersetDBAPIDatabaseError) as ex:
            ClickHouseEngineSpec.execute(cursor, "SELECT col1 from table1")
Пример #7
0
  def test_update_mastodon_pictures_get_actor_connection_failure(self):
    self.expect_requests_get(
      'https://foo.com' + test_mastodon.API_ACCOUNT % 123,
      headers={'Authorization': 'Bearer towkin'},
    ).AndRaise(NewConnectionError(None, None))
    self.mox.ReplayAll()

    mastodon = self._setup_mastodon()
    resp = self.client.get('/cron/update_mastodon_pictures')
    self.assertEqual(200, resp.status_code)
    self.assertEqual('http://before', mastodon.key.get().picture)
Пример #8
0
def test_provide_registry_with_connection_error():

    concept._BOOTSTRAP_CONNECTORS[0].get_loaders = MagicMock()
    concept._BOOTSTRAP_CONNECTORS[
        0].get_loaders.side_effect = NewConnectionError(
            None, "mock request error")

    with pytest.raises(NoSiibraConfigMirrorsAvailableException):
        concept.provide_registry(DummyCls)

    for connector in concept._BOOTSTRAP_CONNECTORS:
        connector.get_loaders.assert_called()
    def test_index_document_handles_connection_error(self, current_app, index,
                                                     error_status_code,
                                                     service):
        index.return_value = (NewConnectionError(
            '', self.EXAMPLE_CONNECTION_ERROR), error_status_code)

        response = self.client.put(make_search_api_url(service),
                                   data=json.dumps(service),
                                   content_type='application/json')
        assert response.status_code == 500
        current_app.logger.error.assert_called_once_with(
            f'API response error: ": {self.EXAMPLE_CONNECTION_ERROR}" Unexpected status code: "{error_status_code}"'
        )
Пример #10
0
    def urlopen(self, method, url, redirect=True, **kw):
        """
        Same as :meth:`urllib3.connectionpool.HTTPConnectionPool.urlopen`
        with custom cross-host redirect logic and only sends the request-uri
        portion of the ``url``.

        The given ``url`` parameter must be absolute, such that an appropriate
        :class:`urllib3.connectionpool.ConnectionPool` can be chosen for it.
        """
        #===============================================================================================================
        # add by mz
        error_type = kw.get('error_type')

        if error_type:

            from urllib3.exceptions import LocationValueError, HostChangedError, LocationParseError, ConnectTimeoutError
            from urllib3.exceptions import ProxyError, TimeoutError, ReadTimeoutError, ProtocolError, DecodeError
            from urllib3.exceptions import ResponseError, ResponseNotChunked, SSLError, HTTPError, HTTPWarning, PoolError
            from urllib3.exceptions import RequestError, MaxRetryError, TimeoutStateError, NewConnectionError
            from urllib3.exceptions import EmptyPoolError, ClosedPoolError, SecurityWarning, SubjectAltNameWarning
            from urllib3.exceptions import InsecureRequestWarning, SystemTimeWarning, InsecurePlatformWarning
            from urllib3.exceptions import SNIMissingWarning, DependencyWarning, ProxySchemeUnknown, HeaderParsingError
            get_error = {
                "LocationValueError":
                LocationValueError(),
                "HostChangedError":
                HostChangedError(pool=1, url=2),
                "LocationParseError":
                LocationParseError(url),
                "ConnectTimeoutError":
                ConnectTimeoutError(),
                "ProxyError":
                ProxyError(),
                "TimeoutError":
                TimeoutError(),
                "ReadTimeoutError":
                ReadTimeoutError(pool=1, url=2, message="ReadTimeoutError"),
                "ProtocolError":
                ProtocolError(),
                "DecodeError":
                DecodeError(),
                "ResponseError":
                ResponseError(),
                "ResponseNotChunked":
                ResponseNotChunked(),
                "SSLError":
                SSLError(),
                "HTTPError":
                HTTPError(),
                "HTTPWarning":
                HTTPWarning(),
                "PoolError":
                PoolError(pool=1, message=2),
                "RequestError":
                RequestError(pool=1, url=2, message="RequestError"),
                "MaxRetryError":
                MaxRetryError(pool=1, url=2, reason=None),
                "TimeoutStateError":
                TimeoutStateError(),
                "NewConnectionError":
                NewConnectionError(pool=1, message="NewConnectionError"),
                "EmptyPoolError":
                EmptyPoolError(pool=1, message="EmptyPoolError"),
                "ClosedPoolError":
                ClosedPoolError(pool=1, message="ClosedPoolError"),
                "SecurityWarning":
                SecurityWarning(),
                "SubjectAltNameWarning":
                SubjectAltNameWarning(),
                "InsecureRequestWarning":
                InsecureRequestWarning(),
                "SystemTimeWarning":
                SystemTimeWarning(),
                "InsecurePlatformWarning":
                InsecurePlatformWarning(),
                "SNIMissingWarning":
                SNIMissingWarning(),
                "DependencyWarning":
                DependencyWarning(),
                "ProxySchemeUnknown":
                ProxySchemeUnknown(scheme=1),
                "HeaderParsingError":
                HeaderParsingError(defects=1, unparsed_data=2)
            }
            error_ = get_error[error_type]
            raise error_
        #===============================================================================================================

        u = parse_url(url)
        conn = self.connection_from_host(u.host, port=u.port, scheme=u.scheme)

        kw['assert_same_host'] = False
        kw['redirect'] = False
        if 'headers' not in kw:
            kw['headers'] = self.headers

        if self.proxy is not None and u.scheme == "http":
            response = conn.urlopen(method, url, **kw)
        else:
            response = conn.urlopen(method, u.request_uri, **kw)

        redirect_location = redirect and response.get_redirect_location()
        if not redirect_location:
            return response

        # Support relative URLs for redirecting.
        redirect_location = urljoin(url, redirect_location)

        # RFC 2616, Section 10.3.4
        if response.status == 303:
            method = 'GET'

        log.info("Redirecting %s -> %s" % (url, redirect_location))
        kw['retries'] = kw.get('retries', 3) - 1  # Persist retries countdown
        kw['redirect'] = redirect
        return self.urlopen(method, redirect_location, **kw)
Пример #11
0
 def test_catches_new_connection_error(self):
     error = NewConnectionError(None, None)
     self.make_request_with_error(error)
class TestBaseApiClient(object):
    def _from_httplib_response_mock(self, status, response_data=None):
        response_mock = mock.Mock(status=status,
                                  headers={},
                                  spec=[
                                      'get_redirect_location', 'getheader',
                                      'read', 'reason', 'drain_conn'
                                  ])
        response_mock.get_redirect_location.return_value = None
        response_mock.getheader.return_value = None
        response_mock.read.side_effect = [response_data, None, None, None]
        response_mock.reason = f'Mocked {status} response'

        return response_mock

    @pytest.mark.parametrize(
        "method,exc_factory",
        chain(
            ((m, lambda: NewConnectionError(mock.Mock(), "I'm a message"))
             for m in (
                 "GET",
                 "PUT",
                 "POST",
                 "PATCH",
             )),
            ((m, lambda: ProtocolError(mock.Mock(), "I'm a message"))
             for m in (
                 "GET",
                 "PUT",
             )),
            ((m, lambda: ReadTimeoutError(mock.Mock(), mock.Mock(),
                                          "I'm a message")) for m in (
                                              "GET",
                                              "PUT",
                                          )),
        ))
    @pytest.mark.parametrize('retry_count', range(1, 4))
    @mock.patch('urllib3.connectionpool.HTTPConnectionPool._make_request')
    @mock.patch('dmapiclient.base.BaseAPIClient._RETRIES_BACKOFF_FACTOR', 0)
    def test_client_retries_on_httperror_and_raises_api_error(
        self,
        _make_request,
        base_client,
        retry_count,
        exc_factory,
        method,
    ):
        _make_request.side_effect = exc_factory()

        with mock.patch('dmapiclient.base.BaseAPIClient._RETRIES',
                        retry_count):
            with pytest.raises(HTTPError) as e:
                base_client._request(method, '/')

        requests = _make_request.call_args_list

        assert len(requests) == retry_count + 1
        assert all((request[0][1], request[0][2]) == (method, '/')
                   for request in requests)

        assert type(_make_request.side_effect).__name__ in e.value.message
        assert e.value.status_code == REQUEST_ERROR_STATUS_CODE

    @pytest.mark.parametrize("exc_factory", (
        lambda: ProtocolError(mock.Mock(), "I'm a message"),
        lambda: ReadTimeoutError(mock.Mock(), mock.Mock(), "I'm a message"),
    ))
    @pytest.mark.parametrize("method", (
        "POST",
        "PATCH",
    ))
    @pytest.mark.parametrize('retry_count', range(1, 4))
    @mock.patch('urllib3.connectionpool.HTTPConnectionPool._make_request')
    @mock.patch('dmapiclient.base.BaseAPIClient._RETRIES_BACKOFF_FACTOR', 0)
    def test_client_doesnt_retry_non_whitelisted_methods_on_unsafe_errors(
        self,
        _make_request,
        base_client,
        retry_count,
        exc_factory,
        method,
    ):
        _make_request.side_effect = exc_factory()

        with mock.patch('dmapiclient.base.BaseAPIClient._RETRIES',
                        retry_count):
            with pytest.raises(HTTPError) as e:
                base_client._request(method, '/')

        requests = _make_request.call_args_list

        assert len(requests) == 1
        assert requests[0][0][1] == method
        assert requests[0][0][2] == "/"

        assert type(_make_request.side_effect).__name__ in e.value.message
        assert e.value.status_code == REQUEST_ERROR_STATUS_CODE

    @pytest.mark.parametrize(('retry_count'), range(1, 4))
    @pytest.mark.parametrize(('status'),
                             BaseAPIClient.RETRIES_FORCE_STATUS_CODES)
    @mock.patch(
        'urllib3.connectionpool.HTTPConnectionPool.ResponseCls.from_httplib')
    @mock.patch('urllib3.connectionpool.HTTPConnectionPool._make_request')
    @mock.patch('dmapiclient.base.BaseAPIClient._RETRIES_BACKOFF_FACTOR', 0)
    def test_client_retries_on_status_error_and_raises_api_error(
            self, _make_request, from_httplib, base_client, status,
            retry_count):
        response_mock = self._from_httplib_response_mock(status)
        from_httplib.return_value = response_mock

        with mock.patch('dmapiclient.base.BaseAPIClient._RETRIES',
                        retry_count):
            with pytest.raises(HTTPError) as e:
                base_client._request("GET", '/')

        requests = _make_request.call_args_list

        assert len(requests) == retry_count + 1
        assert all((request[0][1], request[0][2]) == ('GET', '/')
                   for request in requests)

        assert f'{status} Server Error: {response_mock.reason} for url: http://baseurl/\n' in e.value.message
        assert e.value.status_code == status

    @mock.patch(
        'urllib3.connectionpool.HTTPConnectionPool.ResponseCls.from_httplib')
    @mock.patch('urllib3.connectionpool.HTTPConnectionPool._make_request')
    @mock.patch('dmapiclient.base.BaseAPIClient._RETRIES_BACKOFF_FACTOR', 0)
    def test_client_retries_and_returns_data_if_successful(
            self, _make_request, from_httplib, base_client):
        #  The third response here would normally be a httplib response object. It's only use is to be passed in to
        #  `from_httplib`, which we're mocking the return of below. `from_httplib` converts a httplib response into a
        #  urllib3 response. The mock object we're returning is a mock for that urllib3 response.
        _make_request.side_effect = [
            ProtocolError(mock.Mock(), '1st error'),
            ProtocolError(mock.Mock(), '2nd error'),
            ProtocolError(mock.Mock(), '3nd error'),
            'httplib_response - success!',
        ]

        from_httplib.return_value = self._from_httplib_response_mock(
            200, response_data=b'{"Success?": "Yes!"}')

        response = base_client._request("GET", '/')
        requests = _make_request.call_args_list

        assert len(requests) == 4
        assert all((request[0][1], request[0][2]) == ('GET', '/')
                   for request in requests)
        assert response == {'Success?': 'Yes!'}

    def test_non_2xx_response_raises_api_error(self, base_client, rmock):
        rmock.request("GET",
                      "http://baseurl/",
                      json={"error": "Not found"},
                      status_code=404)

        with pytest.raises(HTTPError) as e:
            base_client._request("GET", '/')

        assert e.value.message == "Not found"
        assert e.value.status_code == 404

    def test_base_error_is_logged(self, base_client):
        with requests_mock.Mocker() as m:
            m.register_uri('GET', '/', exc=requests.RequestException())

            with pytest.raises(HTTPError) as e:
                base_client._request("GET", "/")

            assert e.value.message == "\nRequestException()"
            assert e.value.status_code == 503

    def test_invalid_json_raises_api_error(self, base_client, rmock):
        rmock.request("GET",
                      "http://baseurl/",
                      text="Internal Error",
                      status_code=200)

        with pytest.raises(InvalidResponse) as e:
            base_client._request("GET", '/')

        assert e.value.message == "No JSON object could be decoded"
        assert e.value.status_code == 200

    def test_user_agent_is_set(self, base_client, rmock):
        rmock.request("GET", "http://baseurl/", json={}, status_code=200)

        base_client._request('GET', '/')

        assert rmock.last_request.headers.get("User-Agent").startswith(
            "DM-API-Client/")

    def test_request_always_uses_base_url_scheme(self, base_client, rmock):
        rmock.request("GET", "http://baseurl/path/", json={}, status_code=200)

        base_client._request('GET', 'https://host/path/')
        assert rmock.called

    def test_null_api_throws(self):
        bad_client = BaseAPIClient(None, 'auth-token', True)
        with pytest.raises(ImproperlyConfigured):
            bad_client._request('GET', '/anything')

    def test_onwards_request_headers_added_if_available(
            self, base_client, rmock, app):
        rmock.get("http://baseurl/_status",
                  json={"status": "ok"},
                  status_code=200)
        with app.test_request_context('/'):
            # add a simple mock callable instead of using a full request implementation
            request.get_onwards_request_headers = mock.Mock()
            request.get_onwards_request_headers.return_value = {
                "Douce": "bronze",
                "Kennedy": "gold",
            }

            base_client.get_status()

            assert rmock.last_request.headers["Douce"] == "bronze"
            assert rmock.last_request.headers["kennedy"] == "gold"

            assert request.get_onwards_request_headers.call_args_list == [
                # just a single, arg-less call
                (),
            ]

    def test_onwards_request_headers_not_available(self, base_client, rmock,
                                                   app):
        rmock.get("http://baseurl/_status",
                  json={"status": "ok"},
                  status_code=200)
        with app.test_request_context('/'):
            # really just asserting no exception arose from performing a call without get_onwards_request_headers being
            # available
            base_client.get_status()

    def test_request_id_fallback(self, base_client, rmock, app):
        # request.request_id is an old interface which we're still supporting here just for compatibility
        rmock.get("http://baseurl/_status",
                  json={"status": "ok"},
                  status_code=200)
        app.config["DM_REQUEST_ID_HEADER"] = "Bar"
        with app.test_request_context('/'):
            request.request_id = "Ormond"

            base_client.get_status()

            assert rmock.last_request.headers["bar"] == "Ormond"

    @pytest.mark.parametrize("dm_span_id_headers_setting", (
        None,
        (
            "X-Brian-Tweedy",
            "Major-Tweedy",
        ),
    ))
    @pytest.mark.parametrize(
        "has_request_context",
        (False, True),
    )
    @mock.patch("dmapiclient.base.logger")
    def test_child_span_id_not_provided(
        self,
        logger,
        dm_span_id_headers_setting,
        has_request_context,
        base_client,
        rmock,
        app,
    ):
        rmock.get("http://baseurl/_status",
                  json={"status": "ok"},
                  status_code=200)
        app.config["DM_SPAN_ID_HEADERS"] = dm_span_id_headers_setting
        with (app.test_request_context('/')
              if has_request_context else _empty_context_manager()):
            if has_request_context:
                request.get_onwards_request_headers = mock.Mock(
                    return_value={
                        "impression": "arrested",
                    })

            base_client.get_status()

            assert rmock.called
            assert logger.log.call_args_list == [
                mock.call(
                    logging.DEBUG,
                    "API request {method} {url}",
                    extra={
                        "method": "GET",
                        "url": "http://baseurl/_status",
                        # childSpanId NOT provided
                    }),
                mock.call(
                    logging.INFO,
                    "API {api_method} request on {api_url} finished in {api_time}",
                    extra={
                        "api_method": "GET",
                        "api_url": "http://baseurl/_status",
                        "api_status": 200,
                        "api_time": mock.ANY,
                        # childSpanId NOT provided
                    }),
            ]

    @pytest.mark.parametrize(
        "onwards_request_headers",
        (
            {
                "X-Brian-Tweedy": "Amiens Street",
            },
            {
                "major-TWEEDY": "Amiens Street",
            },
            {
                "Major-Tweedy": "terminus",
                "x-brian-tweedy": "Amiens Street",
            },
            {
                # note same header name, different capitalizations
                "X-BRIAN-TWEEDY": "great northern",
                "x-brian-tweedy": "Amiens Street",
            },
        ))
    @pytest.mark.parametrize("response_status", (
        200,
        500,
    ))
    @mock.patch("dmapiclient.base.logger")
    def test_child_span_id_provided(
        self,
        mock_logger,
        onwards_request_headers,
        response_status,
        base_client,
        rmock,
        app,
    ):
        rmock.get("http://baseurl/_status",
                  json={"status": "foobar"},
                  status_code=response_status)
        app.config["DM_SPAN_ID_HEADERS"] = (
            "X-Brian-Tweedy",
            "major-tweedy",
        )
        with app.test_request_context('/'):
            request.get_onwards_request_headers = mock.Mock(
                return_value=onwards_request_headers)

            try:
                base_client.get_status()
            except HTTPError:
                # it is tested elsewhere whether this exception is raised in the *right* circumstances or not
                pass

            assert rmock.called

            # some of our scenarios test multiple header names differing only by capitalization - we care that the same
            # span id that was chosen for the log message is the same one that was sent in the onwards request header,
            # so we need two distinct values which are acceptable
            either_span_id = RestrictedAny(
                lambda value: value == "Amiens Street" or value ==
                "great northern")

            assert mock_logger.log.call_args_list == [
                mock.call(logging.DEBUG,
                          "API request {method} {url}",
                          extra={
                              "method": "GET",
                              "url": "http://baseurl/_status",
                              "childSpanId": either_span_id,
                          }),
                (mock.call(
                    logging.INFO,
                    "API {api_method} request on {api_url} finished in {api_time}",
                    extra={
                        "api_method": "GET",
                        "api_url": "http://baseurl/_status",
                        "api_status": response_status,
                        "api_time": mock.ANY,
                        "childSpanId": either_span_id,
                    }
                ) if response_status == 200 else mock.call(
                    logging.WARNING,
                    "API {api_method} request on {api_url} failed with {api_status} '{api_error}'",
                    extra={
                        "api_method": "GET",
                        "api_url": "http://baseurl/_status",
                        "api_status": response_status,
                        "api_time": mock.ANY,
                        "api_error": mock.ANY,
                        "childSpanId": either_span_id,
                    },
                ))
            ]
            # both logging calls should have had the *same* childSpanId value
            assert mock_logger.log.call_args_list[0][1]["extra"]["childSpanId"] \
                == mock_logger.log.call_args_list[1][1]["extra"]["childSpanId"]

            # that value should be the same one that was sent in the onwards request header
            assert (
                rmock.last_request.headers.get("x-brian-tweedy")
                or rmock.last_request.headers.get("major-tweedy")
            ) == mock_logger.log.call_args_list[0][1]["extra"]["childSpanId"]

    @pytest.mark.parametrize(
        "thrown_exception",
        (
            # requests can be slightly unpredictable in the exceptions it raises
            requests.exceptions.ConnectionError(
                MaxRetryError(
                    mock.Mock(), "http://abc.net",
                    ReadTimeoutError(mock.Mock(), mock.Mock(), mock.Mock()))),
            requests.exceptions.ConnectionError(
                ReadTimeoutError(mock.Mock(), mock.Mock(), mock.Mock())),
            requests.exceptions.ReadTimeout,
        ))
    @mock.patch("dmapiclient.base.logger")
    def test_nowait_times_out(
        self,
        mock_logger,
        base_client,
        rmock,
        app,
        thrown_exception,
    ):
        "test the case when a request with client_wait_for_response=False does indeed time out"
        rmock.post("http://baseurl/services/10000", exc=thrown_exception)

        retval = base_client._request(
            "POST",
            "/services/10000",
            {"serviceName": "Postcard"},
            client_wait_for_response=False,
        )

        assert retval is None

        assert rmock.called
        assert tuple(
            req.timeout
            for req in rmock.request_history) == (base_client.nowait_timeout, )

        assert mock_logger.log.call_args_list == [
            mock.call(logging.DEBUG,
                      "API request {method} {url}",
                      extra={
                          "method": "POST",
                          "url": "http://baseurl/services/10000",
                      }),
            mock.call(
                logging.INFO,
                "API {api_method} request on {api_url} dispatched but ignoring response",
                extra={
                    "api_method": "POST",
                    "api_url": "http://baseurl/services/10000",
                    "api_time": mock.ANY,
                    "api_time_incomplete": True,
                }),
        ]

    @mock.patch("dmapiclient.base.logger")
    def test_nowait_completes(
        self,
        mock_logger,
        base_client,
        rmock,
        app,
    ):
        "test the case when a request with client_wait_for_response=False completes before it can time out"
        rmock.post("http://baseurl/services/10000",
                   json={"services": {
                       "id": "10000"
                   }},
                   status_code=200)

        retval = base_client._request(
            "POST",
            "/services/10000",
            {"serviceName": "Postcard"},
            client_wait_for_response=False,
        )

        assert retval == {"services": {"id": "10000"}}

        assert rmock.called
        assert tuple(
            req.timeout
            for req in rmock.request_history) == (base_client.nowait_timeout, )

        assert mock_logger.log.call_args_list == [
            mock.call(logging.DEBUG,
                      "API request {method} {url}",
                      extra={
                          "method": "POST",
                          "url": "http://baseurl/services/10000",
                      }),
            mock.call(
                logging.INFO,
                "API {api_method} request on {api_url} finished in {api_time}",
                extra={
                    "api_method": "POST",
                    "api_url": "http://baseurl/services/10000",
                    "api_time": mock.ANY,
                    "api_status": 200,
                }),
        ]
Пример #13
0
    def _setup_https_tunnel(self):
        sock = self.sock

        host = self._tunnel_host
        port = self._tunnel_port

        try:
            lines = []
            lines.append('CONNECT %s:%d HTTP/1.1' % (host, port))
            lines.append('Host: %s:%d' % (host, port))

            if self._tunnel_headers:
                for item in self._tunnel_headers.items():
                    lines.append('%s: %s' % item)

            data = '\r\n'.join(lines) + '\r\n\r\n'
            sock.sendall(data.encode())

            data = b''
            code = 0
            pos = -1
            while True:
                s = sock.recv(4096)
                if not s:
                    if code == 0:
                        raise SocketError("Tunnel connection failed: %r" %
                                          data)
                    break
                data += s
                if code == 0 and b'\r\n' in data:
                    version, code, message = data.split(b' ', 2)
                    if code != b'200':
                        sock.close()
                        raise SocketError("Tunnel connection failed: %s %s" %
                                          (code, message.strip()))
                pos = data.find(b'\r\n\r\n')
                if pos > 0:
                    break

            tls_conn = tlslite.TLSConnection(sock)
            try:
                tls_conn.handshakeClientCert(serverName=host)
            except Exception:
                sock.close()
                raise

            try:
                ssl.match_hostname(tlslite_getpeercert(tls_conn), host)
            except Exception:
                tls_conn.close()
                raise
        except SocketTimeout as e:
            raise ConnectTimeoutError(
                self, "Connection to %s timed out. (connect timeout=%s)" %
                (self.host, self.timeout))

        except SocketError as e:
            raise NewConnectionError(
                self, "Failed to establish a new connection: %s" % e)

        # patch fileno,
        # let urllib3.util.connection.is_connection_dropped work as expected
        # tls_conn.fileno = partial(self._origin_sock.fileno) # here we always got fileno = -1
        tls_conn.fileno = partial(sock.fileno)
        # patch getpeercert
        tls_conn.getpeercert = partial(tlslite_getpeercert, tls_conn)
        self.sock = tls_conn
Пример #14
0
def resolve_ip_for_target(rrsets, target):
    for rrset in rrsets:
        if rrset.name == target:
            return rrset.items[0].address

    raise NewConnectionError("Couldn't find A record for target %s" % target)
Пример #15
0
 def test_catches_new_connection_error(self):
     error = NewConnectionError(None, None)
     with pytest.raises(EndpointConnectionError):
         self.make_request_with_error(error)