As I posted on the squid-users list yesterday, it seems that when a
client makes a forbidden request (tested with 403 Denied and 407 Auth
Required), Squid reads the entire request body into the BodyPipe with no
consumer. If the request body is too large (e.g. a large file being
POSTed), the BodyPipe and the receive buffer fill up and Squid logs:
2013/01/17 17:50:50.780 kid1| client_side.cc(2322)
maybeMakeSpaceAvailable: request buffer full:
client_request_buffer_max_size=524288
Squid stops reading from the client and since the client is still
sending, the socket's rx queue grows. Eventually the rx queue is full
and the TCP stack shrinks the TCP window to 0. The client and Squid
both sit there doing nothing with an open socket until the client drops
the connection (which may be some time).
This is reproducible by setting an ACL:
http_access deny all
Then make a large post request via the proxy:
curl --proxy http://yourproxy:3128 -v -F foo=@somefile -H "Expect:"
http://someurl
Where "somefile" is a file of a few megabytes in size.
The attached patch enables autoconsumption of the BodyPipe if access
isn't allowed in order to discard the request body rather than letting
it fill the buffers.
I'm not entirely sure this is the best fix - calling
expectNoForwarding() would seem to be neater, but this also causes the
client-side socket to close, which would break authentication mechanisms
that require the connection to be kept alive. Hopefully someone with a
better understanding of the Squid internals can review the patch and
tell me if it is the right approach?
--
- Steve Hill
Technical Director
Opendium Limited http://www.opendium.com
Direct contacts:
Instant messager: xmpp:[email protected]
Email: [email protected]
Phone: sip:[email protected]
Sales / enquiries contacts:
Email: [email protected]
Phone: +44-844-9791439 / sip:[email protected]
Support contacts:
Email: [email protected]
Phone: +44-844-4844916 / sip:[email protected]
Index: src/client_side.h
===================================================================
--- src/client_side.h (revision 167)
+++ src/client_side.h (working copy)
@@ -273,6 +273,7 @@
BodyPipe::Pointer expectRequestBody(int64_t size);
virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer);
+ virtual void forwardToBitbucket(void);
virtual void noteBodyConsumerAborted(BodyPipe::Pointer);
bool handleReadData(char *buf, size_t size);
Index: src/client_side.cc
===================================================================
--- src/client_side.cc (revision 167)
+++ src/client_side.cc (working copy)
@@ -3055,6 +3055,14 @@
}
void
+ConnStateData::forwardToBitbucket(void)
+{
+ // request reader may get stuck waiting for space if nobody consumes body
+ if (bodyPipe != NULL)
+ bodyPipe->enableAutoConsumption();
+}
+
+void
ConnStateData::noteBodyConsumerAborted(BodyPipe::Pointer )
{
// request reader may get stuck waiting for space if nobody consumes body
Index: src/client_side_request.cc
===================================================================
--- src/client_side_request.cc (revision 167)
+++ src/client_side_request.cc (working copy)
@@ -829,6 +829,7 @@
NULL);
#endif
http->getConn()->flags.readMore = true; // resume any pipeline reads.
+ http->getConn()->forwardToBitbucket();
node = (clientStreamNode *)http->client_stream.tail->data;
clientStreamRead(node, http, node->readBuffer);
return;