Index: tests/server/sws.c
===================================================================
RCS file: /cvsroot/curl/curl/tests/server/sws.c,v
retrieving revision 1.108
diff -U 20 -r1.108 sws.c
--- tests/server/sws.c	26 Oct 2007 00:36:36 -0000	1.108
+++ tests/server/sws.c	24 Jan 2008 19:19:02 -0000
@@ -90,40 +90,42 @@
 #define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
 #define RCMD_IDLE      1 /* told to sit idle */
 #define RCMD_STREAM    2 /* told to stream */
 
 struct httprequest {
   char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
   int checkindex; /* where to start checking of the request */
   int offset;     /* size of the incoming request */
   long testno;     /* test number found in the request */
   long partno;     /* part number found in the request */
   int open;       /* keep connection open info, as found in the request */
   bool auth_req;  /* authentication required, don't wait for body unless
                      there's an Authorization header */
   bool auth;      /* Authorization header present in the incoming request */
   size_t cl;      /* Content-Length of the incoming request */
   bool digest;    /* Authorization digest header found */
   bool ntlm;      /* Authorization ntlm header found */
   int pipe;       /* if non-zero, expect this many requests to do a "piped"
                      request/response */
   int rcmd;       /* doing a special command, see defines above */
+  int prot_version; /* HTTP version * 10 */
+  bool pipelining; /* true if request is pipelined */
 };
 
 int ProcessRequest(struct httprequest *req);
 void storerequest(char *reqbuf, ssize_t totalsize);
 
 #define DEFAULT_PORT 8999
 
 #ifndef DEFAULT_LOGFILE
 #define DEFAULT_LOGFILE "log/sws.log"
 #endif
 
 const char *serverlogfile = DEFAULT_LOGFILE;
 
 #define SWSVERSION "cURL test suite HTTP server/0.1"
 
 #define REQUEST_DUMP  "log/server.input"
 #define RESPONSE_DUMP "log/server.response"
 
 /* very-big-path support */
 #define MAXDOCNAMELEN 140000
@@ -198,40 +200,42 @@
   static char request[REQUEST_KEYWORD_SIZE];
   static char doc[MAXDOCNAMELEN];
   char logbuf[256];
   int prot_major, prot_minor;
   char *end;
   int error;
   end = strstr(line, END_OF_HEADERS);
 
   logmsg("ProcessRequest() called");
 
   /* try to figure out the request characteristics as soon as possible, but
      only once! */
   if((req->testno == DOCNUMBER_NOTHING) &&
      sscanf(line, "%" REQBUFSIZ_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
             request,
             doc,
             &prot_major,
             &prot_minor) == 4) {
     char *ptr;
 
+    req->prot_version = prot_major*10 + prot_minor;
+
     /* find the last slash */
     ptr = strrchr(doc, '/');
 
     /* get the number after it */
     if(ptr) {
       FILE *stream;
       char *filename;
 
       if((strlen(doc) + strlen(request)) < 200)
         sprintf(logbuf, "Got request: %s %s HTTP/%d.%d",
                 request, doc, prot_major, prot_minor);
       else
         sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d",
                 prot_major, prot_minor);
       logmsg("%s", logbuf);
 
       if(!strncmp("/verifiedserver", ptr, 15)) {
         logmsg("Are-we-friendly question received");
         req->testno = DOCNUMBER_WERULEZ;
         return 1; /* done */
@@ -298,41 +302,41 @@
           else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
             logmsg("instructed to stream");
             req->rcmd = RCMD_STREAM;
           }
           else if(1 == sscanf(cmd, "pipe: %d", &num)) {
             logmsg("instructed to allow a pipe size %d", num);
             req->pipe = num-1; /* decrease by one since we don't count the
                                   first request in this number */
           }
           free(cmd);
         }
       }
     }
     else {
       if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
                 doc, &prot_major, &prot_minor) == 3) {
         sprintf(logbuf, "Received a CONNECT %s HTTP/%d.%d request",
                 doc, prot_major, prot_minor);
         logmsg("%s", logbuf);
 
-        if(prot_major*10+prot_minor == 10)
+        if(req->prot_version == 10)
           req->open = FALSE; /* HTTP 1.0 closes connection by default */
 
         if(!strncmp(doc, "bad", 3))
           /* if the host name starts with bad, we fake an error here */
           req->testno = DOCNUMBER_BADCONNECT;
         else if(!strncmp(doc, "test", 4)) {
           /* if the host name starts with test, the port number used in the
              CONNECT line will be used as test number! */
           char *portp = strchr(doc, ':');
           if(portp)
             req->testno = atoi(portp+1);
           else
             req->testno = DOCNUMBER_CONNECT;
         }
         else
           req->testno = DOCNUMBER_CONNECT;
       }
       else {
         logmsg("Did not find test number in PATH");
         req->testno = DOCNUMBER_404;
@@ -410,40 +414,53 @@
   else if(!req->ntlm &&
           strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
     /* If the client is passing this type-3 NTLM header */
     req->partno += 1002;
     req->ntlm = TRUE; /* NTLM found */
     logmsg("Received NTLM type-3, sending back data %d", req->partno);
     if(req->cl) {
       logmsg("  Expecting %d POSTed bytes", req->cl);
     }
   }
   else if(!req->ntlm &&
           strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
     /* If the client is passing this type-1 NTLM header */
     req->partno += 1001;
     req->ntlm = TRUE; /* NTLM found */
     logmsg("Received NTLM type-1, sending back data %d", req->partno);
   }
   if(strstr(req->reqbuf, "Connection: close"))
     req->open = FALSE; /* close connection after this request */
 
+  if(!req->pipe &&
+     req->open &&
+     req->prot_version >= 11 &&
+     end &&
+     req->reqbuf + req->offset > end + strlen(END_OF_HEADERS) &&
+     (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
+      !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
+    /* If we have a persistent connection, HTTP version >= 1.1
+       and GET/HEAD request, enable pipelining. */
+    req->checkindex = (end - req->reqbuf) + strlen(END_OF_HEADERS);
+    req->pipelining = TRUE;
+  }
+
   while(req->pipe) {
     /* scan for more header ends within this chunk */
     line = &req->reqbuf[req->checkindex];
     end = strstr(line, END_OF_HEADERS);
     if(!end)
       break;
     req->checkindex += (end - line) + strlen(END_OF_HEADERS);
     req->pipe--;
   }
 
 
   /* If authentication is required and no auth was provided, end now. This
      makes the server NOT wait for PUT/POST data and you can then make the
      test case send a rejection before any such data has been sent. Test case
      154 uses this.*/
   if(req->auth_req && !req->auth)
     return 1;
 
   if(req->cl) {
     if(req->cl <= req->offset - (end - req->reqbuf) - strlen(END_OF_HEADERS))
@@ -495,92 +512,108 @@
   fclose(dump);  /* close it ASAP */
 
   if (writeleft > 0) {
     logmsg("Error writing file %s error: %d %s",
            REQUEST_DUMP, error, strerror(error));
     logmsg("Wrote only (%d bytes) of (%d bytes) request input to %s",
            totalsize-writeleft, totalsize, REQUEST_DUMP);
   }
   else {
     logmsg("Wrote request (%d bytes) input to " REQUEST_DUMP,
            totalsize);
   }
 }
 
 /* return 0 on success, non-zero on failure */
 static int get_request(curl_socket_t sock, struct httprequest *req)
 {
   int fail= FALSE;
   char *reqbuf = req->reqbuf;
 
+  char pipereq[REQBUFSIZ];
+  int pipereq_length;
+  if(req->pipelining) {
+    pipereq_length = req->offset - req->checkindex;
+    memcpy(pipereq, reqbuf + req->checkindex, pipereq_length);
+  }
+  else
+    pipereq_length = 0;
+
   /*** Init the httpreqest structure properly for the upcoming request ***/
   memset(req, 0, sizeof(struct httprequest));
 
   /* here's what should not be 0 from the start */
   req->testno = DOCNUMBER_NOTHING; /* safe default */
   req->open = TRUE; /* connection should remain open and wait for more
                        commands */
   req->pipe = 0;
 
   /*** end of httprequest init ***/
 
   while (req->offset < REQBUFSIZ) {
-    ssize_t got = sread(sock, reqbuf + req->offset, REQBUFSIZ - req->offset);
+    ssize_t got;
+    if(pipereq_length) {
+      memcpy(reqbuf, pipereq, pipereq_length);
+      got = pipereq_length;
+      pipereq_length = 0;
+    }
+    else
+      got = sread(sock, reqbuf + req->offset, REQBUFSIZ - req->offset);
     if (got <= 0) {
       if (got < 0) {
         logmsg("recv() returned error: %d", SOCKERRNO);
         return DOCNUMBER_INTERNAL;
       }
       logmsg("Connection closed by client");
       reqbuf[req->offset]=0;
 
       /* dump the request receivied so far to the external file */
       storerequest(reqbuf, req->offset);
       return DOCNUMBER_INTERNAL;
     }
 
     logmsg("Read %d bytes", got);
 
     req->offset += got;
 
     reqbuf[req->offset] = 0;
 
     if(ProcessRequest(req)) {
       if(req->pipe--) {
         logmsg("Waiting for another piped request");
         continue;
       }
       break;
     }
   }
 
   if (req->offset >= REQBUFSIZ) {
     logmsg("Request buffer overflow, closing connection");
     reqbuf[REQBUFSIZ-1]=0;
     fail = TRUE;
     /* dump the request to an external file anyway */
   }
   else
     reqbuf[req->offset]=0;
 
   /* dump the request to an external file */
-  storerequest(reqbuf, req->offset);
+  storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
 
   return fail; /* success */
 }
 
 /* returns -1 on failure */
 static int send_doc(curl_socket_t sock, struct httprequest *req)
 {
   ssize_t written;
   size_t count;
   const char *buffer;
   char *ptr;
   FILE *stream;
   char *cmd=NULL;
   size_t cmdsize=0;
   FILE *dump;
   int persistant = TRUE;
   size_t responsesize;
   int error;
 
   static char weare[256];
@@ -951,40 +984,42 @@
     else
       /* not a fork, just set rc so the following proceeds nicely */
       rc = 0;
     /* 0 is returned to the child */
     if(0 == rc) {
 #endif
     logmsg("====> Client connect");
 
 #ifdef TCP_NODELAY
     /*
      * Disable the Nagle algorithm to make it easier to send out a large
      * response in many small segments to torture the clients more.
      */
     flag = 1;
     if (setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
                    (void *)&flag, sizeof(flag)) == -1) {
       logmsg("====> TCP_NODELAY failed");
     }
 #endif
 
+  memset(&req, 0, sizeof(req));
+
   do {
       if(get_request(msgsock, &req))
         /* non-zero means error, break out of loop */
         break;
 
       if(prevbounce) {
         /* bounce treatment requested */
         if((req.testno == prevtestno) &&
            (req.partno == prevpartno)) {
           req.partno++;
           logmsg("BOUNCE part number to %ld", req.partno);
         }
       }
 
       send_doc(msgsock, &req);
 
       if((req.testno < 0) && (req.testno != DOCNUMBER_CONNECT)) {
         logmsg("special request received, no persistancy");
         break;
       }
