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)
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)
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))
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
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
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))
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) )
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)
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))
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)
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
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""