test 530 is the first ever HTTP pipelining test for libcurl
authorDaniel Stenberg <daniel@haxx.se>
Fri, 8 Sep 2006 11:56:56 +0000 (11:56 +0000)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 8 Sep 2006 11:56:56 +0000 (11:56 +0000)
tests/FILEFORMAT
tests/data/Makefile.am
tests/data/test530 [new file with mode: 0644]
tests/libtest/Makefile.am
tests/libtest/lib530.c [new file with mode: 0644]
tests/server/sws.c

index caaaf19..b403379 100644 (file)
@@ -88,6 +88,8 @@ auth_required - if this is set and a POST/PUT is made without auth, the
                 server will NOT wait for the full request body to get sent
 idle -          do nothing after receiving the request, just "sit idle"
 stream -        continuously send data to the client, never-ending
+pipe: [num] -   tell the server to expect this many HTTP requests before
+                sending back anything, to allow pipelining tests
 </servercmd>
 </reply>
 
index 250084d..4c1bdb1 100644 (file)
@@ -34,5 +34,6 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46           \
  test250 test251 test252 test253 test254 test255 test521 test522 test523   \
  test256 test257 test258 test259 test260 test261 test262 test263 test264   \
  test265 test266 test267 test268 test269 test270 test271 test272 test273   \
- test274 test275 test524 test525 test276 test277 test526 test527 test528
+ test274 test275 test524 test525 test276 test277 test526 test527 test528   \
+ test530
 
diff --git a/tests/data/test530 b/tests/data/test530
new file mode 100644 (file)
index 0000000..671efd8
--- /dev/null
@@ -0,0 +1,78 @@
+<info>
+<keywords>
+HTTP
+Pipelining
+</keywords>
+</info>
+# Server-side
+<reply>
+<servercmd>
+pipe: 4
+</servercmd>
+<data>
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 47
+
+file contents should appear once for each file
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 47
+
+file contents should appear once for each file
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 47
+
+file contents should appear once for each file
+HTTP/1.1 200 OK
+Date: Thu, 09 Nov 2010 14:49:00 GMT
+Server: test-server/fake
+Content-Length: 47
+
+file contents should appear once for each file
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<tool>
+lib530
+</tool>
+ <name>
+HTTP GET using pipelining
+ </name>
+ <command>
+http://%HOSTIP:%HTTPPORT/path/530
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+</strip>
+<protocol>
+GET /path/530 HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+Accept: */*\r
+\r
+GET /path/530 HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+Accept: */*\r
+\r
+GET /path/530 HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+Accept: */*\r
+\r
+GET /path/530 HTTP/1.1\r
+Host: %HOSTIP:%HTTPPORT\r
+Accept: */*\r
+\r
+</protocol>
+</verify>
index 4f721e1..877532d 100644 (file)
@@ -41,7 +41,7 @@ SUPPORTFILES = first.c test.h
 # These are all libcurl test programs
 noinst_PROGRAMS = lib500 lib501 lib502 lib503 lib504 lib505 lib506 lib507 \
   lib508 lib509 lib510 lib511 lib512 lib513 lib514 lib515 lib516 lib517 \
-  lib518 lib519 lib520 lib521 lib523 lib524 lib525 lib526 lib527
+  lib518 lib519 lib520 lib521 lib523 lib524 lib525 lib526 lib527 lib530
 
 lib500_SOURCES = lib500.c $(SUPPORTFILES)
 lib500_LDADD = $(LIBDIR)/libcurl.la
@@ -152,3 +152,7 @@ lib527_CFLAGS = -DLIB527
 lib527_LDADD = $(LIBDIR)/libcurl.la
 lib527_DEPENDENCIES = $(LIBDIR)/libcurl.la
 
+lib530_SOURCES = lib530.c $(SUPPORTFILES)
+lib530_CFLAGS = -DLIB530
+lib530_LDADD = $(LIBDIR)/libcurl.la
+lib530_DEPENDENCIES = $(LIBDIR)/libcurl.la
diff --git a/tests/libtest/lib530.c b/tests/libtest/lib530.c
new file mode 100644 (file)
index 0000000..3b2c950
--- /dev/null
@@ -0,0 +1,120 @@
+/*****************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * $Id$
+ */
+
+/*
+ * This code sets up multiple easy handles that transfer a single file from
+ * the same URL, in a serial manner after each other. Due to the connection
+ * sharing within the multi handle all transfers are performed on the same
+ * persistent connection.
+ *
+ * This source code is used for lib526 _and_ lib527 with only #ifdefs
+ * controlling the small differences. lib526 closes all easy handles after
+ * they all have transfered the file over the single connection, while lib527
+ * closes each easy handle after each single transfer. 526 and 527 use FTP,
+ * while 528 uses the lib526 tool but use HTTP.
+ */
+
+#include "test.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define NUM_HANDLES 4
+
+int test(char *URL)
+{
+  int res = 0;
+  CURL *curl[NUM_HANDLES];
+  int running;
+  char done=FALSE;
+  CURLM *m;
+  int current=0;
+  int i;
+
+  /* In windows, this will init the winsock stuff */
+  curl_global_init(CURL_GLOBAL_ALL);
+
+  m = curl_multi_init();
+
+  /* get NUM_HANDLES easy handles */
+  for(i=0; i < NUM_HANDLES; i++) {
+    curl[i] = curl_easy_init();
+    if(!curl[i])
+      return 100 + i; /* major bad */
+    curl_easy_setopt(curl[i], CURLOPT_URL, URL);
+
+    /* go verbose */
+    curl_easy_setopt(curl[i], CURLOPT_VERBOSE, 1);
+
+    /* include headers */
+    curl_easy_setopt(curl[i], CURLOPT_HEADER, 1);
+
+    res = (int)curl_multi_add_handle(m, curl[i]);
+  }
+
+  curl_multi_setopt(m, CURLMOPT_PIPELINING, 1);
+
+  fprintf(stderr, "Start at URL 0\n");
+
+  while(!done) {
+    fd_set rd, wr, exc;
+    int max_fd;
+    struct timeval interval;
+
+    interval.tv_sec = 1;
+    interval.tv_usec = 0;
+
+    while (res == CURLM_CALL_MULTI_PERFORM) {
+      res = (int)curl_multi_perform(m, &running);
+      if (running <= 0) {
+        done = TRUE; /* bail out */
+        break;
+      }
+    }
+    if(done)
+      break;
+
+    if (res != CURLM_OK) {
+      fprintf(stderr, "not okay???\n");
+      break;
+    }
+
+    FD_ZERO(&rd);
+    FD_ZERO(&wr);
+    FD_ZERO(&exc);
+    max_fd = 0;
+
+    if (curl_multi_fdset(m, &rd, &wr, &exc, &max_fd) != CURLM_OK) {
+      fprintf(stderr, "unexpected failured of fdset.\n");
+      res = 189;
+      break;
+    }
+
+    if (select(max_fd+1, &rd, &wr, &exc, &interval) == -1) {
+      fprintf(stderr, "bad select??\n");
+      res = 195;
+      break;
+    }
+
+    res = CURLM_CALL_MULTI_PERFORM;
+  }
+
+  /* get NUM_HANDLES easy handles */
+  for(i=0; i < NUM_HANDLES; i++) {
+    curl_multi_remove_handle(m, curl[i]);
+    curl_easy_cleanup(curl[i]);
+  }
+
+  curl_multi_cleanup(m);
+
+  curl_global_cleanup();
+  return res;
+}
index d4e6cc9..525163f 100644 (file)
@@ -90,6 +90,7 @@ bool prevbounce;    /* instructs the server to increase the part number for
 
 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 */
@@ -100,7 +101,8 @@ struct httprequest {
   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 */
 };
 
@@ -188,14 +190,16 @@ static void sigpipe_handler(int sig)
 
 int ProcessRequest(struct httprequest *req)
 {
-  char *line=req->reqbuf;
+  char *line=&req->reqbuf[req->checkindex];
   char chunked=FALSE;
   static char request[REQUEST_KEYWORD_SIZE];
   static char doc[MAXDOCNAMELEN];
   char logbuf[256];
   int prot_major, prot_minor;
   char *end;
-  end = strstr(req->reqbuf, END_OF_HEADERS);
+  end = strstr(line, END_OF_HEADERS);
+
+  logmsg("ProcessRequest() called");
 
   /* try to figure out the request characteristics as soon as possible, but
      only once! */
@@ -266,6 +270,7 @@ int ProcessRequest(struct httprequest *req)
       else {
         char *cmd = NULL;
         size_t cmdsize = 0;
+        int num=0;
 
         /* get the custom server control "commands" */
         cmd = (char *)spitout(stream, "reply", "servercmd", &cmdsize);
@@ -287,6 +292,11 @@ int ProcessRequest(struct httprequest *req)
             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);
         }
       }
@@ -323,9 +333,17 @@ int ProcessRequest(struct httprequest *req)
     }
   }
 
-  if(!end)
+  if(!end) {
     /* we don't have a complete request yet! */
+    logmsg("ProcessRequest returned without a complete request");
     return 0;
+  }
+  logmsg("ProcessRequest found a complete request");
+
+  if(req->pipe)
+    /* we do have a full set, advance the checkindex to after the end of the
+       headers, for the pipelining case mostly */
+    req->checkindex += (end - line) + strlen(END_OF_HEADERS);
 
   /* **** Persistancy ****
    *
@@ -402,6 +420,17 @@ int ProcessRequest(struct httprequest *req)
   if(strstr(req->reqbuf, "Connection: close"))
     req->open = FALSE; /* close connection after this request */
 
+  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
@@ -415,6 +444,7 @@ int ProcessRequest(struct httprequest *req)
     else
       return 0; /* not complete yet */
   }
+
   return 1; /* done */
 }
 
@@ -450,6 +480,7 @@ static int get_request(curl_socket_t sock, struct httprequest *req)
   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 ***/
 
@@ -467,12 +498,20 @@ static int get_request(curl_socket_t sock, struct httprequest *req)
       storerequest(reqbuf);
       return DOCNUMBER_INTERNAL;
     }
+
+    logmsg("Read %d bytes", got);
+
     req->offset += got;
 
     reqbuf[req->offset] = 0;
 
-    if(ProcessRequest(req))
+    if(ProcessRequest(req)) {
+      if(req->pipe--) {
+        logmsg("Waiting for another piped request");
+        continue;
+      }
       break;
+    }
   }
 
   if (req->offset >= REQBUFSIZ) {
@@ -764,7 +803,7 @@ int main(int argc, char *argv[])
   }
 
   flag = 1;
-  if (0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, 
+  if (0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
             (void *) &flag, sizeof(flag))) {
     logmsg("setsockopt(SO_REUSEADDR) failed: %d", errno);
     sclose(sock);