]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Author: Christos Tsantilas <chtsanti@users.sourceforge.net>
authorrousskov <>
Tue, 24 Jul 2007 01:58:46 +0000 (01:58 +0000)
committerrousskov <>
Tue, 24 Jul 2007 01:58:46 +0000 (01:58 +0000)
Bug #1971 fix, part 2, take B, v4: Polish HttpStateData::readReply() layout
to weed out bugs.

Moved processReplyHeader post-processing from readReply into newly added
continueAfterParsingHeader() method. Either deleted unreachable code in
readReply or re-arranged it to become reachable in continueAfterParsingHeader.

Start ICAP ACL checks only after the response header has been successfully
processed. The old code would start ICAP ACL checks for transactions that
would be aborted immediately after the check is scheduled.

Set eof when we read zero bytes. Doing so as a stand-alone step and referring
to 'eof' member from there on makes readReply code a little more clear.

Set reply member when HTTP header parser fails in processReplyHeader() so that
the caller has more information about the parsing error.

persistentConnStatus() now returns COMPLETE_NONPERSISTENT_MSG if we reach eof.
The old code would just call serverComplete() in readReply() instead, making
it a yet another special case there.

We are not done with ICAP if an ICAP ACL check is pending. This change
affects FTP as well and allows us to process more errors with ICAP.

Commented that persistentConnStatus() may be called for ICAP-adapted responses
that do not have a notion of a persistent connection. Why does this work?

These changes were inspired by Christos Tsantilas comments and patches but
the new bugs are mine.

src/Server.cc
src/http.cc
src/http.h

index 0c19df7769b441afec496bea2f9b89ea0c65fdf4..291720922095ed5752a8991439ca6dbe7a834bbc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * $Id: Server.cc,v 1.16 2007/07/23 16:55:31 rousskov Exp $
+ * $Id: Server.cc,v 1.17 2007/07/23 19:58:46 rousskov Exp $
  *
  * DEBUG:
  * AUTHOR: Duane Wessels
@@ -361,7 +361,7 @@ ServerStateData::startIcap(ICAPServiceRep::Pointer service, HttpRequest *cause)
 // properly cleans up ICAP-related state
 // may be called multiple times
 void ServerStateData::cleanIcap() {
-    debugs(11,5, HERE << "cleaning ICAP");
+    debugs(11,5, HERE << "cleaning ICAP; ACL: " << icapAccessCheckPending);
 
     if (virginBodyDestination != NULL)
         stopProducingFor(virginBodyDestination, false);
@@ -371,12 +371,14 @@ void ServerStateData::cleanIcap() {
     if (adaptedBodySource != NULL)
         stopConsumingFrom(adaptedBodySource);
 
-    assert(doneWithIcap()); // make sure the two methods are in sync
+    if (!icapAccessCheckPending) // we cannot cancel a pending callback
+        assert(doneWithIcap()); // make sure the two methods are in sync
 }
 
 bool
 ServerStateData::doneWithIcap() const {
-    return !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource;
+    return !icapAccessCheckPending &&
+        !virginBodyDestination && !adaptedHeadSource && !adaptedBodySource;
 }
 
 // can supply more virgin response body data
index ace040356e42fff056555911bdaac7e021c2c122..74539ac5c9344d0ca3e4f03e671ea154935fe5ae 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: http.cc,v 1.533 2007/07/23 16:55:31 rousskov Exp $
+ * $Id: http.cc,v 1.534 2007/07/23 19:58:46 rousskov Exp $
  *
  * DEBUG: section 11    Hypertext Transfer Protocol (HTTP)
  * AUTHOR: Harvest Derived
@@ -646,14 +646,6 @@ httpMakeVaryMark(HttpRequest * request, HttpReply const * reply)
     return vstr.buf();
 }
 
-void
-HttpStateData::failReply(HttpReply *reply, http_status const & status)
-{
-    reply->sline.version = HttpVersion(1, 0);
-    reply->sline.status = status;
-    entry->replaceHttpReply(reply);
-}
-
 void
 HttpStateData::keepaliveAccounting(HttpReply *reply)
 {
@@ -721,8 +713,10 @@ HttpStateData::processReplyHeader()
         if (!parsed && error > 0) { // unrecoverable parsing error
              debugs(11, 3, "processReplyHeader: Non-HTTP-compliant header: '" <<  readBuf->content() << "'");
              flags.headers_parsed = 1;
-             // negated result yields http_status
-             failReply (newrep, error);
+          newrep->sline.version = HttpVersion(1, 0);
+          newrep->sline.status = error;
+          reply = HTTPMSGLOCK(newrep);
+          entry->replaceHttpReply(reply);
              ctx_exit(ctx);
              return;
         }
@@ -754,8 +748,6 @@ HttpStateData::processReplyHeader()
      * Parse the header and remove all referenced headers
      */
 
-    setReply();
-
     ctx_exit(ctx);
 
 }
@@ -890,10 +882,12 @@ HttpStateData::statusIfComplete() const
     return COMPLETE_PERSISTENT_MSG;
 }
 
+// XXX: This is also called for ICAP-adapted responses but they have
+// no notion of "persistent connection"
 HttpStateData::ConnectionStatus
 HttpStateData::persistentConnStatus() const
 {
-    debugs(11, 3, "persistentConnStatus: FD " << fd);
+    debugs(11, 3, "persistentConnStatus: FD " << fd << " eof=" << eof);
     debugs(11, 5, "persistentConnStatus: content_length=" << reply->content_length);
 
     /* If we haven't seen the end of reply headers, we are not done */
@@ -902,6 +896,9 @@ HttpStateData::persistentConnStatus() const
     if (!flags.headers_parsed)
         return INCOMPLETE_MSG;
 
+    if (eof) // already reached EOF
+        return COMPLETE_NONPERSISTENT_MSG;
+
     const int clen = reply->bodySize(request->method);
 
     debugs(11, 5, "persistentConnStatus: clen=" << clen);
@@ -1022,68 +1019,74 @@ HttpStateData::readReply (size_t len, comm_err_t flag, int xerrno)
 
 #endif
 
-    if (len == 0 && !flags.headers_parsed) {
-        fwd->fail(errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_BAD_GATEWAY, fwd->request));
+    if (len == 0) { // reached EOF?
         eof = 1;
         flags.do_next_read = 0;
-        comm_close(fd);
-    } else if (len == 0) {
-        /* Connection closed; retrieval done. */
-        eof = 1;
+    }
 
-        if (!flags.headers_parsed) {
-            /*
-            * When we called processReplyHeader() before, we
-            * didn't find the end of headers, but now we are
-            * definately at EOF, so we want to process the reply
-            * headers.
-             */
-            PROF_start(HttpStateData_processReplyHeader);
-            processReplyHeader();
-            PROF_stop(HttpStateData_processReplyHeader);
-        } else if (getReply()->sline.status == HTTP_INVALID_HEADER && HttpVersion(0,9) != getReply()->sline.version) {
-            fwd->fail(errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, fwd->request));
-            flags.do_next_read = 0;
-        } else {
-            if (entry->mem_obj->getReply()->sline.status == HTTP_HEADER_TOO_LARGE) {
-                entry->reset();
-                fwd->fail( errorCon(ERR_TOO_BIG, HTTP_BAD_GATEWAY, fwd->request));
+    if (!flags.headers_parsed) { // have not parsed headers yet?
+        PROF_start(HttpStateData_processReplyHeader);
+        processReplyHeader();
+        PROF_stop(HttpStateData_processReplyHeader);
+
+        if (!continueAfterParsingHeader()) // parsing error or need more data
+            return; // TODO: send errors to ICAP
+
+        setReply();
+    }
+
+    // kick more reads if needed and/or process the response body, if any
+    PROF_start(HttpStateData_processReplyBody);
+    processReplyBody(); // may call serverComplete()
+    PROF_stop(HttpStateData_processReplyBody);
+}
+
+// Checks whether we can continue with processing the body or doing ICAP.
+// Returns false if we cannot (e.g., due to lack of headers or errors).
+bool
+HttpStateData::continueAfterParsingHeader()
+{
+    if (!flags.headers_parsed && !eof) { // need more and may get more
+        debugs(11, 9, HERE << "needs more at " << readBuf->contentSize());
+        flags.do_next_read = 1;
+        maybeReadVirginBody(); // schedules all kinds of reads; TODO: rename
+        return false; // wait for more data
+    }
+
+    /* we are done with parsing, now check for errors */
+
+    err_type error = ERR_NONE;
+
+    if (flags.headers_parsed) { // parsed headers, possibly with errors
+        // check for header parsing errors
+        if (reply != NULL) {
+            const http_status s = getReply()->sline.status;
+            const HttpVersion &v = getReply()->sline.version;
+            if (s == HTTP_INVALID_HEADER && v != HttpVersion(0,9)) {
+                error = ERR_INVALID_RESP;
+            } else
+            if (s == HTTP_HEADER_TOO_LARGE) {
                 fwd->dontRetry(true);
-                flags.do_next_read = 0;
-                comm_close(fd);
+                error = ERR_TOO_BIG;
             } else {
-                serverComplete();
+                return true; // done parsing, got reply, and no error
             }
+        } else {
+            // parsed headers but got no reply
+            error = ERR_INVALID_RESP;
         }
     } else {
-        if (!flags.headers_parsed) {
-            PROF_start(HttpStateData_processReplyHeader);
-            processReplyHeader();
-            PROF_stop(HttpStateData_processReplyHeader);
-
-            if (flags.headers_parsed) {
-                bool fail = reply == NULL;
-
-                if (!fail) {
-                    http_status s = getReply()->sline.status;
-                    HttpVersion httpver = getReply()->sline.version;
-                    fail = s == HTTP_INVALID_HEADER && httpver != HttpVersion(0,9);
-                }
-
-                if (fail) {
-                    entry->reset();
-                    fwd->fail( errorCon(ERR_INVALID_RESP, HTTP_BAD_GATEWAY, fwd->request));
-                    comm_close(fd);
-                    return;
-                }
-
-            }
-        }
-
-        PROF_start(HttpStateData_processReplyBody);
-        processReplyBody();
-        PROF_stop(HttpStateData_processReplyBody);
+        assert(eof);
+        error = readBuf->hasContent() ?
+            ERR_INVALID_RESP : ERR_ZERO_SIZE_OBJECT;
     }
+
+    assert(error != ERR_NONE);
+    entry->reset();
+    fwd->fail(errorCon(error, HTTP_BAD_GATEWAY, fwd->request));
+    flags.do_next_read = 0;
+    comm_close(fd);
+    return false; // quit on error
 }
 
 /*
index 6edca83d1841f825b7c6425b7ce631293ed95eb2..34115fa3542d20db778bbcf126c65dccddb2491e 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: http.h,v 1.30 2007/07/19 12:07:41 hno Exp $
+ * $Id: http.h,v 1.31 2007/07/23 19:58:46 rousskov Exp $
  *
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
@@ -94,10 +94,11 @@ private:
     };
     ConnectionStatus statusIfComplete() const;
     ConnectionStatus persistentConnStatus() const;
-    void failReply (HttpReply *reply, http_status const &status);
     void keepaliveAccounting(HttpReply *);
     void checkDateSkew(HttpReply *);
 
+    bool continueAfterParsingHeader();
+
     virtual void haveParsedReplyHeaders();
     virtual void closeServer(); // end communication with the server
     virtual bool doneWithServer() const; // did we end communication?