mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-21 17:16:47 +03:00
Replaced parse_header with parse_header_parameters. (#8556)
Add a backwards compatibility shim for Django versions that have no (or an incompatible) django.utils.http.parse_header_parameters implementation. Thanks to Shai Berger for review. Co-authored-by: Jaap Roes <jroes@leukeleu.nl>
This commit is contained in:
parent
101aff6c43
commit
ad282da97c
|
@ -2,6 +2,7 @@
|
|||
The `compat` module provides support for backwards compatibility with older
|
||||
versions of Django/Python, and compatibility wrappers around optional packages.
|
||||
"""
|
||||
import django
|
||||
from django.conf import settings
|
||||
from django.views.generic import View
|
||||
|
||||
|
@ -152,6 +153,30 @@ else:
|
|||
return False
|
||||
|
||||
|
||||
if django.VERSION >= (4, 2):
|
||||
# Django 4.2+: use the stock parse_header_parameters function
|
||||
# Note: Django 4.1 also has an implementation of parse_header_parameters
|
||||
# which is slightly different from the one in 4.2, it needs
|
||||
# the compatibility shim as well.
|
||||
from django.utils.http import parse_header_parameters
|
||||
else:
|
||||
# Django <= 4.1: create a compatibility shim for parse_header_parameters
|
||||
from django.http.multipartparser import parse_header
|
||||
|
||||
def parse_header_parameters(line):
|
||||
# parse_header works with bytes, but parse_header_parameters
|
||||
# works with strings. Call encode to convert the line to bytes.
|
||||
main_value_pair, params = parse_header(line.encode())
|
||||
return main_value_pair, {
|
||||
# parse_header will convert *some* values to string.
|
||||
# parse_header_parameters converts *all* values to string.
|
||||
# Make sure all values are converted by calling decode on
|
||||
# any remaining non-string values.
|
||||
k: v if isinstance(v, str) else v.decode()
|
||||
for k, v in params.items()
|
||||
}
|
||||
|
||||
|
||||
# `separators` argument to `json.dumps()` differs between 2.x and 3.x
|
||||
# See: https://bugs.python.org/issue22767
|
||||
SHORT_SEPARATORS = (',', ':')
|
||||
|
|
|
@ -4,7 +4,7 @@ incoming request. Typically this will be based on the request's Accept header.
|
|||
"""
|
||||
from django.http import Http404
|
||||
|
||||
from rest_framework import HTTP_HEADER_ENCODING, exceptions
|
||||
from rest_framework import exceptions
|
||||
from rest_framework.settings import api_settings
|
||||
from rest_framework.utils.mediatypes import (
|
||||
_MediaType, media_type_matches, order_by_precedence
|
||||
|
@ -64,9 +64,11 @@ class DefaultContentNegotiation(BaseContentNegotiation):
|
|||
# Accepted media type is 'application/json'
|
||||
full_media_type = ';'.join(
|
||||
(renderer.media_type,) +
|
||||
tuple('{}={}'.format(
|
||||
key, value.decode(HTTP_HEADER_ENCODING))
|
||||
for key, value in media_type_wrapper.params.items()))
|
||||
tuple(
|
||||
'{}={}'.format(key, value)
|
||||
for key, value in media_type_wrapper.params.items()
|
||||
)
|
||||
)
|
||||
return renderer, full_media_type
|
||||
else:
|
||||
# Eg client requests 'application/json; indent=8'
|
||||
|
|
|
@ -5,7 +5,6 @@ They give us a generic way of being able to handle various media types
|
|||
on the request, such as form content or json encoded data.
|
||||
"""
|
||||
import codecs
|
||||
from urllib import parse
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.uploadhandler import StopFutureHandlers
|
||||
|
@ -13,10 +12,10 @@ from django.http import QueryDict
|
|||
from django.http.multipartparser import ChunkIter
|
||||
from django.http.multipartparser import \
|
||||
MultiPartParser as DjangoMultiPartParser
|
||||
from django.http.multipartparser import MultiPartParserError, parse_header
|
||||
from django.utils.encoding import force_str
|
||||
from django.http.multipartparser import MultiPartParserError
|
||||
|
||||
from rest_framework import renderers
|
||||
from rest_framework.compat import parse_header_parameters
|
||||
from rest_framework.exceptions import ParseError
|
||||
from rest_framework.settings import api_settings
|
||||
from rest_framework.utils import json
|
||||
|
@ -201,23 +200,10 @@ class FileUploadParser(BaseParser):
|
|||
|
||||
try:
|
||||
meta = parser_context['request'].META
|
||||
disposition = parse_header(meta['HTTP_CONTENT_DISPOSITION'].encode())
|
||||
filename_parm = disposition[1]
|
||||
if 'filename*' in filename_parm:
|
||||
return self.get_encoded_filename(filename_parm)
|
||||
return force_str(filename_parm['filename'])
|
||||
disposition, params = parse_header_parameters(meta['HTTP_CONTENT_DISPOSITION'])
|
||||
if 'filename*' in params:
|
||||
return params['filename*']
|
||||
else:
|
||||
return params['filename']
|
||||
except (AttributeError, KeyError, ValueError):
|
||||
pass
|
||||
|
||||
def get_encoded_filename(self, filename_parm):
|
||||
"""
|
||||
Handle encoded filenames per RFC6266. See also:
|
||||
https://tools.ietf.org/html/rfc2231#section-4
|
||||
"""
|
||||
encoded_filename = force_str(filename_parm['filename*'])
|
||||
try:
|
||||
charset, lang, filename = encoded_filename.split('\'', 2)
|
||||
filename = parse.unquote(filename)
|
||||
except (ValueError, LookupError):
|
||||
filename = force_str(filename_parm['filename'])
|
||||
return filename
|
||||
|
|
|
@ -14,7 +14,6 @@ from django import forms
|
|||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.core.paginator import Page
|
||||
from django.http.multipartparser import parse_header
|
||||
from django.template import engines, loader
|
||||
from django.urls import NoReverseMatch
|
||||
from django.utils.html import mark_safe
|
||||
|
@ -22,7 +21,7 @@ from django.utils.html import mark_safe
|
|||
from rest_framework import VERSION, exceptions, serializers, status
|
||||
from rest_framework.compat import (
|
||||
INDENT_SEPARATORS, LONG_SEPARATORS, SHORT_SEPARATORS, coreapi, coreschema,
|
||||
pygments_css, yaml
|
||||
parse_header_parameters, pygments_css, yaml
|
||||
)
|
||||
from rest_framework.exceptions import ParseError
|
||||
from rest_framework.request import is_form_media_type, override_method
|
||||
|
@ -72,7 +71,7 @@ class JSONRenderer(BaseRenderer):
|
|||
# If the media type looks like 'application/json; indent=4',
|
||||
# then pretty print the result.
|
||||
# Note that we coerce `indent=0` into `indent=None`.
|
||||
base_media_type, params = parse_header(accepted_media_type.encode('ascii'))
|
||||
base_media_type, params = parse_header_parameters(accepted_media_type)
|
||||
try:
|
||||
return zero_as_none(max(min(int(params['indent']), 8), 0))
|
||||
except (KeyError, ValueError, TypeError):
|
||||
|
|
|
@ -14,11 +14,11 @@ from contextlib import contextmanager
|
|||
|
||||
from django.conf import settings
|
||||
from django.http import HttpRequest, QueryDict
|
||||
from django.http.multipartparser import parse_header
|
||||
from django.http.request import RawPostDataException
|
||||
from django.utils.datastructures import MultiValueDict
|
||||
|
||||
from rest_framework import HTTP_HEADER_ENCODING, exceptions
|
||||
from rest_framework import exceptions
|
||||
from rest_framework.compat import parse_header_parameters
|
||||
from rest_framework.settings import api_settings
|
||||
|
||||
|
||||
|
@ -26,7 +26,7 @@ def is_form_media_type(media_type):
|
|||
"""
|
||||
Return True if the media type is a valid form media type.
|
||||
"""
|
||||
base_media_type, params = parse_header(media_type.encode(HTTP_HEADER_ENCODING))
|
||||
base_media_type, params = parse_header_parameters(media_type)
|
||||
return (base_media_type == 'application/x-www-form-urlencoded' or
|
||||
base_media_type == 'multipart/form-data')
|
||||
|
||||
|
|
|
@ -3,9 +3,7 @@ Handling of media types, as found in HTTP Content-Type and Accept headers.
|
|||
|
||||
See https://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
|
||||
"""
|
||||
from django.http.multipartparser import parse_header
|
||||
|
||||
from rest_framework import HTTP_HEADER_ENCODING
|
||||
from rest_framework.compat import parse_header_parameters
|
||||
|
||||
|
||||
def media_type_matches(lhs, rhs):
|
||||
|
@ -46,7 +44,7 @@ def order_by_precedence(media_type_lst):
|
|||
class _MediaType:
|
||||
def __init__(self, media_type_str):
|
||||
self.orig = '' if (media_type_str is None) else media_type_str
|
||||
self.full_type, self.params = parse_header(self.orig.encode(HTTP_HEADER_ENCODING))
|
||||
self.full_type, self.params = parse_header_parameters(self.orig)
|
||||
self.main_type, sep, self.sub_type = self.full_type.partition('/')
|
||||
|
||||
def match(self, other):
|
||||
|
@ -79,5 +77,5 @@ class _MediaType:
|
|||
def __str__(self):
|
||||
ret = "%s/%s" % (self.main_type, self.sub_type)
|
||||
for key, val in self.params.items():
|
||||
ret += "; %s=%s" % (key, val.decode('ascii'))
|
||||
ret += "; %s=%s" % (key, val)
|
||||
return ret
|
||||
|
|
Loading…
Reference in New Issue
Block a user