Esempio n. 1
0
import http

# 打印“OK”对应的状态码
print('"OK" match to "{}"'.format(http.HTTPStatus.OK))

# 打印“NOT FOUND”对应的状态码
print('"NOT_FOUND" match to "{}"'.format(http.HTTPStatus.NOT_FOUND))

# 打印状态码对应的状态短语
while True:
    st_code = input("请输入要查询的状态码:(输入 q 退出)\n")

    try:
        print("状态为:{}\n".format(http.HTTPStatus(int(st_code)).phrase))
    except ValueError:
        if st_code == 'q':
            break
        print("对应的状态码 {} 不存在\n".format(st_code))
Esempio n. 2
0
    async def graphql(
        self,
        query: str,
        *,
        endpoint: str = "https://api.github.com/graphql",
        **variables: Any,
    ) -> Any:
        """Query the GraphQL v4 API.

        The *endpoint* argument specifies the endpoint URL to use. The
        *variables* kwargs-style argument collects all variables for the query.
        """
        payload: Dict[str, Any] = {"query": query}
        if variables:
            payload["variables"] = variables
        request_data = json.dumps(payload).encode("utf-8")
        request_headers = sansio.create_headers(self.requester,
                                                accept=JSON_UTF_8_CHARSET,
                                                oauth_token=self.oauth_token)
        request_headers.update({
            "content-type": JSON_UTF_8_CHARSET,
            "content-length": str(len(request_data)),
        })
        status_code, response_headers, response_data = await self._request(
            "POST", endpoint, request_headers, request_data)

        if not response_data:
            raise GraphQLException("Response contained no data", response_data)

        # Decode content.
        resp_content_type = response_headers.get("content-type")
        type_, encoding = sansio._parse_content_type(resp_content_type)
        response_str = response_data.decode(encoding)
        if type_ == "application/json":
            response: Dict[str, Any] = json.loads(response_str)
        else:
            raise GraphQLResponseTypeError(resp_content_type, response_str)

        if status_code >= 500:
            raise GitHubBroken(http.HTTPStatus(status_code))
        elif status_code == 401:
            raise GraphQLAuthorizationFailure(response)
        elif status_code >= 400:
            # 400 corresponds to malformed JSON, but that should never receive
            # that as a response as json.dumps() should have raised its own
            # exception before we made the request.
            raise BadGraphQLRequest(http.HTTPStatus(status_code), response)
        elif status_code == 200:
            self.rate_limit = sansio.RateLimit.from_http(response_headers)
            if "errors" in response:
                raise QueryError(response)
            if "data" in response:
                return response["data"]
            else:
                raise GraphQLException(
                    f"Response did not contain 'errors' or 'data': {response}",
                    response)
        else:
            raise GraphQLException(
                f"Unexpected HTTP response to GraphQL request: {status_code}",
                response)
Esempio n. 3
0
    def alt_response(
        self,
        status_code,
        response=None,
        *,
        schema=None,
        content_type=None,
        description=None,
        example=None,
        examples=None,
        headers=None,
        success=False,
    ):
        """Decorator documenting an alternative response

        :param int|str|HTTPStatus status_code: HTTP status code.
        :param str response: Reponse reference.
        :param schema schema|str|dict: :class:`Schema <marshmallow.Schema>`
            class or instance or reference or dict.
        :param str description: Description of the response (default: None).
        :param dict example: Example of response message.
        :param dict examples: Examples of response message.
        :param dict headers: Headers returned by the response.
        :param bool success: ``True`` if this response is part of the normal
            flow of the function. Default: ``False``.

        This decorator allows the user to document an alternative response.
        This can be an error managed with :func:`abort <abort>` or any response
        that is not the primary flow of the function documented by
        :meth:`Blueprint.reponse <Blueprint.response>`.

        When a response reference is passed as ``response``, it is used as
        description and the keyword arguments are ignored. Otherwise, a
        description is built from the keyword arguments.

        See :ref:`document-alternative-responses`.
        """
        # Response ref is passed
        if response is not None:
            resp_doc = response
        # Otherwise, build response description
        else:
            if isinstance(schema, type):
                schema = schema()

            # Document response (schema, description,...) in the API doc
            doc_schema = self._make_doc_response_schema(schema)
            if description is None:
                description = http.HTTPStatus(int(status_code)).phrase
            resp_doc = remove_none({
                "schema": doc_schema,
                "description": description,
                "example": example,
                "examples": examples,
                "headers": headers,
            })
            resp_doc["content_type"] = content_type

        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):
                return func(*args, **kwargs)

            # Store doc in wrapper function
            # The deepcopy avoids modifying the wrapped function doc
            wrapper._apidoc = deepcopy(getattr(wrapper, "_apidoc", {}))
            wrapper._apidoc.setdefault("response", {}).setdefault(
                "responses", {})[status_code] = resp_doc
            if success:
                # Indicate this code is a success status code
                # Helps other decorators documenting success responses
                wrapper._apidoc.setdefault("success_status_codes",
                                           []).append(status_code)
            return wrapper

        return decorator
Esempio n. 4
0
def _get_status_line(status_code):
    try:
        phrase = http.HTTPStatus(status_code).phrase.encode()
    except ValueError:
        phrase = b""
    return b"".join([b"HTTP/1.1 ", str(status_code).encode(), b" ", phrase, b"\r\n"])
Esempio n. 5
0
def decipher_response(
        status_code: int, headers: Mapping[str, str],
        body: bytes) -> Tuple[Any, Optional[RateLimit], Optional[str]]:
    """Decipher an HTTP response for a GitHub API request.

    The mapping providing the headers is expected to support lowercase keys.

    The parameters of this function correspond to the three main parts
    of an HTTP response: the status code, headers, and body. Assuming
    no errors which lead to an exception being raised, a 3-item tuple
    is returned. The first item is the decoded body (typically a JSON
    object, but possibly None or a string depending on the content
    type of the body). The second item is an instance of RateLimit
    based on what the response specified.

    The last item of the tuple is the URL where to request the next
    part of results. If there are no more results then None is
    returned. Do be aware that the URL can be a URI template and so
    may need to be expanded.

    If the status code is anything other than 200, 201, or 204, then
    an HTTPException is raised.
    """
    data = _decode_body(headers.get("content-type"), body)
    if status_code in {200, 201, 204}:
        return data, RateLimit.from_http(headers), _next_link(
            headers.get("link"))
    else:
        try:
            message = data["message"]
        except (TypeError, KeyError):
            message = None
        exc_type: Type[HTTPException]
        if status_code >= 500:
            exc_type = GitHubBroken
        elif status_code >= 400:
            exc_type = BadRequest
            if status_code == 403:
                rate_limit = RateLimit.from_http(headers)
                if rate_limit and not rate_limit.remaining:
                    raise RateLimitExceeded(rate_limit, message)
            elif status_code == 422:
                try:
                    errors = data.get("errors", None)
                except AttributeError:
                    # Not JSON so don't know why the request failed.
                    raise BadRequestUnknownError(data)
                exc_type = InvalidField
                if errors:
                    if any(e["code"] in [
                            "missing", "missing_field", "invalid",
                            "already_exists"
                    ] for e in errors):
                        error_context = ", ".join(
                            repr(e.get("field")) for e in errors)
                        message = f"{message} for {error_context}"
                    else:
                        exc_type = ValidationError
                        error_context = ", ".join(
                            repr(e.get("message")) for e in errors)
                        message = f"{message}: {error_context}"
                else:
                    message = data["message"]
                raise exc_type(errors, message)
        elif status_code >= 300:
            exc_type = RedirectionException
        else:
            exc_type = HTTPException
        status_code_enum = http.HTTPStatus(status_code)
        args: Union[Tuple[http.HTTPStatus, str], Tuple[http.HTTPStatus]]
        if message:
            args = status_code_enum, message
        else:
            args = (status_code_enum, )
        raise exc_type(*args)
Esempio n. 6
0
 def test_5XX(self):
     status_code = 502
     with pytest.raises(GitHubBroken) as exc_info:
         sansio.decipher_response(status_code, {}, b"")
     assert exc_info.value.status_code == http.HTTPStatus(status_code)
Esempio n. 7
0
 def test_3XX(self):
     status_code = 301
     with pytest.raises(RedirectionException) as exc_info:
         sansio.decipher_response(status_code, {}, b"")
     assert exc_info.value.status_code == http.HTTPStatus(status_code)
Esempio n. 8
0
 def _prepare_auth_doc(doc, doc_info, **kwargs):
     if doc_info.get("auth", False):
         doc.setdefault("responses", {})["401"] = http.HTTPStatus(401).name
         doc["security"] = [{"TokenAuthentication": []}]
     return doc
Esempio n. 9
0
 def _prepare_404_doc(doc, doc_info, **kwargs):
     if doc_info.get("validate", False):
         doc.setdefault("responses", {})["404"] = http.HTTPStatus(404).name
     return doc
Esempio n. 10
0
def run(args):
    # SNIPPET_START: parsing
    with open(args.scenario) as f:
        scenario = json.load(f)

    hosts = scenario.get("hosts", ["localhost", "localhost"])
    if args.consensus == "pbft":
        hosts = ["localhost"] * 3
    args.package = scenario["package"]
    # SNIPPET_END: parsing

    scenario_dir = os.path.dirname(args.scenario)

    # SNIPPET_START: create_network
    with infra.network.network(hosts, args.binary_dir, args.debug_nodes,
                               args.perf_nodes) as network:
        network.start_and_join(args)
        # SNIPPET_END: create_network

        primary, backups = network.find_nodes()

        with primary.client() as mc:

            check = infra.checker.Checker()
            check_commit = infra.checker.Checker(mc)

            for connection in scenario["connections"]:
                with (primary.client("user0")
                      if not connection.get("on_backup") else
                      random.choice(backups).client("user0")) as client:
                    txs = connection.get("transactions", [])

                    for include_file in connection.get("include", []):
                        with open(os.path.join(scenario_dir,
                                               include_file)) as f:
                            txs += json.load(f)

                    for tx in txs:
                        r = client.call(
                            tx["method"],
                            body=tx["body"],
                            http_verb=tx.get("verb", "POST"),
                        )

                        if tx.get("expected_error") is not None:
                            check(
                                r,
                                error=lambda status, msg, transaction=tx:
                                status
                                # pylint: disable=no-member
                                == http.HTTPStatus(
                                    transaction.get("expected_error")).value,
                            )

                        elif tx.get("expected_result") is not None:
                            check_commit(r, result=tx.get("expected_result"))

                        else:
                            check_commit(r, result=lambda res: res is not None)

                network.wait_for_node_commit_sync(args.consensus)

    if args.network_only:
        LOG.info("Keeping network alive with the following nodes:")
        LOG.info("  Primary = {}:{}".format(primary.pubhost, primary.rpc_port))
        for i, f in enumerate(backups):
            LOG.info("  Backup[{}] = {}:{}".format(i, f.pubhost, f.rpc_port))

        input("Press Enter to shutdown...")
Esempio n. 11
0
        with pytest.raises(ValueError):
            falcon.get_http_status('-404.3')
        assert falcon.get_http_status(123, 'Go Away') == '123 Go Away'

    @pytest.mark.parametrize('v_in,v_out', [
        (703, falcon.HTTP_703),
        (404, falcon.HTTP_404),
        (404.9, falcon.HTTP_404),
        (falcon.HTTP_200, falcon.HTTP_200),
        (falcon.HTTP_307, falcon.HTTP_307),
        (falcon.HTTP_404, falcon.HTTP_404),
        (123, '123 Unknown'),
        ('123 Wow Such Status', '123 Wow Such Status'),
        (b'123 Wow Such Status', '123 Wow Such Status'),
        (b'200 OK', falcon.HTTP_OK),
        (http.HTTPStatus(200), falcon.HTTP_200),
        (http.HTTPStatus(307), falcon.HTTP_307),
        (http.HTTPStatus(401), falcon.HTTP_401),
        (http.HTTPStatus(410), falcon.HTTP_410),
        (http.HTTPStatus(429), falcon.HTTP_429),
        (http.HTTPStatus(500), falcon.HTTP_500),
    ])
    def test_code_to_http_status(self, v_in, v_out):
        assert falcon.code_to_http_status(v_in) == v_out

    @pytest.mark.parametrize(
        'v', [0, 13, 99, 1000, 1337.01, -99, -404.3, -404, -404.3])
    def test_code_to_http_status_value_error(self, v):
        with pytest.raises(ValueError):
            falcon.code_to_http_status(v)
Esempio n. 12
0
    def test_code_to_http_status(self, v_in, v_out):
        assert falcon.code_to_http_status(v_in) == v_out

    @pytest.mark.parametrize(
        'v',
        ['not_a_number', 0, '0', 99, '99', '404.3', -404.3, '-404', '-404.3'])
    def test_code_to_http_status_neg(self, v):
        with pytest.raises(ValueError):
            falcon.code_to_http_status(v)

    @pytest.mark.parametrize(
        'v_in,v_out',
        [
            # NOTE(kgriffs): Include some codes not used elsewhere so that
            #   we get past the LRU.
            (http.HTTPStatus(505), 505),
            (712, 712),
            ('712', 712),
            (b'404 Not Found', 404),
            (b'712 NoSQL', 712),
            ('404 Not Found', 404),
            ('123 Wow Such Status', 123),

            # NOTE(kgriffs): Test LRU
            (http.HTTPStatus(505), 505),
            ('123 Wow Such Status', 123),
        ])
    def test_http_status_to_code(self, v_in, v_out):
        assert falcon.http_status_to_code(v_in) == v_out

    @pytest.mark.parametrize('v',
Esempio n. 13
0
def decipher_response(status_code: int, headers: Mapping,
                      body: bytes) -> Tuple[Any, RateLimit, Optional[str]]:
    """Decipher an HTTP response for a GitHub API request.

    The mapping providing the headers is expected to support lowercase keys.

    The parameters of this function correspond to the three main parts
    of an HTTP response: the status code, headers, and body. Assuming
    no errors which lead to an exception being raised, a 3-item tuple
    is returned. The first item is the decoded body (typically a JSON
    object, but possibly None or a string depending on the content
    type of the body). The second item is an instance of RateLimit
    based on what the response specified.

    The last item of the tuple is the URL where to request the next
    part of results. If there are no more results then None is
    returned. Do be aware that the URL can be a URI template and so
    may need to be expanded.

    If the status code is anything other than 200, 201, or 204, then
    an HTTPException is raised.
    """
    data = _decode_body(headers.get("content-type"), body)
    if status_code in {200, 201, 204}:
        return data, RateLimit.from_http(headers), _next_link(
            headers.get("link"))
    else:
        try:
            message = data["message"]
            print("Message: ", message)
        except (TypeError, KeyError):
            message = None
        exc_type: Type[HTTPException]
        if status_code >= 500:
            exc_type = GitHubBroken
        elif status_code >= 400:
            exc_type = BadRequest
            if status_code == 403:
                rate_limit = RateLimit.from_http(headers)
                if not rate_limit.remaining:
                    raise RateLimitExceeded(rate_limit, message)
            elif status_code == 404:
                print(
                    "File not found or you do not have permission to access this repo"
                )
            elif status_code == 422:
                errors = data.get("errors", None)
                if errors:
                    #fields = ", ".join(repr(e["field"]) for e in errors)
                    message = f"{message} for {repr(errors)} "
                else:
                    message = data["message"]
                raise InvalidField(errors, message)
        elif status_code >= 300:
            exc_type = RedirectionException
        else:
            exc_type = HTTPException
        status_code_enum = http.HTTPStatus(status_code)
        args: Tuple
        if message:
            args = status_code_enum, message
        else:
            args = status_code_enum,
        raise exc_type(*args)
Esempio n. 14
0
 async def test_bad_credentials(self):
     gh, response_data = self.gh_and_response("bad-credentials-401.json")
     with pytest.raises(GraphQLAuthorizationFailure) as exc:
         await gh.graphql(_SAMPLE_QUERY)
     assert exc.value.response == response_data
     assert exc.value.status_code == http.HTTPStatus(401)
Esempio n. 15
0
async def test_new_comment():
    # Comment not from PR author.
    data = {
        "action": "created",
        "issue": {
            "user": {
                "login": "******"
            }
        },
        "comment": {
            "user": {
                "login": "******"
            },
            "body": awaiting.BORING_TRIGGER_PHRASE,
        },
    }
    event = sansio.Event(data, event="issue_comment", delivery_id="12345")
    gh = FakeGH()
    await awaiting.router.dispatch(event, gh)
    assert not len(gh.post_)

    # Comment from PR author but missing trigger phrase.
    data = {
        "action": "created",
        "issue": {
            "user": {
                "login": "******"
            }
        },
        "comment": {
            "user": {
                "login": "******"
            },
            "body": "I DID expect the Spanish Inquisition",
        },
    }
    event = sansio.Event(data, event="issue_comment", delivery_id="12345")
    gh = FakeGH()
    await awaiting.router.dispatch(event, gh)
    assert not len(gh.post_)

    # Everything is right with the world.
    data = {
        "action": "created",
        "issue": {
            "user": {
                "login": "******"
            },
            "labels": [],
            "labels_url": "https://api.github.com/labels/42",
            "url": "https://api.github.com/issue/42",
            "pull_request": {
                "url": "https://api.github.com/pr/42"
            },
            "comments_url": "https://api.github.com/comments/42",
        },
        "comment": {
            "user": {
                "login": "******"
            },
            "body": awaiting.BORING_TRIGGER_PHRASE,
        },
    }
    event = sansio.Event(data, event="issue_comment", delivery_id="12345")
    items = {
        "https://api.github.com/teams/6/memberships/brettcannon":
        True,
        "https://api.github.com/teams/6/memberships/gvanrossum":
        True,
        "https://api.github.com/teams/6/memberships/not-core-dev":
        gidgethub.BadRequest(status_code=http.HTTPStatus(404)),
    }
    iterators = {
        "https://api.github.com/orgs/python/teams": [{
            "name": "python core",
            "id": 6
        }],
        "https://api.github.com/pr/42/reviews": [
            {
                "user": {
                    "login": "******"
                },
                "state": "approved"
            },
            {
                "user": {
                    "login": "******"
                },
                "state": "changes_requested"
            },
            {
                "user": {
                    "login": "******"
                },
                "state": "approved"
            },
        ],
    }
    gh = FakeGH(getitem=items, getiter=iterators)
    await awaiting.router.dispatch(event, gh)
    assert len(gh.post_) == 3
    labeling, comment, review_request = gh.post_
    assert labeling[0] == "https://api.github.com/labels/42"
    assert labeling[1] == [awaiting.Blocker.change_review.value]
    assert comment[0] == "https://api.github.com/comments/42"
    comment_body = comment[1]["body"]
    assert "@brettcannon" in comment_body
    assert "@gvanrossum" in comment_body
    assert "not-core-dev" not in comment_body
    assert review_request[
        0] == "https://api.github.com/pr/42/requested_reviewers"
    requested_reviewers = review_request[1]["reviewers"]
    assert "brettcannon" in requested_reviewers
    assert "gvanrossum" in requested_reviewers
    assert "not-core-dev" not in requested_reviewers

    # All is right with the Monty Python world.
    data = {
        "action": "created",
        "issue": {
            "user": {
                "login": "******"
            },
            "labels": [],
            "labels_url": "https://api.github.com/labels/42",
            "url": "https://api.github.com/issue/42",
            "pull_request": {
                "url": "https://api.github.com/pr/42"
            },
            "comments_url": "https://api.github.com/comments/42",
        },
        "comment": {
            "user": {
                "login": "******"
            },
            "body": awaiting.FUN_TRIGGER_PHRASE,
        },
    }
    event = sansio.Event(data, event="issue_comment", delivery_id="12345")
    gh = FakeGH(getitem=items, getiter=iterators)
    await awaiting.router.dispatch(event, gh)
    assert len(gh.post_) == 3
    labeling, comment, review_request = gh.post_
    assert labeling[0] == "https://api.github.com/labels/42"
    assert labeling[1] == [awaiting.Blocker.change_review.value]
    assert comment[0] == "https://api.github.com/comments/42"
    comment_body = comment[1]["body"]
    assert "@brettcannon" in comment_body
    assert "@gvanrossum" in comment_body
    assert "not-core-dev" not in comment_body
    assert review_request[
        0] == "https://api.github.com/pr/42/requested_reviewers"
    requested_reviewers = review_request[1]["reviewers"]
    assert "brettcannon" in requested_reviewers
    assert "gvanrossum" in requested_reviewers
    assert "not-core-dev" not in requested_reviewers
Esempio n. 16
0
    def run(self, command, **data):
        """Convert specified command and data into HTTP request, send it to
        webservice, and return response. Handle error responses.
        The data kwargs are passed to the HTTP request.
        'pocket' and 'table_name' data fields are substituted, if None.

        :return: dict. See Server class for possible keys
        :raise: ValueError if invalid command given
        :raise: CommunicationError on e.g. timeouts or server-side errors,
            InvalidRequest on invalid requests
        """

        pocket = data.pop("pocket", None) or DEFAULT_POCKET_NAME

        host = self.http_config.get("host", DEFAULT_HOST)
        base_url = "{}{}".format(host, POCKETS_TAIL)
        pocket_url = "{}/{}".format(base_url, pocket)
        copy_url = "{}{}".format(host, COPY_TAIL)
        eid_url = "{}/{}/{}".format(pocket_url,
                                    data.get("table_name") or DEFAULT_TABLE,
                                    data.get("eid"))

        username = self.http_config.get("username")
        password = self.http_config.get("password")
        auth = None
        if username and password:
            auth = (username, password)

        kwargs = dict(auth=auth, timeout=self.http_config.get("timeout"))

        if command == "list":
            # Correctly send filters; allowing for server-side deserialization
            kwargs["json"] = json.dumps(data)
        else:
            kwargs["json"] = data or None

        if command == "list":
            url = pocket_url
            function = requests.get
        elif command == "remove":
            url = eid_url
            function = requests.delete
        elif command == "add":
            url = pocket_url
            function = requests.post
        elif command == "pockets":
            url = base_url
            function = requests.post
        elif command == "copy":
            url = copy_url
            function = requests.post
        elif command == "get":
            url = eid_url
            function = requests.get
        elif command == "update":
            url = eid_url
            function = requests.patch
        else:
            raise ValueError("Unknown command: {}".format(command))

        try:
            response = function(url, **kwargs)
        except requests.RequestException as e:
            raise exceptions.CommunicationError(
                "Error sending request: {}".format(e))

        if response.ok:
            return response.json()
        else:
            try:
                # Get further information about error (see Server.run)
                error = response.json()["error"]
            except (json.JSONDecodeError, KeyError):
                error = "-"

            status_code = response.status_code
            if 400 <= status_code < 500:
                error_class = exceptions.InvalidRequest
            else:
                error_class = exceptions.CommunicationError

            message = "Error handling request. " +\
                "Server returned '{} ({}): {}'".format(
                    http.HTTPStatus(status_code).phrase, status_code, error)

            raise error_class(message)
Esempio n. 17
0
    def __init__(self, status, headers, data):
        self.headers = headers
        self.data = data

        self.status = http.HTTPStatus(status)
Esempio n. 18
0
    def response(self,
                 schema=None,
                 *,
                 code=200,
                 description=None,
                 example=None,
                 examples=None,
                 headers=None):
        """Decorator generating an endpoint response

        :param schema: :class:`Schema <marshmallow.Schema>` class or instance.
            If not None, will be used to serialize response data.
        :param int|str|HTTPStatus code: HTTP status code (default: 200).
            Used if none is returned from the view function.
        :param str description: Description of the response (default: None).
        :param dict example: Example of response message.
        :param list examples: Examples of response message.
        :param dict headers: Headers returned by the response.

        The decorated function is expected to return the same types of value
        than a typical flask view function, except the body part may be an
        object or a list of objects to serialize with the schema, rather than
        a ``string``.

        If the decorated function returns a ``Response`` object, the ``schema``
        and ``code`` parameters are only used to document the resource.

        The `example` and `examples` parameters are mutually exclusive. The
        latter should only be used with OpenAPI 3.

        The `example`, `examples` and `headers` parameters are only used to
        document the resource.

        See :doc:`Response <response>`.
        """
        if isinstance(schema, type):
            schema = schema()

        # Document response (schema, description,...) in the API doc
        resp_doc = {}
        doc_schema = self._make_doc_response_schema(schema)
        if doc_schema is not None:
            resp_doc['schema'] = doc_schema
        if description is not None:
            resp_doc['description'] = description
        else:
            resp_doc['description'] = http.HTTPStatus(int(code)).phrase
        if example is not None:
            resp_doc['example'] = example
        if examples is not None:
            resp_doc['examples'] = examples
        if headers is not None:
            resp_doc['headers'] = headers
        doc = {'responses': {code: resp_doc}}

        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):

                # Execute decorated function
                result_raw, status, headers = unpack_tuple_response(
                    func(*args, **kwargs))

                # If return value is a werkzeug BaseResponse, return it
                if isinstance(result_raw, BaseResponse):
                    set_status_and_headers_in_response(result_raw, status,
                                                       headers)
                    return result_raw

                # Dump result with schema if specified
                if schema is None:
                    result_dump = result_raw
                else:
                    result_dump = schema.dump(result_raw)
                    if MARSHMALLOW_VERSION_MAJOR < 3:
                        result_dump = result_dump.data

                # Store result in appcontext (may be used for ETag computation)
                appcontext = get_appcontext()
                appcontext['result_raw'] = result_raw
                appcontext['result_dump'] = result_dump

                # Build response
                resp = jsonify(self._prepare_response_content(result_dump))
                set_status_and_headers_in_response(resp, status, headers)
                if status is None:
                    resp.status_code = code

                return resp

            # Document pagination header if needed
            if getattr(func, '_paginated', False) is True:
                doc['responses'][code]['headers'] = {
                    self.PAGINATION_HEADER_FIELD_NAME:
                    (self.PAGINATION_HEADER_DOC)
                }

            # Document default error response
            doc['responses']['default'] = 'DEFAULT_ERROR'

            # Store doc in wrapper function
            # The deepcopy avoids modifying the wrapped function doc
            wrapper._apidoc = deepcopy(getattr(wrapper, '_apidoc', {}))
            wrapper._apidoc['response'] = doc

            return wrapper

        return decorator
Esempio n. 19
0
 def test_4XX_no_message(self):
     status_code = 400
     with pytest.raises(BadRequest) as exc_info:
         sansio.decipher_response(status_code, {}, b"")
     assert exc_info.value.status_code == http.HTTPStatus(status_code)
Esempio n. 20
0
def _get_status_phrase(status_code):
    try:
        return http.HTTPStatus(status_code).phrase.encode()
    except ValueError:
        return b""
Esempio n. 21
0
 def test_2XX_error(self):
     status_code = 205
     with pytest.raises(HTTPException) as exc_info:
         sansio.decipher_response(status_code, {}, b"")
     assert exc_info.value.status_code == http.HTTPStatus(status_code)
Esempio n. 22
0
 def __init__(self, response: Any) -> None:
     super().__init__(http.HTTPStatus(401), response)
Esempio n. 23
0
 def __init__(self, status_code: int, detail: str = None) -> None:
     if detail is None:
         detail = http.HTTPStatus(status_code).phrase
     self.status_code = status_code
     self.detail = detail
Esempio n. 24
0
            def on_get(self, req, resp):
                resp.content_type = falcon.MEDIA_TEXT
                resp.data = b'Hello, World!'
                resp.status = status

        app = create_app(asgi=asgi)
        app.add_route('/status', Resource())
        return testing.TestClient(app)

    return client


@pytest.mark.parametrize(
    'status,expected_code',
    [
        (http.HTTPStatus(200), 200),
        (http.HTTPStatus(202), 202),
        (http.HTTPStatus(403), 403),
        (http.HTTPStatus(500), 500),
        (http.HTTPStatus.OK, 200),
        (http.HTTPStatus.USE_PROXY, 305),
        (http.HTTPStatus.NOT_FOUND, 404),
        (http.HTTPStatus.NOT_IMPLEMENTED, 501),
        (200, 200),
        (307, 307),
        (500, 500),
        (702, 702),
        (b'200 OK', 200),
        (b'702 Emacs', 702),
    ],
)
Esempio n. 25
0
def _get_reason_phrase(status_code: int) -> str:
    try:
        return http.HTTPStatus(status_code).phrase
    except ValueError:
        return ""
Esempio n. 26
0
async def test_new_review():
    # First non-comment review from a non-core dev.
    username = "******"
    data = {
        "action": "submitted",
        "review": {
            "state": "approved",
            "user": {
                "login": username,
            },
        },
        "pull_request": {
            "url": "https://api.github.com/pr/42",
            "issue_url": "https://api.github.com/issue/42",
            "state": "open",
        },
    }
    event = sansio.Event(data,
                         event="pull_request_review",
                         delivery_id="12345")
    teams = [{"name": "python core", "id": 6}]
    items = {
        f"https://api.github.com/teams/6/memberships/{username}":
        gidgethub.BadRequest(status_code=http.HTTPStatus(404)),
        "https://api.github.com/teams/6/memberships/brettcannon":
        True,
        "https://api.github.com/issue/42": {
            "labels": [],
            "labels_url": "https://api.github.com/labels/42",
        },
    }
    iterators = {
        "https://api.github.com/orgs/python/teams":
        teams,
        "https://api.github.com/pr/42/reviews": [{
            "user": {
                "login": "******"
            },
            "state": "commented"
        }],
    }
    gh = FakeGH(getiter=iterators, getitem=items)
    await awaiting.router.dispatch(event, gh)
    assert len(gh.post_) == 1
    post_ = gh.post_[0]
    assert post_[0] == "https://api.github.com/labels/42"
    assert post_[1] == [awaiting.Blocker.core_review.value]

    # First and second review from a non-core dev.
    items = {
        f"https://api.github.com/teams/6/memberships/{username}":
        gidgethub.BadRequest(status_code=http.HTTPStatus(404)),
        "https://api.github.com/teams/6/memberships/brettcannon":
        True,
        "https://api.github.com/issue/42": {
            "labels": [],
            "labels_url": "https://api.github.com/labels/42",
        },
    }
    iterators = {
        "https://api.github.com/orgs/python/teams":
        teams,
        "https://api.github.com/pr/42/reviews": [{
            "user": {
                "login": "******"
            },
            "state": "approved"
        }],
    }
    gh = FakeGH(getiter=iterators, getitem=items)
    await awaiting.router.dispatch(event, gh)
    assert not gh.post_

    # First comment review from a non-core dev.
    data = {
        "action": "submitted",
        "review": {
            "state": "comment",
            "user": {
                "login": username,
            },
        },
        "pull_request": {
            "url": "https://api.github.com/pr/42",
            "issue_url": "https://api.github.com/issue/42",
            "state": "open",
        },
    }
    event = sansio.Event(data,
                         event="pull_request_review",
                         delivery_id="12345")
    items = {
        f"https://api.github.com/teams/6/memberships/{username}":
        gidgethub.BadRequest(status_code=http.HTTPStatus(404)),
        "https://api.github.com/teams/6/memberships/brettcannon":
        True,
        "https://api.github.com/issue/42": {
            "labels": [],
            "labels_url": "https://api.github.com/labels/42",
        },
    }
    iterators = {
        "https://api.github.com/orgs/python/teams":
        teams,
        "https://api.github.com/pr/42/reviews": [{
            "user": {
                "login": "******"
            },
            "state": "approved"
        }],
    }
    gh = FakeGH(getiter=iterators, getitem=items)
    await awaiting.router.dispatch(event, gh)
    assert not gh.post_

    # Core dev submits an approving review.
    username = "******"
    data = {
        "action": "submitted",
        "review": {
            "user": {
                "login": username,
            },
            "state": "APPROVED",
        },
        "pull_request": {
            "url": "https://api.github.com/pr/42",
            "issue_url": "https://api.github.com/issue/42",
            "state": "open",
        },
    }
    event = sansio.Event(data,
                         event="pull_request_review",
                         delivery_id="12345")
    teams = [{"name": "python core", "id": 6}]
    items = {
        f"https://api.github.com/teams/6/memberships/{username}": True,
        "https://api.github.com/issue/42": {
            "labels": [{
                "name": awaiting.Blocker.changes.value
            }],
            "labels_url": "https://api.github.com/labels/42",
        },
    }
    iterators = {
        "https://api.github.com/orgs/python/teams": teams,
        "https://api.github.com/pr/42/reviews": [],
    }
    gh = FakeGH(getiter=iterators, getitem=items)
    await awaiting.router.dispatch(event, gh)
    assert len(gh.post_) == 1
    post_ = gh.post_[0]
    assert post_[0] == "https://api.github.com/labels/42"
    assert post_[1] == [awaiting.Blocker.merge.value]

    # Core dev submits an approving review on an already closed pull request.
    username = "******"
    data = {
        "action": "submitted",
        "review": {
            "user": {
                "login": username,
            },
            "state": "APPROVED",
        },
        "pull_request": {
            "url": "https://api.github.com/pr/42",
            "issue_url": "https://api.github.com/issue/42",
            "state": "closed",
        },
    }
    event = sansio.Event(data,
                         event="pull_request_review",
                         delivery_id="12345")
    teams = [{"name": "python core", "id": 6}]
    items = {
        f"https://api.github.com/teams/6/memberships/{username}": True,
        "https://api.github.com/issue/42": {
            "labels": [{
                "name": awaiting.Blocker.changes.value
            }],
            "labels_url": "https://api.github.com/labels/42",
        },
    }
    iterators = {
        "https://api.github.com/orgs/python/teams": teams,
        "https://api.github.com/pr/42/reviews": [],
    }
    gh = FakeGH(getiter=iterators, getitem=items)
    await awaiting.router.dispatch(event, gh)
    assert not gh.post_

    # Core dev requests changes.
    data = {
        "action": "submitted",
        "review": {
            "user": {
                "login": username,
            },
            "state": "changes_requested".upper(),
        },
        "pull_request": {
            "url": "https://api.github.com/pr/42",
            "issue_url": "https://api.github.com/issue/42",
            "comments_url": "https://api.github.com/comment/42",
            "user": {
                "login": "******"
            },
            "state": "open",
        },
    }
    event = sansio.Event(data,
                         event="pull_request_review",
                         delivery_id="12345")
    items = {
        f"https://api.github.com/teams/6/memberships/{username}":
        True,
        f"https://api.github.com/teams/6/memberships/miss-islington":
        gidgethub.BadRequest(status_code=http.HTTPStatus(404)),
        "https://api.github.com/issue/42": {
            "labels": [],
            "labels_url": "https://api.github.com/labels/42",
        },
    }
    gh = FakeGH(getiter=iterators, getitem=items)
    await awaiting.router.dispatch(event, gh)
    assert len(gh.post_) == 2
    labeling = gh.post_[0]
    assert labeling[0] == "https://api.github.com/labels/42"
    assert labeling[1] == [awaiting.Blocker.changes.value]
    message = gh.post_[1]
    assert message[0] == "https://api.github.com/comment/42"
    assert awaiting.BORING_TRIGGER_PHRASE in message[1]["body"]

    # Comment reviews do nothing.
    data = {
        "action": "submitted",
        "review": {
            "user": {
                "login": username,
            },
            "state": "commented".upper(),
        },
        "pull_request": {
            "url": "https://api.github.com/pr/42",
            "issue_url": "https://api.github.com/issue/42",
            "comments_url": "https://api.github.com/comment/42",
            "state": "open",
        },
    }
    event = sansio.Event(data,
                         event="pull_request_review",
                         delivery_id="12345")
    gh = FakeGH(getiter=iterators, getitem=items)
    await awaiting.router.dispatch(event, gh)
    assert not len(gh.post_)

    # Skip commenting if "awaiting changes" is already set.
    data = {
        "action": "submitted",
        "review": {
            "user": {
                "login": username,
            },
            "state": "changes_requested".upper(),
        },
        "pull_request": {
            "url": "https://api.github.com/pr/42",
            "issue_url": "https://api.github.com/issue/42",
            "comments_url": "https://api.github.com/comment/42",
            "state": "open",
        },
    }
    event = sansio.Event(data,
                         event="pull_request_review",
                         delivery_id="12345")
    items = {
        f"https://api.github.com/teams/6/memberships/{username}": True,
        "https://api.github.com/issue/42": {
            "labels": [{
                "name": awaiting.Blocker.changes.value
            }],
            "labels_url": "https://api.github.com/labels/42",
        },
    }
    gh = FakeGH(getiter=iterators, getitem=items)
    await awaiting.router.dispatch(event, gh)
    assert not len(gh.post_)
Esempio n. 27
0
    def update(self, data):
        """Decrypt cipher text

        Cipher text must be passed to this function in the order in
        which it was output from the encryption.update function.

        data:
            (A portion of) the cipher text to be decrypted.  data
            value has to be contained in a bytes, bytearray or memoryview object.

        returns:
            any plain text produced by the call
        """

        if not isinstance(data, (bytes, bytearray, memoryview)):
            raise RuntimeError(
                "Data must be bytes, bytearray, or memoryview objects")

        #
        # each encryption has a header on it that identifies the algorithm
        # used  and an encryption of the data key that was used to encrypt
        # the original plain text. there is no guarantee how much of that
        # data will be passed to this function or how many times this
        # function will be called to process all of the data. to that end,
        # this function buffers data internally, when it is unable to
        # process it.
        #
        # the function buffers data internally until the entire header is
        # received. once the header has been received, the encrypted data
        # key is sent to the server for decryption. after the header has
        # been successfully handled, this function always decrypts all of
        # the data in its internal buffer *except* for however many bytes
        # are specified by the algorithm's tag size. see the end() function
        # for details.
        #

        self._buf += data
        pt = b''

        # if there is no key or 'dec' member of key, then the code
        # is still trying to build a complete header

        if not hasattr(self, '_key') or not 'dec' in self._key:
            fmt = '!BBBBH'
            fmtlen = struct.calcsize(fmt)

            # does the buffer contain enough of the header to
            # determine the lengths of the initialization vector
            # and the key?

            if len(self._buf) >= fmtlen:
                ver, flags, alg, veclen, keylen = struct.unpack(
                    fmt, self._buf[:fmtlen])

                # For VER 0, lsb of indicates AAD or not
                if (ver != 0) or (flags & ~algorithm.UBIQ_HEADER_V0_FLAG_AAD):
                    raise RuntimeError('invalid encryption header')

                # does the buffer contain the entire header?

                if len(self._buf) >= fmtlen + veclen + keylen:

                    # Get the Header for AAD purposes.  Only needed if
                    # version != 0, but get it now anyways
                    aad = self._buf[:fmtlen + veclen + keylen]
                    # extract the initialization vector and the key
                    vec = self._buf[fmtlen:fmtlen + veclen]
                    key = self._buf[fmtlen + veclen:fmtlen + veclen + keylen]

                    # remove the header from the buffer
                    self._buf = self._buf[fmtlen + veclen + keylen:]

                    # generate a local identifier for the key
                    sha = crypto.hashes.Hash(crypto.hashes.SHA256(),
                                             backend=crypto_backend())
                    sha.update(key)
                    client_id = sha.finalize()

                    # if the object already has a key (from a previous
                    # decryption), is the key in this header the same as
                    # that previous one?
                    #
                    # if not, clear out the existing key
                    if hasattr(self, '_key'):
                        if self._key['client_id'] != client_id:
                            self.reset()

                    # if the object (still) has a key, then it can be
                    # reused--see below. if not, then request a decryption
                    # of the key in the current header from the server
                    if not hasattr(self, '_key'):
                        url = self._endpoint_base() + '/decryption/key'
                        response = requests.post(
                            url,
                            data=json.dumps({
                                'encrypted_data_key':
                                base64.b64encode(key).decode('utf-8')
                            }).encode('utf-8'),
                            auth=http_auth(self._papi, self._sapi))
                        if response.status_code != http.HTTPStatus.OK:
                            raise urllib.error.HTTPError(
                                url, response.status_code,
                                http.HTTPStatus(response.status_code).phrase,
                                response.headers, response.content)

                        content = json.loads(response.content.decode('utf-8'))

                        self._key = {}

                        self._key['algo'] = algorithm(alg)
                        # the client's id for recognizing key reuse
                        self._key['client_id'] = client_id
                        # the server's id for sending updates
                        self._key['finger_print'] = content['key_fingerprint']
                        self._key['session'] = content['encryption_session']

                        # decrypt the client's private key (sent
                        # by the server)
                        prvkey = crypto.serialization.load_pem_private_key(
                            content['encrypted_private_key'].encode('utf-8'),
                            self._srsa.encode('utf-8'), crypto_backend())
                        # use the private key to decrypt the data key
                        self._key['raw'] = prvkey.decrypt(
                            base64.b64decode(content['wrapped_data_key']),
                            crypto.asymmetric.padding.OAEP(
                                mgf=crypto.asymmetric.padding.MGF1(
                                    algorithm=crypto.hashes.SHA1()),
                                algorithm=crypto.hashes.SHA1(),
                                label=None))

                        # this key hasn't been used (yet)
                        self._key['uses'] = 0

                    # if the key object exists, create a new decryptor
                    # with the initialization vector from the header and
                    # the decrypted key (which is either new from the
                    # server or cached from the previous decryption). in
                    # either case, increment the key usage
                    if hasattr(self, '_key'):
                        self._key['dec'] = self._key['algo'].decryptor(
                            self._key['raw'], vec)
                        self._key['uses'] += 1

                        if (flags & algorithm.UBIQ_HEADER_V0_FLAG_AAD):
                            self._key['dec'].authenticate_additional_data(aad)

        # if the object has a key and a decryptor, then decrypt whatever
        # data is in the buffer, less any data that needs to be saved to
        # serve as the tag.
        if hasattr(self, '_key') and 'dec' in self._key:
            sz = len(self._buf) - self._key['algo'].len['tag']
            if sz > 0:
                pt = self._key['dec'].update(self._buf[:sz])
                self._buf = self._buf[sz:]

        return pt
Esempio n. 28
0
async def test_non_core_dev_does_not_downgrade():
    core_dev = "brettcannon"
    non_core_dev = "andreamcinnes"
    teams = [{"name": "python core", "id": 6}]
    items = {
        f"https://api.github.com/teams/6/memberships/{non_core_dev}":
        gidgethub.BadRequest(status_code=http.HTTPStatus(404)),
        f"https://api.github.com/teams/6/memberships/{core_dev}":
        True,
        "https://api.github.com/issue/42": {
            "labels": [],
            "labels_url": "https://api.github.com/labels/42",
        },
    }

    # Approval from a core dev changes the state to "Awaiting merge".
    data = {
        "action": "submitted",
        "review": {
            "state": "approved",
            "user": {
                "login": core_dev,
            },
        },
        "pull_request": {
            "url": "https://api.github.com/pr/42",
            "issue_url": "https://api.github.com/issue/42",
            "state": "open",
        },
    }
    event = sansio.Event(data,
                         event="pull_request_review",
                         delivery_id="12345")
    iterators = {
        "https://api.github.com/orgs/python/teams":
        teams,
        "https://api.github.com/pr/42/reviews": [{
            "user": {
                "login": core_dev
            },
            "state": "approved"
        }],
    }
    gh = FakeGH(getiter=iterators, getitem=items)
    await awaiting.router.dispatch(event, gh)
    assert len(gh.post_) == 1
    post_ = gh.post_[0]
    assert post_[0] == "https://api.github.com/labels/42"
    assert post_[1] == [awaiting.Blocker.merge.value]

    # Non-comment review from a non-core dev doesn't "downgrade" the PR's state.
    data = {
        "action": "submitted",
        "review": {
            "state": "approved",
            "user": {
                "login": non_core_dev,
            },
        },
        "pull_request": {
            "url": "https://api.github.com/pr/42",
            "issue_url": "https://api.github.com/issue/42",
            "state": "open",
        },
    }
    event = sansio.Event(data,
                         event="pull_request_review",
                         delivery_id="12345")
    iterators = {
        "https://api.github.com/orgs/python/teams":
        teams,
        "https://api.github.com/pr/42/reviews": [
            {
                "user": {
                    "login": core_dev
                },
                "state": "approved"
            },
            {
                "user": {
                    "login": non_core_dev
                },
                "state": "approved"
            },
        ],
    }
    gh = FakeGH(getiter=iterators, getitem=items)
    await awaiting.router.dispatch(event, gh)
    assert not gh.post_
Esempio n. 29
0
    def response(
        self,
        status_code,
        schema=None,
        *,
        content_type=None,
        description=None,
        example=None,
        examples=None,
        headers=None,
    ):
        """Decorator generating an endpoint response

        :param int|str|HTTPStatus status_code: HTTP status code.
            Used if none is returned from the view function.
        :param schema schema|str|dict: :class:`Schema <marshmallow.Schema>`
            class or instance or reference or dict.
            If not None, will be used to serialize response data.
        :param str content_type: Content type of the response.
        :param str description: Description of the response (default: None).
        :param dict example: Example of response message.
        :param dict examples: Examples of response message.
        :param dict headers: Headers returned by the response.

        The decorated function is expected to return the same types of value
        than a typical flask view function, except the body part may be an
        object or a list of objects to serialize with the schema, rather than
        a ``string``.

        If the decorated function returns a ``Response`` object, the ``schema``
        and ``status_code`` parameters are only used to document the resource.
        Only in this case, ``schema`` may be a reference as string or a schema
        definition as dict.

        The `example` and `examples` parameters are mutually exclusive. The
        latter should only be used with OpenAPI 3.

        The `example`, `examples` and `headers` parameters are only used to
        document the resource.

        See :doc:`Response <response>`.
        """
        if isinstance(schema, type):
            schema = schema()

        # Document response (schema, description,...) in the API doc
        doc_schema = self._make_doc_response_schema(schema)
        if description is None:
            description = http.HTTPStatus(int(status_code)).phrase
        resp_doc = remove_none({
            "schema": doc_schema,
            "description": description,
            "example": example,
            "examples": examples,
            "headers": headers,
        })
        resp_doc["content_type"] = content_type

        def decorator(func):
            @wraps(func)
            def wrapper(*args, **kwargs):

                # Execute decorated function
                result_raw, r_status_code, r_headers = unpack_tuple_response(
                    func(*args, **kwargs))

                # If return value is a werkzeug Response, return it
                if isinstance(result_raw, Response):
                    set_status_and_headers_in_response(result_raw,
                                                       r_status_code,
                                                       r_headers)
                    return result_raw

                # Dump result with schema if specified
                if schema is None:
                    result_dump = result_raw
                else:
                    result_dump = schema.dump(result_raw)

                # Store result in appcontext (may be used for ETag computation)
                appcontext = get_appcontext()
                appcontext["result_raw"] = result_raw
                appcontext["result_dump"] = result_dump

                # Build response
                resp = jsonify(self._prepare_response_content(result_dump))
                set_status_and_headers_in_response(resp, r_status_code,
                                                   r_headers)
                if r_status_code is None:
                    resp.status_code = status_code

                return resp

            # Store doc in wrapper function
            # The deepcopy avoids modifying the wrapped function doc
            wrapper._apidoc = deepcopy(getattr(wrapper, "_apidoc", {}))
            wrapper._apidoc.setdefault("response", {}).setdefault(
                "responses", {})[status_code] = resp_doc
            # Indicate this code is a success status code
            # Helps other decorators documenting success responses
            wrapper._apidoc.setdefault("success_status_codes",
                                       []).append(status_code)

            return wrapper

        return decorator
Esempio n. 30
0
    def run(self, command, **data):
        """Run the specified command. If no http_config given, it is read from
        the user config. The data kwargs are passed to the HTTP request.
        'period' and 'table_name' are substituted, if None.

        :return: dict. See Server class for possible keys
        :raise: ValueError if invalid command given
        :raise: CommunicationError on e.g. timeouts or server-side errors,
            InvalidRequest on invalid requests
        """

        period = data.pop("period", None) or default_period_name()

        host = self.http_config.get("host", DEFAULT_HOST)
        base_url = "{}{}".format(host, PERIODS_TAIL)
        period_url = "{}/{}".format(base_url, period)
        copy_url = "{}{}".format(host, COPY_TAIL)
        eid_url = "{}/{}/{}".format(period_url,
                                    data.get("table_name") or DEFAULT_TABLE,
                                    data.get("eid"))
        version_url = "{}/version".format(host)

        username = self.http_config.get("username")
        password = self.http_config.get("password")
        auth = None
        if username and password:
            auth = (username, password)

        kwargs = dict(auth=auth, timeout=DEFAULT_TIMEOUT)

        if command == "list":
            # Correctly send filters; allowing for server-side deserialization
            kwargs["json"] = json.dumps(data)
        else:
            kwargs["json"] = data or None

        if command == "list":
            url = period_url
            function = requests.get
        elif command == "rm":
            url = eid_url
            function = requests.delete
        elif command == "add":
            url = period_url
            function = requests.post
        elif command == "periods":
            url = base_url
            function = requests.post
        elif command == "copy":
            url = copy_url
            function = requests.post
        elif command == "get":
            url = eid_url
            function = requests.get
        elif command == "update":
            url = eid_url
            function = requests.patch
        elif command == "service":
            url = version_url
            function = requests.get
        else:
            raise ValueError("Unknown command: {}".format(command))

        try:
            response = function(url, **kwargs)
        except requests.RequestException as e:
            raise CommunicationError("Error sending request: {}".format(e))

        if response.ok:
            return response.json()
        else:
            try:
                # Get further information about error (see Server.run)
                error = response.json()["error"]
            except (json.JSONDecodeError, KeyError):
                error = "-"

            status_code = response.status_code
            if 400 <= status_code < 500:
                error_class = InvalidRequest
            else:
                error_class = CommunicationError

            message = "Error handling request. " +\
                "Server returned '{} ({}): {}'".format(
                    http.HTTPStatus(status_code).phrase, status_code, error)

            raise error_class(message)