fb5d65b
diff --git a/src/client_side.cc b/src/client_side.cc
fb5d65b
index f488fc4..69586df 100644
fb5d65b
--- a/src/client_side.cc
fb5d65b
+++ b/src/client_side.cc
fb5d65b
@@ -932,7 +932,7 @@ ConnStateData::kick()
fb5d65b
      * We are done with the response, and we are either still receiving request
fb5d65b
      * body (early response!) or have already stopped receiving anything.
fb5d65b
      *
fb5d65b
-     * If we are still receiving, then clientParseRequest() below will fail.
fb5d65b
+     * If we are still receiving, then parseRequests() below will fail.
fb5d65b
      * (XXX: but then we will call readNextRequest() which may succeed and
fb5d65b
      * execute a smuggled request as we are not done with the current request).
fb5d65b
      *
fb5d65b
@@ -952,28 +952,12 @@ ConnStateData::kick()
fb5d65b
      * Attempt to parse a request from the request buffer.
fb5d65b
      * If we've been fed a pipelined request it may already
fb5d65b
      * be in our read buffer.
fb5d65b
-     *
fb5d65b
-     \par
fb5d65b
-     * This needs to fall through - if we're unlucky and parse the _last_ request
fb5d65b
-     * from our read buffer we may never re-register for another client read.
fb5d65b
      */
fb5d65b
 
fb5d65b
-    if (clientParseRequests()) {
fb5d65b
-        debugs(33, 3, clientConnection << ": parsed next request from buffer");
fb5d65b
-    }
fb5d65b
+    parseRequests();
fb5d65b
 
fb5d65b
-    /** \par
fb5d65b
-     * Either we need to kick-start another read or, if we have
fb5d65b
-     * a half-closed connection, kill it after the last request.
fb5d65b
-     * This saves waiting for half-closed connections to finished being
fb5d65b
-     * half-closed _AND_ then, sometimes, spending "Timeout" time in
fb5d65b
-     * the keepalive "Waiting for next request" state.
fb5d65b
-     */
fb5d65b
-    if (commIsHalfClosed(clientConnection->fd) && pipeline.empty()) {
fb5d65b
-        debugs(33, 3, "half-closed client with no pending requests, closing");
fb5d65b
-        clientConnection->close();
fb5d65b
+    if (!isOpen())
fb5d65b
         return;
fb5d65b
-    }
fb5d65b
 
fb5d65b
     /** \par
fb5d65b
      * At this point we either have a parsed request (which we've
fb5d65b
@@ -1893,16 +1877,11 @@ ConnStateData::receivedFirstByte()
fb5d65b
     resetReadTimeout(Config.Timeout.request);
fb5d65b
 }
fb5d65b
 
fb5d65b
-/**
fb5d65b
- * Attempt to parse one or more requests from the input buffer.
fb5d65b
- * Returns true after completing parsing of at least one request [header]. That
fb5d65b
- * includes cases where parsing ended with an error (e.g., a huge request).
fb5d65b
- */
fb5d65b
-bool
fb5d65b
-ConnStateData::clientParseRequests()
fb5d65b
+/// Attempt to parse one or more requests from the input buffer.
fb5d65b
+/// May close the connection.
fb5d65b
+void
fb5d65b
+ConnStateData::parseRequests()
fb5d65b
 {
fb5d65b
-    bool parsed_req = false;
fb5d65b
-
fb5d65b
     debugs(33, 5, clientConnection << ": attempting to parse");
fb5d65b
 
fb5d65b
     // Loop while we have read bytes that are not needed for producing the body
fb5d65b
@@ -1947,8 +1926,6 @@ ConnStateData::clientParseRequests()
fb5d65b
 
fb5d65b
             processParsedRequest(context);
fb5d65b
 
fb5d65b
-            parsed_req = true; // XXX: do we really need to parse everything right NOW ?
fb5d65b
-
fb5d65b
             if (context->mayUseConnection()) {
fb5d65b
                 debugs(33, 3, "Not parsing new requests, as this request may need the connection");
fb5d65b
                 break;
fb5d65b
@@ -1961,8 +1938,19 @@ ConnStateData::clientParseRequests()
fb5d65b
         }
fb5d65b
     }
fb5d65b
 
fb5d65b
-    /* XXX where to 'finish' the parsing pass? */
fb5d65b
-    return parsed_req;
fb5d65b
+    debugs(33, 7, "buffered leftovers: " << inBuf.length());
fb5d65b
+
fb5d65b
+    if (isOpen() && commIsHalfClosed(clientConnection->fd)) {
fb5d65b
+        if (pipeline.empty()) {
fb5d65b
+            // we processed what we could parse, and no more data is coming
fb5d65b
+            debugs(33, 5, "closing half-closed without parsed requests: " << clientConnection);
fb5d65b
+            clientConnection->close();
fb5d65b
+        } else {
fb5d65b
+            // we parsed what we could, and no more data is coming
fb5d65b
+            debugs(33, 5, "monitoring half-closed while processing parsed requests: " << clientConnection);
fb5d65b
+            flags.readMore = false; // may already be false
fb5d65b
+        }
fb5d65b
+    }
fb5d65b
 }
fb5d65b
 
fb5d65b
 void
fb5d65b
@@ -1979,18 +1967,7 @@ ConnStateData::afterClientRead()
fb5d65b
     if (pipeline.empty())
fb5d65b
         fd_note(clientConnection->fd, "Reading next request");
fb5d65b
 
fb5d65b
-    if (!clientParseRequests()) {
fb5d65b
-        if (!isOpen())
fb5d65b
-            return;
fb5d65b
-        // We may get here if the client half-closed after sending a partial
fb5d65b
-        // request. See doClientRead() and shouldCloseOnEof().
fb5d65b
-        // XXX: This partially duplicates ConnStateData::kick().
fb5d65b
-        if (pipeline.empty() && commIsHalfClosed(clientConnection->fd)) {
fb5d65b
-            debugs(33, 5, clientConnection << ": half-closed connection, no completed request parsed, connection closing.");
fb5d65b
-            clientConnection->close();
fb5d65b
-            return;
fb5d65b
-        }
fb5d65b
-    }
fb5d65b
+    parseRequests();
fb5d65b
 
fb5d65b
     if (!isOpen())
fb5d65b
         return;
fb5d65b
@@ -3775,7 +3752,7 @@ ConnStateData::notePinnedConnectionBecameIdle(PinnedIdleContext pic)
fb5d65b
     startPinnedConnectionMonitoring();
fb5d65b
 
fb5d65b
     if (pipeline.empty())
fb5d65b
-        kick(); // in case clientParseRequests() was blocked by a busy pic.connection
fb5d65b
+        kick(); // in case parseRequests() was blocked by a busy pic.connection
fb5d65b
 }
fb5d65b
 
fb5d65b
 /// Forward future client requests using the given server connection.
fb5d65b
diff --git a/src/client_side.h b/src/client_side.h
fb5d65b
index 6027b31..60b99b1 100644
fb5d65b
--- a/src/client_side.h
fb5d65b
+++ b/src/client_side.h
fb5d65b
@@ -98,7 +98,6 @@ public:
fb5d65b
     void doneWithControlMsg() override;
fb5d65b
 
fb5d65b
     /// Traffic parsing
fb5d65b
-    bool clientParseRequests();
fb5d65b
     void readNextRequest();
fb5d65b
 
fb5d65b
     /// try to make progress on a transaction or read more I/O
fb5d65b
@@ -443,6 +442,7 @@ private:
fb5d65b
 
fb5d65b
     void checkLogging();
fb5d65b
 
fb5d65b
+    void parseRequests();
fb5d65b
     void clientAfterReadingRequests();
fb5d65b
     bool concurrentRequestQueueFilled() const;
fb5d65b
 
fb5d65b
diff --git a/src/tests/stub_client_side.cc b/src/tests/stub_client_side.cc
fb5d65b
index 8c160e5..f49d5dc 100644
fb5d65b
--- a/src/tests/stub_client_side.cc
fb5d65b
+++ b/src/tests/stub_client_side.cc
fb5d65b
@@ -14,7 +14,7 @@
fb5d65b
 #include "tests/STUB.h"
fb5d65b
 
fb5d65b
 #include "client_side.h"
fb5d65b
-bool ConnStateData::clientParseRequests() STUB_RETVAL(false)
fb5d65b
+void ConnStateData::parseRequests() STUB
fb5d65b
 void ConnStateData::readNextRequest() STUB
fb5d65b
 bool ConnStateData::isOpen() const STUB_RETVAL(false)
fb5d65b
 void ConnStateData::kick() STUB