Avoid abandoned client connections after url_rewriter redirects CONNECT. clientProcessRequest() assumed that a CONNECT request is always tunneled and sets flags.readMore to false. However, if url_rewriter redirects the CONNECTing user, Squid responds with a redirect message and does not tunnel. In that case, we do want to read more. Otherwise, keepaliveNextRequest() will declare the connection abandoned and the connection descriptor will "leak" until the connection lifetime expires, even if the client disconnects immediately after receiving the redirect response. The fix delays setting flags.readMore to false until we are about to call tunnelStart(). The effect on CONNECT authentication (another case where CONNECT is not tunneled) is untested, but I hope that code continues to work because it should be OK with reading more requests on the [being] authenticated connection. === modified file 'src/client_side.cc' --- src/client_side.cc 2012-11-06 12:45:17 +0000 +++ src/client_side.cc 2013-01-16 16:59:29 +0000 @@ -2629,41 +2629,41 @@ const bool supportedExpect = (expect.caseCmp("100-continue") == 0); if (!supportedExpect) { clientStreamNode *node = context->getClientReplyContext(); clientReplyContext *repContext = dynamic_cast(node->data.getRaw()); assert (repContext); repContext->setReplyToError(ERR_INVALID_REQ, HTTP_EXPECTATION_FAILED, request->method, http->uri, conn->clientConnection->remote, request, NULL, NULL); assert(context->http->out.offset == 0); context->pullData(); conn->flags.readMore = false; goto finish; } } http->request = HTTPMSGLOCK(request); clientSetKeepaliveFlag(http); // Let tunneling code be fully responsible for CONNECT requests if (http->request->method == METHOD_CONNECT) { context->mayUseConnection(true); - conn->flags.readMore = false; + // "may" in "mayUse" is meaningful here: no tunnel if Squid redirects! } /* Do we expect a request-body? */ expectBody = chunked || request->content_length > 0; if (!context->mayUseConnection() && expectBody) { request->body_pipe = conn->expectRequestBody( chunked ? -1 : request->content_length); // consume header early so that body pipe gets just the body connNoteUseOfBuffer(conn, http->req_sz); notedUseOfBuffer = true; /* Is it too large? */ if (!chunked && // if chunked, we will check as we accumulate clientIsRequestBodyTooLargeForPolicy(request->content_length)) { clientStreamNode *node = context->getClientReplyContext(); clientReplyContext *repContext = dynamic_cast(node->data.getRaw()); assert (repContext); repContext->setReplyToError(ERR_TOO_BIG, HTTP_REQUEST_ENTITY_TOO_LARGE, METHOD_NONE, NULL, @@ -3873,40 +3873,41 @@ bool ConnStateData::transparent() const { return clientConnection != NULL && (clientConnection->flags & (COMM_TRANSPARENT|COMM_INTERCEPTION)); } bool ConnStateData::reading() const { return reader != NULL; } void ConnStateData::stopReading() { if (reading()) { comm_read_cancel(clientConnection->fd, reader); reader = NULL; } + flags.readMore = false; } BodyPipe::Pointer ConnStateData::expectRequestBody(int64_t size) { bodyPipe = new BodyPipe(this); if (size >= 0) bodyPipe->setBodySize(size); else startDechunkingRequest(); return bodyPipe; } int64_t ConnStateData::mayNeedToReadMoreBody() const { if (!bodyPipe) return 0; // request without a body or read/produced all body bytes