mirror of
https://github.com/moparisthebest/curl
synced 2024-12-22 08:08:50 -05:00
test 530 is the first ever HTTP pipelining test for libcurl
This commit is contained in:
parent
dc7c915553
commit
e3c15fc4b9
@ -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>
|
||||||
|
|
||||||
|
@ -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
78
tests/data/test530
Normal 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>
|
@ -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
120
tests/libtest/lib530.c
Normal 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;
|
||||||
|
}
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user