Ejemplo n.º 1
0
    def get_request_body(self):
        """
        Decodes the request body and returns it.

        :return: the decoded request body as a :class:`dict` instance.
        :raises: :class:`tornado.web.HTTPError` if the body cannot be
            decoded (415) or if decoding fails (400)

        """
        if self._request_body is None:
            content_type_str = self.request.headers.get(
                'Content-Type', 'application/octet-stream')
            LOGGER.debug('decoding request body of type %s', content_type_str)
            content_type = headers.parse_content_type(content_type_str)
            try:
                selected, requested = algorithms.select_content_type(
                    [content_type], _content_types.values())
            except errors.NoMatch:
                raise web.HTTPError(
                    415, 'cannot decoded content type %s', content_type_str,
                    reason='Unexpected content type')
            handler = _content_handlers[str(selected)]
            try:
                self._request_body = handler.unpack_bytes(
                    self.request.body,
                    encoding=content_type.parameters.get('charset'),
                )
            except ValueError as error:
                raise web.HTTPError(
                    400, 'failed to decode content body - %r', error,
                    reason='Content body decode failure')
        return self._request_body
Ejemplo n.º 2
0
    def _deserialize(self):
        """Try and deserialize a response body based upon the specified
        content type.

        :rtype: mixed

        """
        if not self._responses or not self._responses[-1].body:
            return None
        if 'Content-Type' not in self._responses[-1].headers:
            return self._responses[-1].body
        try:
            content_type = algorithms.select_content_type(
                [headers.parse_content_type(
                    self._responses[-1].headers['Content-Type'])],
                AVAILABLE_CONTENT_TYPES)
        except errors.NoMatch:
            return self._responses[-1].body

        if content_type[0] == CONTENT_TYPE_JSON:
            return self._decode(
                self._json.loads(self._decode(self._responses[-1].body)))
        elif content_type[0] == CONTENT_TYPE_MSGPACK:  # pragma: nocover
            return self._decode(
                self._msgpack.unpackb(self._responses[-1].body))
Ejemplo n.º 3
0
 def assertContentTypeMatchedAs(self, expected, *supported, **kwargs):
     selected, matched = algorithms.select_content_type(
         self.requested,
         [headers.parse_content_type(value) for value in supported],
     )
     self.assertEqual(
         selected, headers.parse_content_type(expected),
         '\nExpected to select "{!s}", actual selection was "{!s}"'.format(
             expected, selected,
         ))
     if 'matching_pattern' in kwargs:
         self.assertEqual(str(matched), kwargs['matching_pattern'])
Ejemplo n.º 4
0
 def test_that_explicit_priority_1_is_preferred(self):
     selected, matched = algorithms.select_content_type(
         headers.parse_accept(
             'application/vnd.com.example+json, '
             'application/vnd.com.example+json;version=1;q=1.0, '
             'application/vnd.com.example+json;version=2'),
         [headers.parse_content_type(value)
          for value in ('application/vnd.com.example+json;version=1',
                        'application/vnd.com.example+json;version=2',
                        'application/vnd.com.example+json;version=3')],
     )
     self.assertEqual(str(selected),
                      'application/vnd.com.example+json; version=1')
Ejemplo n.º 5
0
 def test_that_multiple_matches_result_in_any_appropriate_value(self):
     # note that this also tests that duplicated values are acceptable
     selected, matched = algorithms.select_content_type(
         headers.parse_accept(
             'application/vnd.com.example+json;version=1, '
             'application/vnd.com.example+json;version=1, '
             'application/vnd.com.example+json;version=1;q=0.9, '
             'application/vnd.com.example+json;version=2;q=0.9'),
         [headers.parse_content_type(value)
          for value in ('application/vnd.com.example+json;version=1',
                        'application/vnd.com.example+json;version=2',
                        'application/vnd.com.example+json;version=3')],
     )
     self.assertEqual(str(selected),
                      'application/vnd.com.example+json; version=1')
Ejemplo n.º 6
0
    def _get_response_content_type(self):
        """Detect the requested Content-Type by parsing the ``Accept`` header.

        :rtype: str

        """
        accept = self.request.headers.get('Accept')
        if accept == '*/*' or not accept:
            accept = self.settings['default_content_type']
        acceptable = headers.parse_accept(accept)
        try:
            selected, _ = algorithms.select_content_type(
                acceptable, self.application.transcoders[0])
        except errors.NoMatch:
            return self.settings['default_content_type']
        else:
            return '/'.join([selected.content_type, selected.content_subtype])
Ejemplo n.º 7
0
    def send_response(self, response_dict):
        """
        Encode a response according to the request.

        :param dict response_dict: the response to send

        :raises: :class:`tornado.web.HTTPError` if no acceptable content
            type exists

        This method will encode `response_dict` using the most appropriate
        encoder based on the :mailheader:`Accept` request header and the
        available encoders.  The result is written to the client by calling
        ``self.write`` after setting the response content type using
        ``self.set_header``.

        """
        accept = headers.parse_http_accept_header(
            self.request.headers.get('Accept', '*/*'))
        try:
            selected, _ = algorithms.select_content_type(
                accept, _content_types.values())
        except errors.NoMatch:
            raise web.HTTPError(406,
                                'no acceptable content type for %s in %r',
                                accept, _content_types.values(),
                                reason='Content Type Not Acceptable')

        LOGGER.debug('selected %s as outgoing content type', selected)
        handler = _content_handlers[str(selected)]

        accept = self.request.headers.get('Accept-Charset', '*')
        charsets = headers.parse_accept_charset(accept)
        charset = charsets[0] if charsets[0] != '*' else None
        LOGGER.debug('encoding response body using %r with encoding %s',
                     handler, charset)
        encoding, response_bytes = handler.pack_bytes(response_dict,
                                                      encoding=charset)

        if encoding:  # don't overwrite the value in _content_types
            copied = datastructures.ContentType(selected.content_type,
                                                selected.content_subtype,
                                                selected.parameters)
            copied.parameters['charset'] = encoding
            selected = copied
        self.set_header('Content-Type', str(selected))
        self.write(response_bytes)
Ejemplo n.º 8
0
    def get_response_content_type(self):
        """Figure out what content type will be used in the response."""
        if self._best_response_match is None:
            settings = get_settings(self.application, force_instance=True)
            acceptable = headers.parse_accept(
                self.request.headers.get(
                    'Accept',
                    settings.default_content_type
                    if settings.default_content_type else '*/*'))
            try:
                selected, _ = algorithms.select_content_type(
                    acceptable, settings.available_content_types)
                self._best_response_match = '/'.join(
                    [selected.content_type, selected.content_subtype])
            except errors.NoMatch:
                self._best_response_match = settings.default_content_type

        return self._best_response_match
Ejemplo n.º 9
0
    def _http_resp_deserialize(self, response):
        """Try and deserialize a response body based upon the specified
        content type.

        :param tornado.httpclient.HTTPResponse: The HTTP response to decode
        :rtype: mixed

        """
        if not response.body:
            return None
        try:
            content_type = algorithms.select_content_type(
                [headers.parse_content_type(response.headers['Content-Type'])],
                self.AVAILABLE_CONTENT_TYPES)
        except errors.NoMatch:
            return response.body

        if content_type[0] == CONTENT_TYPE_JSON:
            return self._http_resp_decode(
                json.loads(self._http_resp_decode(response.body)))
        elif content_type[0] == CONTENT_TYPE_MSGPACK:
            return self._http_resp_decode(umsgpack.unpackb(response.body))
Ejemplo n.º 10
0
    def get_body_arguments(self) -> typing.Optional[dict]:
        """Parse the request body

        Use the configured transcoders to parse the content using the
        ``Content-Type`` header field to determine which transcoder to use.

        """
        if not self.request.body:
            return None
        parsed = headers.parse_content_type(
            self.request.headers.get('Content-Type', ''))
        if not parsed:
            return None
        if not isinstance(parsed, tuple):
            parsed = parsed,
        try:
            selected, _ = algorithms.select_content_type(
                parsed, self.application.transcoders[0])
        except errors.NoMatch:
            raise ValueError('Cant transcode a Content-Type of {}'.format(
                self.request.headers.get('Content-Type', '')))
        key = '/'.join([selected.content_type, selected.content_subtype])
        transcoder = self.application.transcoders[1][key]
        return transcoder.from_bytes(self.request.body)
Ejemplo n.º 11
0
 def test_that_inappropriate_value_is_not_matched(self):
     with self.assertRaises(errors.NoMatch):
         algorithms.select_content_type(
             self.requested,
             [headers.parse_content_type('image/png')],
         )
Ejemplo n.º 12
0
 def test_that_zero_quality_is_not_matched(self):
     with self.assertRaises(errors.NoMatch):
         algorithms.select_content_type(
             self.requested,
             [headers.parse_content_type('text/javascript')],
         )