Fixes to bring back the the "Expect: 100-continue" functionality. If the
authorDaniel Stenberg <daniel@haxx.se>
Mon, 24 Feb 2003 16:53:53 +0000 (16:53 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 24 Feb 2003 16:53:53 +0000 (16:53 +0000)
header is used, we must wait for a 100-code (or timeout), before we send the
data. The timeout is merely 1000 ms at this point. We may have reason to set
a longer timeout in the future.

lib/http.c
lib/transfer.c
lib/urldata.h

index 03f7b91d64a51fea58d2773d87a3f7898f1daf23..699df536178a67c1c875095b4cebcb518a1a2456 100644 (file)
@@ -232,7 +232,7 @@ CURLcode add_buffer_send(send_buffer *in,
       
       return CURLE_OK;
     }
-
+    http->sending = HTTPSEND_BODY;
     /* the full buffer was sent, clean up and return */
   }
   if(in->buffer)
index 929d6ab25d54b27d9bf689c30388be3ca2adcf81..0d1d563bd053044d27c668cee0bc568df3f7a4e4 100644 (file)
 #define min(a, b)   ((a) < (b) ? (a) : (b))
 #endif
 
+#define CURL_TIMEOUT_EXPECT_100 1000 /* counting ms here */
+
 enum {
   KEEP_NONE,
   KEEP_READ,
@@ -248,8 +250,12 @@ CURLcode Curl_readwrite(struct connectdata *conn,
         if(result>0)
           return result;
 
-        if ((k->bytecount == 0) && (k->writebytecount == 0))
+        if ((k->bytecount == 0) && (k->writebytecount == 0)) {
           Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+          if(k->wait100_after_headers)
+            /* set time stamp to compare with when waiting for the 100 */
+            k->start100 = Curl_tvnow();
+        }
 
         didwhat |= KEEP_READ;
 
@@ -382,9 +388,9 @@ CURLcode Curl_readwrite(struct connectdata *conn,
 
               if(100 == k->httpcode) {
                 /*
-                 * we have made a HTTP PUT or POST and this is 1.1-lingo
+                 * We have made a HTTP PUT or POST and this is 1.1-lingo
                  * that tells us that the server is OK with this and ready
-                 * to receive our stuff.
+                 * to receive the data.
                  * However, we'll get more headers now so we must get
                  * back into the header-parsing state!
                  */
@@ -954,8 +960,27 @@ CURLcode Curl_readwrite(struct connectdata *conn,
           /* init the "upload from here" pointer */
           conn->upload_fromhere = k->uploadbuf;
 
-          if(!k->upload_done)
+          if(!k->upload_done) {
+            /* HTTP pollution, this should be written nicer to become more
+               protocol agnostic. */
+
+            if(k->wait100_after_headers &&
+               (conn->proto.http->sending == HTTPSEND_BODY)) {
+              /* If this call is to send body data, we must take some action:
+                 We have sent off the full HTTP 1.1 request, and we shall now
+                 go into the Expect: 100 state and await such a header */
+              k->wait100_after_headers = FALSE; /* headers sent */
+              k->write_after_100_header = TRUE; /* wait for the header */
+              FD_ZERO (&k->writefd);            /* clear it */
+              k->wkeepfd = k->writefd;          /* set the keeper variable */
+              k->keepon &= ~KEEP_WRITE;         /* disable writing */
+              k->start100 = Curl_tvnow();       /* timeout count starts now */
+              didwhat &= ~KEEP_WRITE;  /* we didn't write anything actually */
+              break;
+            }
+
             nread = fillbuffer(conn, BUFSIZE);
+          }
           else
             nread = 0; /* we're done uploading/reading */
 
@@ -1054,6 +1079,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
 
   } while(0); /* just to break out from! */
 
+  k->now = Curl_tvnow();
   if(didwhat) {
     /* Update read/write counters */
     if(conn->bytecountp)
@@ -1067,14 +1093,27 @@ CURLcode Curl_readwrite(struct connectdata *conn,
       /* This should allow some time for the header to arrive, but only a
          very short time as otherwise it'll be too much wasted times too
          often. */
-      k->write_after_100_header = FALSE;
-      FD_SET (conn->writesockfd, &k->writefd); /* write socket */
-      k->keepon |= KEEP_WRITE;
-      k->wkeepfd = k->writefd;
+
+      /* Quoting RFC2616, section "8.2.3 Use of the 100 (Continue) Status":
+         
+      Therefore, when a client sends this header field to an origin server
+      (possibly via a proxy) from which it has never seen a 100 (Continue)
+      status, the client SHOULD NOT wait for an indefinite period before
+      sending the request body.
+
+      */
+
+      int ms = Curl_tvdiff(k->now, k->start100);
+      if(ms > CURL_TIMEOUT_EXPECT_100) {
+        /* we've waited long enough, continue anyway */
+        k->write_after_100_header = FALSE;
+        FD_SET (conn->writesockfd, &k->writefd); /* write socket */
+        k->keepon |= KEEP_WRITE;
+        k->wkeepfd = k->writefd;
+      }
     }    
   }
 
-  k->now = Curl_tvnow();
   if(Curl_pgrsUpdate(conn))
     result = CURLE_ABORTED_BY_CALLBACK;
   else
@@ -1160,10 +1199,26 @@ CURLcode Curl_readwrite_init(struct connectdata *conn)
 
     FD_ZERO (&k->writefd);              /* clear it */
     if(conn->writesockfd != -1) {
-      if (data->set.expect100header)
+      /* HTTP 1.1 magic:
+
+         Even if we require a 100-return code before uploading data, we might
+         need to write data before that since the REQUEST may not have been
+         finished sent off just yet.
+
+         Thus, we must check if the request has been sent before we set the
+         state info where we wait for the 100-return code
+      */
+      if (data->set.expect100header &&
+          (conn->proto.http->sending == HTTPSEND_BODY)) {
         /* wait with write until we either got 100-continue or a timeout */
         k->write_after_100_header = TRUE;
+        k->start100 = k->start;
+      }
       else {
+        if(data->set.expect100header)
+          /* when we've sent off the rest of the headers, we must await a
+             100-continue */
+          k->wait100_after_headers = TRUE;
         FD_SET (conn->writesockfd, &k->writefd); /* write socket */
         k->keepon |= KEEP_WRITE;
       }
index bd0c8002478369b71de2c0379228b19e2ed928e5..2b924ae171b90ee7b32555694ae931804eed85e1 100644 (file)
@@ -281,9 +281,13 @@ struct Curl_transfer_keeper {
                                    Content-Range: header */
   int httpcode;                        /* error code from the 'HTTP/1.? XXX' line */
   int httpversion;             /* the HTTP version*10 */
-  bool write_after_100_header;  /* should we enable the write after
-                                   we received a 100-continue/timeout
-                                   or directly */
+  struct timeval start100;      /* time stamp to wait for the 100 code from */
+  bool write_after_100_header;  /* TRUE = we enable the write after we
+                                   received a 100-continue/timeout or
+                                   FALSE = directly */
+  bool wait100_after_headers;   /* TRUE = after the request-headers have been
+                                   sent off properly, we go into the wait100
+                                   state, FALSE = don't */
   int content_encoding;        /* What content encoding. sec 3.5, RFC2616. */
 
 #define IDENTITY 0             /* No encoding */