Skip to content

Commit e5639b2

Browse files
committed
Add store_request_data setting to explicitly store request data
1 parent 91a41bb commit e5639b2

File tree

4 files changed

+48
-5
lines changed

4 files changed

+48
-5
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ Hasan Ramezani <[email protected]>
1414
Felix Yan <[email protected]>
1515
Henri Hulski <[email protected]>
1616
Theodore Ni
17+
Alissa Gerhard <[email protected]>

README.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,13 @@ poking around in the code itself.
9191
* ``content`` - content of next response (str, bytes, or iterable of either)
9292
* ``headers`` - response headers (dict)
9393
* ``chunked`` - whether to chunk-encode the response (enumeration)
94+
* ``store_request_data`` - whether to store request data for later use
9495

9596
Once these attributes are set, all subsequent requests will be answered with
9697
these values until they are changed or the server is stopped. A more
9798
convenient way to change these is ::
9899

99-
httpserver.serve_content(content=None, code=200, headers=None, chunked=pytest_localserver.http.Chunked.NO)
100+
httpserver.serve_content(content=None, code=200, headers=None, chunked=pytest_localserver.http.Chunked.NO, store_request_data=True)
100101

101102
The ``chunked`` attribute or parameter can be set to
102103

@@ -108,6 +109,11 @@ poking around in the code itself.
108109
If chunk encoding is applied, each str or bytes in ``content`` becomes one
109110
chunk in the response.
110111

112+
You can use ``store_request_data=False`` to disable loading the request data into
113+
memory. This will make it impossible to check the request data using
114+
``httpserver.requests[index].data`` but may make sense when posting a larger amount of
115+
data and you don't need to check this.
116+
111117
The server address can be found in property
112118

113119
* ``url``

pytest_localserver/http.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,18 @@ def __init__(self, host="127.0.0.1", port=0, ssl_context=None):
8989
self.compress = None
9090
self.requests = []
9191
self.chunked = Chunked.NO
92+
self.store_request_data = False
9293

9394
def __call__(self, environ, start_response):
9495
"""
9596
This is the WSGI application.
9697
"""
9798
request = Request(environ)
99+
100+
if self.store_request_data:
101+
# need to invoke this method to cache the data
102+
request.get_data(cache=True)
103+
98104
self.requests.append(request)
99105
if (
100106
request.content_type == "application/x-www-form-urlencoded"
@@ -129,7 +135,7 @@ def __call__(self, environ, start_response):
129135

130136
return response(environ, start_response)
131137

132-
def serve_content(self, content, code=200, headers=None, chunked=Chunked.NO):
138+
def serve_content(self, content, code=200, headers=None, chunked=Chunked.NO, store_request_data=True):
133139
"""
134140
Serves string content (with specified HTTP error code) as response to
135141
all subsequent request.
@@ -138,6 +144,7 @@ def serve_content(self, content, code=200, headers=None, chunked=Chunked.NO):
138144
:param code: HTTP status code
139145
:param headers: HTTP headers to be returned
140146
:param chunked: whether to apply chunked transfer encoding to the content
147+
:param store_request_data: whether to store data sent as request payload.
141148
"""
142149
if not isinstance(content, (str, bytes, list, tuple)):
143150
# If content is an iterable which is not known to be a string,
@@ -153,6 +160,7 @@ def serve_content(self, content, code=200, headers=None, chunked=Chunked.NO):
153160
self.content = content
154161
self.code = code
155162
self.chunked = chunked
163+
self.store_request_data = store_request_data
156164
if headers:
157165
self.headers = Headers(headers)
158166

tests/test_http.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import pytest
77
import requests
8+
from werkzeug.exceptions import ClientDisconnected
89

910
from pytest_localserver import http
1011
from pytest_localserver import plugin
@@ -90,6 +91,29 @@ def test_HEAD_request(httpserver):
9091
# assert resp.status_code == 200
9192

9293

94+
def test_POST_request_no_store_data(httpserver):
95+
headers = {"Content-type": "text/plain"}
96+
httpserver.serve_content("TEST!", store_request_data=False)
97+
requests.post(httpserver.url, data=b"testdata", headers=headers)
98+
99+
request = httpserver.requests[-1]
100+
request.input_stream.close()
101+
102+
with pytest.raises(ClientDisconnected):
103+
request.data
104+
105+
106+
def test_POST_request_store_data(httpserver):
107+
headers = {"Content-type": "text/plain"}
108+
httpserver.serve_content("TEST!", store_request_data=True)
109+
requests.post(httpserver.url, data=b"testdata", headers=headers)
110+
111+
request = httpserver.requests[-1]
112+
request.input_stream.close()
113+
114+
assert httpserver.requests[-1].data == b"testdata"
115+
116+
93117
@pytest.mark.parametrize("chunked_flag", [http.Chunked.YES, http.Chunked.AUTO, http.Chunked.NO])
94118
def test_chunked_attribute_without_header(httpserver, chunked_flag):
95119
"""
@@ -274,19 +298,23 @@ def test_GET_request_chunked_no_content_length(httpserver, chunked_flag):
274298
def test_httpserver_init_failure_no_stderr_during_cleanup(tmp_path):
275299
"""
276300
Test that, when the server encounters an error during __init__, its cleanup
277-
does not raise an AttributeError in its __del__ method, which would emit a
301+
does not raise an AttributeError in its __del__ method, which would emit a
278302
warning onto stderr.
279303
"""
280304

281305
script_path = tmp_path.joinpath("script.py")
282306

283-
script_path.write_text(textwrap.dedent("""
307+
script_path.write_text(
308+
textwrap.dedent(
309+
"""
284310
from pytest_localserver import http
285311
from unittest.mock import patch
286312
287313
with patch("pytest_localserver.http.make_server", side_effect=RuntimeError("init failure")):
288314
server = http.ContentServer()
289-
"""))
315+
"""
316+
)
317+
)
290318

291319
result = subprocess.run([sys.executable, str(script_path)], stderr=subprocess.PIPE)
292320

0 commit comments

Comments
 (0)