Пример #1
0
 def _serialize_error(
     self,
     error: ServiceException,
     code: str,
     sender_fault: bool,
     serialized: HttpResponse,
     shape: Shape,
     operation_model: OperationModel,
 ) -> None:
     # TODO handle error shapes with members
     body = {"__type": code, "message": str(error)}
     serialized.set_json(body)
Пример #2
0
 def _serialize_payload(
     self,
     parameters: dict,
     serialized: HttpResponse,
     shape: Optional[Shape],
     shape_members: dict,
     operation_model: OperationModel,
 ) -> None:
     json_version = operation_model.metadata.get("jsonVersion")
     if json_version is not None:
         serialized.headers[
             "Content-Type"] = "application/x-amz-json-%s" % json_version
     body = {}
     if shape is not None:
         self._serialize(body, parameters, shape)
     serialized.set_json(body)
Пример #3
0
 def _serialize_error(
     self,
     error: ServiceException,
     code: str,
     sender_fault: bool,
     serialized: HttpResponse,
     shape: Shape,
     operation_model: OperationModel,
 ) -> None:
     # EC2 errors look like:
     # <Response>
     #   <Errors>
     #     <Error>
     #       <Code>InvalidInstanceID.Malformed</Code>
     #       <Message>Invalid id: "1343124"</Message>
     #     </Error>
     #   </Errors>
     #   <RequestID>12345</RequestID>
     # </Response>
     # This is different from QueryParser in that it's RequestID, not RequestId
     # and that the Error tag is in an enclosing Errors tag.
     attr = ({
         "xmlns": operation_model.metadata.get("xmlNamespace")
     } if "xmlNamespace" in operation_model.metadata else None)
     root = ETree.Element("Errors", attr)
     error_tag = ETree.SubElement(root, "Error")
     self._add_error_tags(code, error, error_tag, sender_fault)
     request_id = ETree.SubElement(root, "RequestID")
     request_id.text = gen_amzn_requestid_long()
     serialized.data = self._encode_payload(
         ETree.tostring(root, encoding=self.DEFAULT_ENCODING))
Пример #4
0
 def _prepare_additional_traits_in_response(
         self, response: HttpResponse, operation_model: OperationModel):
     """Adds the request ID to the headers (in contrast to the body - as in the Query protocol)."""
     response = super()._prepare_additional_traits_in_response(
         response, operation_model)
     response.headers["x-amz-request-id"] = gen_amzn_requestid_long()
     return response
Пример #5
0
 def to_server_response(self, response: HttpResponse):
     # TODO: creating response objects in this way (re-using the requests library instead of an HTTP server
     #  framework) is a bit ugly, but it's the way that the edge proxy expects them.
     resp = Response()
     resp._content = response.get_data()
     resp.status_code = response.status_code
     resp.headers.update(response.headers)
     return resp
Пример #6
0
    def _create_default_response(self, operation_model: OperationModel) -> HttpResponse:
        """
        Creates a boilerplate default response to be used by subclasses as starting points.
        Uses the default HTTP response status code defined in the operation model (if defined).

        :param operation_model: to extract the default HTTP status code
        :return: boilerplate HTTP response
        """
        return HttpResponse(response=b"", status=operation_model.http.get("responseCode", 200))
Пример #7
0
    def _serialize_response(
        self,
        parameters: dict,
        response: HttpResponse,
        shape: Optional[Shape],
        shape_members: dict,
        operation_model: OperationModel,
    ) -> None:
        """
        Serializes the given parameters as XML.

        :param parameters: The user input params
        :param response: The final serialized HttpResponse
        :param shape: Describes the expected output shape (can be None in case of an "empty" response)
        :param shape_members: The members of the output struct shape
        :param operation_model: The specification of the operation of which the response is serialized here
        :return: None - the given `serialized` dict is modified
        """
        # TODO implement the handling of location traits (where "location" is "header", "headers")

        payload_member = shape.serialization.get("payload") if shape is not None else None
        if payload_member is not None and shape_members[payload_member].type_name in [
            "blob",
            "string",
        ]:
            # If it's streaming, then the body is just the value of the payload.
            body_payload = parameters.get(payload_member, b"")
            body_payload = self._encode_payload(body_payload)
            response.data = body_payload
        elif payload_member is not None:
            # If there's a payload member, we serialized that member to the body.
            body_params = parameters.get(payload_member)
            if body_params is not None:
                response.data = self._encode_payload(
                    self._serialize_body_params(
                        body_params, shape_members[payload_member], operation_model
                    )
                )
        else:
            # Otherwise, we use the "traditional" way of serializing the whole parameters dict recursively.
            response.data = self._encode_payload(
                self._serialize_body_params(parameters, shape, operation_model)
            )
Пример #8
0
def proxy_moto(context: RequestContext) -> HttpResponse:
    """
    Similar to ``call``, only that ``proxy`` does not parse the HTTP response into a ServiceResponse, but instead
    returns directly the HTTP response. This can be useful to pass through moto's response directly to the client.

    :param context: the request context
    :return: the HttpResponse from moto
    """
    status, headers, content = dispatch_to_moto(context)

    return HttpResponse(response=content, status=status, headers=headers)
Пример #9
0
 def _serialize_error(
     self,
     error: ServiceException,
     code: str,
     sender_fault: bool,
     serialized: HttpResponse,
     shape: Shape,
     operation_model: OperationModel,
 ) -> None:
     # TODO handle error shapes with members
     # Check if we need to add a namespace
     attr = ({
         "xmlns": operation_model.metadata.get("xmlNamespace")
     } if "xmlNamespace" in operation_model.metadata else {})
     root = ETree.Element("ErrorResponse", attr)
     error_tag = ETree.SubElement(root, "Error")
     self._add_error_tags(code, error, error_tag, sender_fault)
     request_id = ETree.SubElement(root, "RequestId")
     request_id.text = gen_amzn_requestid_long()
     serialized.data = self._encode_payload(
         ETree.tostring(root, encoding=self.DEFAULT_ENCODING))
Пример #10
0
 def _serialize_error(
     self,
     error: ServiceException,
     code: str,
     sender_fault: bool,
     serialized: HttpResponse,
     shape: Shape,
     operation_model: OperationModel,
 ) -> None:
     # It wouldn't be a spec if there wouldn't be any exceptions.
     # S3 errors look differently than other service's errors.
     if operation_model.name == "s3":
         attr = ({
             "xmlns": operation_model.metadata.get("xmlNamespace")
         } if "xmlNamespace" in operation_model.metadata else None)
         root = ETree.Element("Error", attr)
         self._add_error_tags(code, error, root, sender_fault)
         request_id = ETree.SubElement(root, "RequestId")
         request_id.text = gen_amzn_requestid_long()
         serialized.data = self._encode_payload(
             ETree.tostring(root, encoding=self.DEFAULT_ENCODING))
     else:
         super()._serialize_error(error, code, sender_fault, serialized,
                                  shape, operation_model)
Пример #11
0
 def _prepare_additional_traits_in_response(
         self, response: HttpResponse, operation_model: OperationModel):
     response.headers["x-amzn-requestid"] = gen_amzn_requestid_long()
     response = super()._prepare_additional_traits_in_response(
         response, operation_model)
     return response
Пример #12
0
    def _serialize_response(
        self,
        parameters: dict,
        response: HttpResponse,
        shape: Optional[Shape],
        shape_members: dict,
        operation_model: OperationModel,
    ) -> None:
        json_version = operation_model.metadata.get("jsonVersion")
        if json_version is not None:
            response.headers["Content-Type"] = "application/x-amz-json-%s" % json_version

        if shape is None:
            response.set_json({})
            return

        # first, serialize everything into a dictionary
        data = {}
        self._serialize(data, parameters, shape)

        if shape.serialization is None:
            # if there are no special serialization instructions then just set the data dict as JSON payload
            response.set_json(data)
            return

        # otherwise, move data attributes into their appropriate locations
        # (some shapes have a serialization dict, but don't actually have attributes with special locations)
        body = {}
        for name in shape_members:
            member_shape = shape_members[name]
            key = member_shape.serialization.get("name", name)
            if key not in data:
                # ignores optional keys
                continue
            value = data[key]

            location = member_shape.serialization.get("location")

            if not location:
                body[key] = value
            elif location == "header":
                response.headers[key] = value
            elif location == "headers":
                response.headers.update(value)
            elif location == "statusCode":
                # statusCode is quite rare, and it looks like it's always just an int shape, so taking a shortcut here
                response.status_code = int(value)
            else:
                raise ValueError("unknown location %s" % location)

        # the shape defines a specific attribute that should be serialized as payload
        payload_key = shape.serialization.get("payload")
        if not payload_key:
            response.set_json(body)
            return
        payload_shape = shape.members[payload_key]

        if payload_key not in body:
            LOG.warning("missing payload attribute %s in body", payload_key)

        value = body.pop(payload_key, None)

        if body:
            # in principle, if a payload attribute is specified in the shape, all other attributes should be in other
            # locations, so this is just a logging guard to warn if that assumption is violated
            LOG.warning("dangling attributes in body: %s", body.keys())

        # a payload can be a string, blob, or structure: https://gist.github.com/thrau/39fd20b437f8719ffc361ad9a908c0c6
        if payload_shape.type_name == "structure":
            response.set_json(value if value is not None else {})
        elif payload_shape.type_name == "blob":
            # payloads blobs are not base64 encoded, but the `_serialize` recursion doesn't know that
            response.data = base64.b64decode(value) if value is not None else b""
        else:
            response.data = value if value is not None else b""