From 8a47c3fcaa3ab3d3715003fc169e020a25a523a3 Mon Sep 17 00:00:00 2001 From: Brian Neradt Date: Tue, 25 Nov 2025 01:54:26 +0000 Subject: [PATCH] Fix request buffering with post_copy_size=0 When proxy.config.http.request_buffer_enabled is set to 1 with proxy.config.http.post_copy_size set to 0, POST requests were failing. The fix disables request buffering when post_copy_size is 0, treating it as an indication to not buffer POST data. Fixes: #6900 --- src/proxy/http/HttpTransact.cc | 2 +- .../pluginTest/test_hooks/body_buffer.test.py | 89 +++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/proxy/http/HttpTransact.cc b/src/proxy/http/HttpTransact.cc index ae4ebc55745..ef9d5d75cd8 100644 --- a/src/proxy/http/HttpTransact.cc +++ b/src/proxy/http/HttpTransact.cc @@ -1573,7 +1573,7 @@ HttpTransact::HandleRequest(State *s) } } } - if (s->txn_conf->request_buffer_enabled && + if (s->txn_conf->request_buffer_enabled && s->http_config_param->post_copy_size > 0 && s->state_machine->get_ua_txn()->has_request_body(s->hdr_info.request_content_length, s->client_info.transfer_encoding == TransferEncoding_t::CHUNKED)) { TRANSACT_RETURN(StateMachineAction_t::WAIT_FOR_FULL_BODY, nullptr); diff --git a/tests/gold_tests/pluginTest/test_hooks/body_buffer.test.py b/tests/gold_tests/pluginTest/test_hooks/body_buffer.test.py index 07e19343417..096670dccc3 100644 --- a/tests/gold_tests/pluginTest/test_hooks/body_buffer.test.py +++ b/tests/gold_tests/pluginTest/test_hooks/body_buffer.test.py @@ -130,5 +130,94 @@ def run(self): tr.Processes.Default.Streams.stderr = "200.gold" +class ZeroPostCopySizeTest: + ''' + Test for issue #6900: Verify that post_copy_size=0 with request_buffer_enabled=1 + does not cause 403 errors. When post_copy_size is 0, request buffering should be + disabled and POST requests should proceed normally. + ''' + + def __init__(self): + self.setupOriginServer() + self.setupTS() + + def setupOriginServer(self): + self._server = Test.MakeOriginServer("server_zero_copy") + # Empty body POST + request_header_empty = { + "headers": "POST /empty HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "Content-Length: 0\r\n\r\n", + "timestamp": "1469733493.993", + "body": "" + } + response_header_empty = { + "headers": "HTTP/1.1 200 OK\r\n" + "Server: microserver\r\n" + "Content-Length: 2\r\n" + "Connection: close\r\n\r\n", + "timestamp": "1469733493.993", + "body": "OK" + } + self._server.addResponse("sessionlog.json", request_header_empty, response_header_empty) + + # Small body POST + self.small_body = "small" + request_header_small = { + "headers": "POST /small HTTP/1.1\r\n" + "Host: www.example.com\r\n" + f"Content-Length: {len(self.small_body)}\r\n\r\n", + "timestamp": "1469733493.993", + "body": self.small_body + } + response_header_small = { + "headers": "HTTP/1.1 200 OK\r\n" + "Server: microserver\r\n" + "Content-Length: 2\r\n" + "Connection: close\r\n\r\n", + "timestamp": "1469733493.993", + "body": "OK" + } + self._server.addResponse("sessionlog.json", request_header_small, response_header_small) + + def setupTS(self): + # Use default port selection (not select_ports=False) to avoid port conflicts + self._ts = Test.MakeATSProcess("ts_zero_copy") + self._ts.Disk.remap_config.AddLine(f'map / http://127.0.0.1:{self._server.Variables.Port}') + # Install the request_buffer plugin to enable per-request buffering + Test.PrepareInstalledPlugin('request_buffer.so', self._ts) + self._ts.Disk.records_config.update( + { + 'proxy.config.diags.debug.enabled': 1, + 'proxy.config.diags.debug.tags': 'http|request_buffer', + # The key configuration: post_copy_size=0 with request_buffer_enabled=1 + # This should NOT cause 403 errors (issue #6900 fix) + # The plugin will enable buffering per-request, but with post_copy_size=0 + # buffering should be disabled and the request should proceed normally + 'proxy.config.http.post_copy_size': 0, + 'proxy.config.http.request_buffer_enabled': 1, + }) + + def run(self): + tr = Test.AddTestRun() + # Send POST with empty body and small body. + # These should return 200, not 403, verifying the fix for issue #6900. + tr.MakeCurlCommand( + f'-v -X POST -H "Content-Length: 0" http://127.0.0.1:{self._ts.Variables.port}/empty --next ' + f'-v http://127.0.0.1:{self._ts.Variables.port}/small -d {self.small_body}', + ts=self._ts) + tr.Processes.Default.ReturnCode = 0 + tr.Processes.Default.StartBefore(self._server) + tr.Processes.Default.StartBefore(self._ts) + # Verify we get 200 OK, not 403 Forbidden + tr.Processes.Default.Streams.stderr += Testers.ContainsExpression( + "< HTTP/1.1 200 OK", "Verify POST with empty body returns 200") + tr.Processes.Default.Streams.stderr += Testers.ExcludesExpression("< HTTP/1.1 403", "Verify no 403 Forbidden errors") + + bodyBufferTest = BodyBufferTest("Test request body buffering.") bodyBufferTest.run() + +# Test for issue #6900: post_copy_size=0 should not cause 403 errors +zeroCopySizeTest = ZeroPostCopySizeTest() +zeroCopySizeTest.run()