From 56abeaf105e756f368784f84ed70692473036fc1 Mon Sep 17 00:00:00 2001 From: Benoit Chesneau Date: Fri, 23 Jan 2026 11:00:09 +0100 Subject: [PATCH] fix: unreader.unread() now prepends data to buffer The unread method was incorrectly appending data to the end of the buffer instead of prepending it to the beginning. This caused issues when reading partial data and then unreading it. Closes #2915 Closes #2346 --- gunicorn/http/unreader.py | 4 +++- tests/test_http.py | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/gunicorn/http/unreader.py b/gunicorn/http/unreader.py index 9aadfbcf..1138e02f 100644 --- a/gunicorn/http/unreader.py +++ b/gunicorn/http/unreader.py @@ -49,8 +49,10 @@ class Unreader: return data[:size] def unread(self, data): - self.buf.seek(0, os.SEEK_END) + rest = self.buf.getvalue() + self.buf = io.BytesIO() self.buf.write(data) + self.buf.write(rest) class SocketUnreader(Unreader): diff --git a/tests/test_http.py b/tests/test_http.py index 3aa4808f..94a3a9d3 100644 --- a/tests/test_http.py +++ b/tests/test_http.py @@ -137,6 +137,14 @@ def test_unreader_unread(): assert b'hi there' in unreader.read() +def test_unreader_unread_should_place_data_at_the_beginning_of_the_buffer(): + unreader = IterUnreader([b"abc", b"def"]) + ab = unreader.read(2) + unreader.unread(ab) + + assert unreader.read(None) == b"abc" + + def test_unreader_read_zero_size(): unreader = Unreader() unreader.chunk = mock.MagicMock(side_effect=[b'qwerty', b'asdfgh'])