Fixed #29186 -- Fixed pickling HttpRequest and subclasses.
This commit is contained in:
parent
e14d08cd89
commit
6220c445c4
|
@ -64,6 +64,9 @@ class LimitedStream:
|
|||
|
||||
|
||||
class WSGIRequest(HttpRequest):
|
||||
non_picklable_attrs = HttpRequest.non_picklable_attrs | frozenset(["environ"])
|
||||
meta_non_picklable_attrs = frozenset(["wsgi.errors", "wsgi.input"])
|
||||
|
||||
def __init__(self, environ):
|
||||
script_name = get_script_name(environ)
|
||||
# If PATH_INFO is empty (e.g. accessing the SCRIPT_NAME URL without a
|
||||
|
@ -89,6 +92,13 @@ class WSGIRequest(HttpRequest):
|
|||
self._read_started = False
|
||||
self.resolver_match = None
|
||||
|
||||
def __getstate__(self):
|
||||
state = super().__getstate__()
|
||||
for attr in self.meta_non_picklable_attrs:
|
||||
if attr in state["META"]:
|
||||
del state["META"][attr]
|
||||
return state
|
||||
|
||||
def _get_scheme(self):
|
||||
return self.environ.get("wsgi.url_scheme")
|
||||
|
||||
|
|
|
@ -51,6 +51,8 @@ class HttpRequest:
|
|||
_encoding = None
|
||||
_upload_handlers = []
|
||||
|
||||
non_picklable_attrs = frozenset(["resolver_match", "_stream"])
|
||||
|
||||
def __init__(self):
|
||||
# WARNING: The `WSGIRequest` subclass doesn't call `super`.
|
||||
# Any variable assignment made here should also happen in
|
||||
|
@ -78,6 +80,21 @@ class HttpRequest:
|
|||
self.get_full_path(),
|
||||
)
|
||||
|
||||
def __getstate__(self):
|
||||
obj_dict = self.__dict__.copy()
|
||||
for attr in self.non_picklable_attrs:
|
||||
if attr in obj_dict:
|
||||
del obj_dict[attr]
|
||||
return obj_dict
|
||||
|
||||
def __deepcopy__(self, memo):
|
||||
obj = copy.copy(self)
|
||||
for attr in self.non_picklable_attrs:
|
||||
if hasattr(self, attr):
|
||||
setattr(obj, attr, copy.deepcopy(getattr(self, attr), memo))
|
||||
memo[id(self)] = obj
|
||||
return obj
|
||||
|
||||
@cached_property
|
||||
def headers(self):
|
||||
return HttpHeaders(self.META)
|
||||
|
|
|
@ -370,12 +370,10 @@ class HttpResponse(HttpResponseBase):
|
|||
[
|
||||
"resolver_match",
|
||||
# Non-picklable attributes added by test clients.
|
||||
"asgi_request",
|
||||
"client",
|
||||
"context",
|
||||
"json",
|
||||
"templates",
|
||||
"wsgi_request",
|
||||
]
|
||||
)
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import pickle
|
||||
from io import BytesIO
|
||||
from itertools import chain
|
||||
from urllib.parse import urlencode
|
||||
|
@ -669,6 +670,20 @@ class RequestsTests(SimpleTestCase):
|
|||
with self.assertRaises(UnreadablePostError):
|
||||
request.FILES
|
||||
|
||||
def test_pickling_request(self):
|
||||
request = HttpRequest()
|
||||
request.method = "GET"
|
||||
request.path = "/testpath/"
|
||||
request.META = {
|
||||
"QUERY_STRING": ";some=query&+query=string",
|
||||
"SERVER_NAME": "example.com",
|
||||
"SERVER_PORT": 80,
|
||||
}
|
||||
request.COOKIES = {"post-key": "post-value"}
|
||||
dump = pickle.dumps(request)
|
||||
request_from_pickle = pickle.loads(dump)
|
||||
self.assertEqual(repr(request), repr(request_from_pickle))
|
||||
|
||||
|
||||
class HostValidationTests(SimpleTestCase):
|
||||
poisoned_hosts = [
|
||||
|
|
Loading…
Reference in New Issue