Adrian Schuur added trailer support in the chunked encoding stream. The
authorDaniel Stenberg <daniel@haxx.se>
Tue, 12 Jul 2005 18:15:34 +0000 (18:15 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 12 Jul 2005 18:15:34 +0000 (18:15 +0000)
trailer is then sent to the normal header callback/stream.

CHANGES
RELEASE-NOTES
lib/http_chunks.c
lib/http_chunks.h
lib/transfer.c
lib/url.c
lib/urldata.h
tests/data/Makefile.am
tests/data/test266 [new file with mode: 0644]

diff --git a/CHANGES b/CHANGES
index 434266c..d4d788f 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -7,6 +7,12 @@
                                   Changelog
 
 
+Daniel (12 July 2005)
+- Adrian Schuur added trailer support in the chunked encoding stream. The
+  trailer is then sent to the normal header callback/stream. I wrote up test
+  case 266 to verify the basic functionality. Do note that test case 34
+  contains a flawed chunked encoding stream that still works the same.
+
 Daniel (5 July 2005)
 - Gisle Vanem came up with a nice little work-around for bug #1230118. It
   seems the Windows (MSVC) libc time functions may return data one hour off if
index 45406ba..da40688 100644 (file)
@@ -10,7 +10,8 @@ Curl and libcurl 7.14.1
  Number of contributors:                   437
 
 This release includes the following changes:
+
+ o trailer support for chunked encoded data streams 
  o -x/CURL_PROXY strings may now contain user+password
  o --trace-time now outputs the full microsecond, all 6 digits
 
@@ -41,6 +42,7 @@ This release would not have looked like this without help, code, reports and
 advice from friends like these:
 
  John McGowan, Georg Wicherski, Andres Garcia, Eric Cooper, Todd Kulesza,
- Tupone Alfredo, Gisle Vanem, David Shaw, Andrew Bushnell, Dan Fandrich
+ Tupone Alfredo, Gisle Vanem, David Shaw, Andrew Bushnell, Dan Fandrich,
+ Adrian Schuur
 
         Thanks! (and sorry if I forgot to mention someone)
index f333f95..63e136c 100644 (file)
@@ -153,10 +153,17 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
       if(*datap == '\n') {
         /* we're now expecting data to come, unless size was zero! */
         if(0 == ch->datasize) {
-          ch->state = CHUNK_STOP; /* stop reading! */
-          if(1 == length) {
-            /* This was the final byte, return right now */
-            return CHUNKE_STOP;
+          if (conn->bits.trailerHdrPresent!=TRUE) {
+            /* No Trailer: header found - revert to original Curl processing */
+            ch->state = CHUNK_STOP;
+            if (1 == length) {
+               /* This is the final byte, return right now */
+               return CHUNKE_STOP;
+            }
+          }
+          else {
+            ch->state = CHUNK_TRAILER; /* attempt to read trailers */
+            conn->trlPos=0;
           }
         }
         else
@@ -250,6 +257,64 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
         return CHUNKE_BAD_CHUNK;
       break;
 
+    case CHUNK_TRAILER:
+      /* conn->trailer is assumed to be freed in url.c on a
+         connection basis */
+      if (conn->trlPos >= conn->trlMax) {
+        char *ptr;
+        if(conn->trlMax) {
+          conn->trlMax *= 2;
+          ptr = (char*)realloc(conn->trailer,conn->trlMax);
+        }
+        else {
+          conn->trlMax=128;
+          ptr = (char*)malloc(conn->trlMax);
+        }
+        if(!ptr)
+          return CHUNKE_OUT_OF_MEMORY;
+        conn->trailer = ptr;
+      }
+      conn->trailer[conn->trlPos++]=*datap;
+
+      if(*datap == '\r')
+        ch->state = CHUNK_TRAILER_CR;
+      else {
+        datap++;
+        length--;
+     }
+      break;
+
+    case CHUNK_TRAILER_CR:
+      if(*datap == '\r') {
+        ch->state = CHUNK_TRAILER_POSTCR;
+        datap++;
+        length--;
+      }
+      else
+        return CHUNKE_BAD_CHUNK;
+      break;
+
+    case CHUNK_TRAILER_POSTCR:
+      if (*datap == '\n') {
+        conn->trailer[conn->trlPos++]='\n';
+        conn->trailer[conn->trlPos]=0;
+        if (conn->trlPos==2) {
+          ch->state = CHUNK_STOP;
+          return CHUNKE_STOP;
+        }
+        else {
+          Curl_client_write(conn->data, CLIENTWRITE_HEADER,
+                            conn->trailer, conn->trlPos);
+        }
+        ch->state = CHUNK_TRAILER;
+        conn->trlPos=0;
+        datap++;
+        length--;
+      }
+      else
+        return CHUNKE_BAD_CHUNK;
+      break;
+
     case CHUNK_STOP:
       /* If we arrive here, there is data left in the end of the buffer
          even if there's no more chunks to read */
index c5b7fdf..211818a 100644 (file)
@@ -52,8 +52,8 @@ typedef enum {
   /* POSTCR should get a CR and nothing else, then move to POSTLF */
   CHUNK_POSTCR,
 
-  /* POSTLF should get a LF and nothing else, then move back to HEX as
-     the CRLF combination marks the end of a chunk */
+  /* POSTLF should get a LF and nothing else, then move back to HEX as the
+     CRLF combination marks the end of a chunk */
   CHUNK_POSTLF,
 
   /* This is mainly used to really mark that we're out of the game.
@@ -62,7 +62,22 @@ typedef enum {
      buffer! */
   CHUNK_STOP,
 
+  /* At this point optional trailer headers can be found, unless the next line
+     is CRLF */
+  CHUNK_TRAILER,
+
+  /* A trailer CR has been found - next state is CHUNK_TRAILER_POSTCR.
+     Next char must be a LF */
+  CHUNK_TRAILER_CR,
+
+  /* A trailer LF must be found now, otherwise CHUNKE_BAD_CHUNK will be
+     signalled If this is an empty trailer CHUNKE_STOP will be signalled.
+     Otherwise the trailer will be broadcasted via Curl_client_write() and the
+     next state will be CHUNK_TRAILER */
+  CHUNK_TRAILER_POSTCR,
+
   CHUNK_LAST /* never use */
+
 } ChunkyState;
 
 typedef enum {
@@ -74,6 +89,7 @@ typedef enum {
   CHUNKE_WRITE_ERROR,
   CHUNKE_STATE_ERROR,
   CHUNKE_BAD_ENCODING,
+  CHUNKE_OUT_OF_MEMORY,
   CHUNKE_LAST
 } CHUNKcode;
 
index fdc825b..a2cd391 100644 (file)
@@ -833,6 +833,20 @@ CURLcode Curl_readwrite(struct connectdata *conn,
               /* init our chunky engine */
               Curl_httpchunk_init(conn);
             }
+
+            else if (checkprefix("Trailer:", k->p) ||
+                     checkprefix("Trailers:", k->p)) {
+              /*
+               * This test helps Curl_httpchunk_read() to determine to look
+               * for well formed trailers after the zero chunksize record. In
+               * this case a CRLF is required after the zero chunksize record
+               * when no trailers are sent, or after the last trailer record.
+               *
+               * It seems both Trailer: and Trailers: occur in the wild.
+               */
+              conn->bits.trailerHdrPresent = TRUE;
+            }
+
             else if (checkprefix("Content-Encoding:", k->p) &&
                      data->set.encoding) {
               /*
@@ -1074,6 +1088,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
              * the name says read, this function both reads and writes away
              * the data. The returned 'nread' holds the number of actual
              * data it wrote to the client.  */
+
             CHUNKcode res =
               Curl_httpchunk_read(conn, k->str, nread, &nread);
 
index 469e6b3..9c05c80 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -1496,6 +1496,7 @@ CURLcode Curl_disconnect(struct connectdata *conn)
   Curl_safefree(conn->allocptr.host);
   Curl_safefree(conn->allocptr.cookiehost);
   Curl_safefree(conn->ip_addr_str);
+  Curl_safefree(conn->trailer);
 
   /* possible left-overs from the async name resolvers */
 #if defined(USE_ARES)
index 9bd2459..4200c38 100644 (file)
@@ -421,6 +421,10 @@ struct ConnectBits {
                          LPRT doesn't work we disable it for the forthcoming
                          requests */
   bool netrc;         /* name+password provided by netrc */
+  
+  bool trailerHdrPresent; /* Set when Trailer: header found in HTTP response.
+                             Required to determine whether to look for trailers 
+                             in case of Transfer-Encoding: chunking */ 
 };
 
 struct hostname {
@@ -726,6 +730,12 @@ struct connectdata {
                                      transfer */
 
   enum { NORMAL, SOURCE3RD, TARGET3RD } xfertype;
+
+  /* These three are used for chunked-encoding trailer support */
+  char *trailer; /* allocated buffer to store trailer in */
+  int trlMax;    /* allocated buffer size */
+  int trlPos;    /* index of where to store data */
+
 };
 
 /* The end of connectdata. */
index b32e9ee..a153ae9 100644 (file)
@@ -33,4 +33,4 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46           \
  test237 test238 test239 test243 test245 test246 test247 test248 test249   \
  test250 test251 test252 test253 test254 test255 test521 test522 test523   \
  test256 test257 test258 test259 test260 test261 test262 test263 test264   \
- test265
+ test265 test266
diff --git a/tests/data/test266 b/tests/data/test266
new file mode 100644 (file)
index 0000000..f3c6132
--- /dev/null
@@ -0,0 +1,76 @@
+<info>
+<keywords>
+HTTP
+HTTP GET
+chunked Transfer-Encoding
+</keywords>
+</info>
+#
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 funky chunky!\r
+Server: fakeit/0.9 fakeitbad/1.0\r
+Transfer-Encoding: chunked\r
+Trailer: chunky-trailer\r
+Connection: mooo\r
+\r
+40\r
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\r
+30\r
+bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\r
+21;heresatest=moooo\r
+cccccccccccccccccccccccccccccccc
+\r
+0\r
+chunky-trailer: header data\r
+\r
+</data>
+<datacheck>
+HTTP/1.1 200 funky chunky!\r
+Server: fakeit/0.9 fakeitbad/1.0\r
+Transfer-Encoding: chunked\r
+Trailer: chunky-trailer\r
+Connection: mooo\r
+\r
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccccccccccccc
+</datacheck>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+ <name>
+HTTP GET with chunked Transfer-Encoding and chunked trailer
+ </name>
+ <command>
+http://%HOSTIP:%HTTPPORT/266 -D log/heads266
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET /266 HTTP/1.1\r
+Host: 127.0.0.1:%HTTPPORT\r
+Accept: */*\r
+\r
+</protocol>
+<file name="log/heads266">
+HTTP/1.1 200 funky chunky!\r
+Server: fakeit/0.9 fakeitbad/1.0\r
+Transfer-Encoding: chunked\r
+Trailer: chunky-trailer\r
+Connection: mooo\r
+\r
+chunky-trailer: header data\r
+</file>
+</verify>
+