test 530 is the first ever HTTP pipelining test for libcurl

This commit is contained in:
Daniel Stenberg 2006-09-08 11:56:56 +00:00
parent dc7c915553
commit e3c15fc4b9
6 changed files with 252 additions and 8 deletions

View 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 server will NOT wait for the full request body to get sent
idle - do nothing after receiving the request, just "sit idle" idle - do nothing after receiving the request, just "sit idle"
stream - continuously send data to the client, never-ending 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> </servercmd>
</reply> </reply>

View File

@ -34,5 +34,6 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \
test250 test251 test252 test253 test254 test255 test521 test522 test523 \ test250 test251 test252 test253 test254 test255 test521 test522 test523 \
test256 test257 test258 test259 test260 test261 test262 test263 test264 \ test256 test257 test258 test259 test260 test261 test262 test263 test264 \
test265 test266 test267 test268 test269 test270 test271 test272 test273 \ 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

78
tests/data/test530 Normal file
View File

@ -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
Host: %HOSTIP:%HTTPPORT
Accept: */*
GET /path/530 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
GET /path/530 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
GET /path/530 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
</protocol>
</verify>

View File

@ -41,7 +41,7 @@ SUPPORTFILES = first.c test.h
# These are all libcurl test programs # These are all libcurl test programs
noinst_PROGRAMS = lib500 lib501 lib502 lib503 lib504 lib505 lib506 lib507 \ noinst_PROGRAMS = lib500 lib501 lib502 lib503 lib504 lib505 lib506 lib507 \
lib508 lib509 lib510 lib511 lib512 lib513 lib514 lib515 lib516 lib517 \ 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_SOURCES = lib500.c $(SUPPORTFILES)
lib500_LDADD = $(LIBDIR)/libcurl.la lib500_LDADD = $(LIBDIR)/libcurl.la
@ -152,3 +152,7 @@ lib527_CFLAGS = -DLIB527
lib527_LDADD = $(LIBDIR)/libcurl.la lib527_LDADD = $(LIBDIR)/libcurl.la
lib527_DEPENDENCIES = $(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

120
tests/libtest/lib530.c Normal file
View File

@ -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;
}

View File

@ -90,6 +90,7 @@ bool prevbounce; /* instructs the server to increase the part number for
struct httprequest { struct httprequest {
char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */ 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 */ int offset; /* size of the incoming request */
long testno; /* test number found in the request */ long testno; /* test number found in the request */
long partno; /* part 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 */ size_t cl; /* Content-Length of the incoming request */
bool digest; /* Authorization digest header found */ bool digest; /* Authorization digest header found */
bool ntlm; /* Authorization ntlm 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 rcmd; /* doing a special command, see defines above */
}; };
@ -188,14 +190,16 @@ static void sigpipe_handler(int sig)
int ProcessRequest(struct httprequest *req) int ProcessRequest(struct httprequest *req)
{ {
char *line=req->reqbuf; char *line=&req->reqbuf[req->checkindex];
char chunked=FALSE; char chunked=FALSE;
static char request[REQUEST_KEYWORD_SIZE]; static char request[REQUEST_KEYWORD_SIZE];
static char doc[MAXDOCNAMELEN]; static char doc[MAXDOCNAMELEN];
char logbuf[256]; char logbuf[256];
int prot_major, prot_minor; int prot_major, prot_minor;
char *end; 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 /* try to figure out the request characteristics as soon as possible, but
only once! */ only once! */
@ -266,6 +270,7 @@ int ProcessRequest(struct httprequest *req)
else { else {
char *cmd = NULL; char *cmd = NULL;
size_t cmdsize = 0; size_t cmdsize = 0;
int num=0;
/* get the custom server control "commands" */ /* get the custom server control "commands" */
cmd = (char *)spitout(stream, "reply", "servercmd", &cmdsize); cmd = (char *)spitout(stream, "reply", "servercmd", &cmdsize);
@ -287,6 +292,11 @@ int ProcessRequest(struct httprequest *req)
logmsg("instructed to stream"); logmsg("instructed to stream");
req->rcmd = RCMD_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); free(cmd);
} }
} }
@ -323,9 +333,17 @@ int ProcessRequest(struct httprequest *req)
} }
} }
if(!end) if(!end) {
/* we don't have a complete request yet! */ /* we don't have a complete request yet! */
logmsg("ProcessRequest returned without a complete request");
return 0; 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 **** /* **** Persistancy ****
* *
@ -402,6 +420,17 @@ int ProcessRequest(struct httprequest *req)
if(strstr(req->reqbuf, "Connection: close")) if(strstr(req->reqbuf, "Connection: close"))
req->open = FALSE; /* close connection after this request */ 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 /* 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 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 test case send a rejection before any such data has been sent. Test case
@ -415,6 +444,7 @@ int ProcessRequest(struct httprequest *req)
else else
return 0; /* not complete yet */ return 0; /* not complete yet */
} }
return 1; /* done */ 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->testno = DOCNUMBER_NOTHING; /* safe default */
req->open = TRUE; /* connection should remain open and wait for more req->open = TRUE; /* connection should remain open and wait for more
commands */ commands */
req->pipe = 0;
/*** end of httprequest init ***/ /*** end of httprequest init ***/
@ -467,12 +498,20 @@ static int get_request(curl_socket_t sock, struct httprequest *req)
storerequest(reqbuf); storerequest(reqbuf);
return DOCNUMBER_INTERNAL; return DOCNUMBER_INTERNAL;
} }
logmsg("Read %d bytes", got);
req->offset += got; req->offset += got;
reqbuf[req->offset] = 0; reqbuf[req->offset] = 0;
if(ProcessRequest(req)) if(ProcessRequest(req)) {
if(req->pipe--) {
logmsg("Waiting for another piped request");
continue;
}
break; break;
}
} }
if (req->offset >= REQBUFSIZ) { if (req->offset >= REQBUFSIZ) {
@ -764,7 +803,7 @@ int main(int argc, char *argv[])
} }
flag = 1; flag = 1;
if (0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, if (0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
(void *) &flag, sizeof(flag))) { (void *) &flag, sizeof(flag))) {
logmsg("setsockopt(SO_REUSEADDR) failed: %d", errno); logmsg("setsockopt(SO_REUSEADDR) failed: %d", errno);
sclose(sock); sclose(sock);