Resync
[CMakeLuaTailorHgBridge.git] / CMakeLua / Utilities / cmcurl-7.19.0 / tests / server / sws.c
blob397780c6463a6c654844e9a27fb11ef666fe6cb4
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: sws.c,v 1.1.1.1 2008-09-23 16:32:06 hoffman Exp $
22 ***************************************************************************/
24 /* sws.c: simple (silly?) web server
26 This code was originally graciously donated to the project by Juergen
27 Wilke. Thanks a bunch!
30 #include "setup.h" /* portability help from the lib directory */
32 #ifdef HAVE_SIGNAL_H
33 #include <signal.h>
34 #endif
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38 #ifdef HAVE_SYS_SOCKET_H
39 #include <sys/socket.h>
40 #endif
41 #ifdef HAVE_NETINET_IN_H
42 #include <netinet/in.h>
43 #endif
44 #ifdef _XOPEN_SOURCE_EXTENDED
45 /* This define is "almost" required to build on HPUX 11 */
46 #include <arpa/inet.h>
47 #endif
48 #ifdef HAVE_NETDB_H
49 #include <netdb.h>
50 #endif
51 #ifdef HAVE_NETINET_TCP_H
52 #include <netinet/tcp.h> /* for TCP_NODELAY */
53 #endif
55 #define ENABLE_CURLX_PRINTF
56 /* make the curlx header define all printf() functions to use the curlx_*
57 versions instead */
58 #include "curlx.h" /* from the private lib dir */
59 #include "getpart.h"
60 #include "util.h"
62 /* include memdebug.h last */
63 #include "memdebug.h"
65 #if !defined(CURL_SWS_FORK_ENABLED) && defined(HAVE_FORK)
67 * The normal sws build for the plain standard curl test suite has no use for
68 * fork(), but if you feel wild and crazy and want to setup some more exotic
69 * tests. Define this and run...
71 #define CURL_SWS_FORK_ENABLED
72 #endif
74 #define REQBUFSIZ 150000
75 #define REQBUFSIZ_TXT "149999"
77 long prevtestno=-1; /* previous test number we served */
78 long prevpartno=-1; /* previous part number we served */
79 bool prevbounce=FALSE; /* instructs the server to increase the part number for
80 a test in case the identical testno+partno request
81 shows up again */
83 #define RCMD_NORMALREQ 0 /* default request, use the tests file normally */
84 #define RCMD_IDLE 1 /* told to sit idle */
85 #define RCMD_STREAM 2 /* told to stream */
87 struct httprequest {
88 char reqbuf[REQBUFSIZ]; /* buffer area for the incoming request */
89 int checkindex; /* where to start checking of the request */
90 int offset; /* size of the incoming request */
91 long testno; /* test number found in the request */
92 long partno; /* part number found in the request */
93 bool open; /* keep connection open info, as found in the request */
94 bool auth_req; /* authentication required, don't wait for body unless
95 there's an Authorization header */
96 bool auth; /* Authorization header present in the incoming request */
97 size_t cl; /* Content-Length of the incoming request */
98 bool digest; /* Authorization digest header found */
99 bool ntlm; /* Authorization ntlm header found */
100 int pipe; /* if non-zero, expect this many requests to do a "piped"
101 request/response */
102 int skip; /* if non-zero, the server is instructed to not read this
103 many bytes from a PUT/POST request. Ie the client sends N
104 bytes said in Content-Length, but the server only reads N
105 - skip bytes. */
106 int rcmd; /* doing a special command, see defines above */
107 int prot_version; /* HTTP version * 10 */
108 bool pipelining; /* true if request is pipelined */
111 int ProcessRequest(struct httprequest *req);
112 void storerequest(char *reqbuf, ssize_t totalsize);
114 #define DEFAULT_PORT 8999
116 #ifndef DEFAULT_LOGFILE
117 #define DEFAULT_LOGFILE "log/sws.log"
118 #endif
120 const char *serverlogfile = DEFAULT_LOGFILE;
122 #define SWSVERSION "cURL test suite HTTP server/0.1"
124 #define REQUEST_DUMP "log/server.input"
125 #define RESPONSE_DUMP "log/server.response"
127 /* very-big-path support */
128 #define MAXDOCNAMELEN 140000
129 #define MAXDOCNAMELEN_TXT "139999"
131 #define REQUEST_KEYWORD_SIZE 256
132 #define REQUEST_KEYWORD_SIZE_TXT "255"
134 #define CMD_AUTH_REQUIRED "auth_required"
136 /* 'idle' means that it will accept the request fine but never respond
137 any data. Just keep the connection alive. */
138 #define CMD_IDLE "idle"
140 /* 'stream' means to send a never-ending stream of data */
141 #define CMD_STREAM "stream"
143 #define END_OF_HEADERS "\r\n\r\n"
145 enum {
146 DOCNUMBER_NOTHING = -7,
147 DOCNUMBER_QUIT = -6,
148 DOCNUMBER_BADCONNECT = -5,
149 DOCNUMBER_INTERNAL= -4,
150 DOCNUMBER_CONNECT = -3,
151 DOCNUMBER_WERULEZ = -2,
152 DOCNUMBER_404 = -1
156 /* sent as reply to a QUIT */
157 static const char *docquit =
158 "HTTP/1.1 200 Goodbye" END_OF_HEADERS;
160 /* sent as reply to a CONNECT */
161 static const char *docconnect =
162 "HTTP/1.1 200 Mighty fine indeed" END_OF_HEADERS;
164 /* sent as reply to a "bad" CONNECT */
165 static const char *docbadconnect =
166 "HTTP/1.1 501 Forbidden you fool" END_OF_HEADERS;
168 /* send back this on 404 file not found */
169 static const char *doc404 = "HTTP/1.1 404 Not Found\r\n"
170 "Server: " SWSVERSION "\r\n"
171 "Connection: close\r\n"
172 "Content-Type: text/html"
173 END_OF_HEADERS
174 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
175 "<HTML><HEAD>\n"
176 "<TITLE>404 Not Found</TITLE>\n"
177 "</HEAD><BODY>\n"
178 "<H1>Not Found</H1>\n"
179 "The requested URL was not found on this server.\n"
180 "<P><HR><ADDRESS>" SWSVERSION "</ADDRESS>\n" "</BODY></HTML>\n";
182 #ifdef SIGPIPE
183 static volatile int sigpipe; /* Why? It's not used */
184 #endif
186 #ifdef SIGPIPE
187 static void sigpipe_handler(int sig)
189 (void)sig; /* prevent warning */
190 sigpipe = 1;
192 #endif
194 int ProcessRequest(struct httprequest *req)
196 char *line=&req->reqbuf[req->checkindex];
197 bool chunked = FALSE;
198 static char request[REQUEST_KEYWORD_SIZE];
199 static char doc[MAXDOCNAMELEN];
200 char logbuf[256];
201 int prot_major, prot_minor;
202 char *end;
203 int error;
204 end = strstr(line, END_OF_HEADERS);
206 logmsg("ProcessRequest() called");
208 /* try to figure out the request characteristics as soon as possible, but
209 only once! */
210 if((req->testno == DOCNUMBER_NOTHING) &&
211 sscanf(line,
212 "%" REQUEST_KEYWORD_SIZE_TXT"s %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
213 request,
214 doc,
215 &prot_major,
216 &prot_minor) == 4) {
217 char *ptr;
219 req->prot_version = prot_major*10 + prot_minor;
221 /* find the last slash */
222 ptr = strrchr(doc, '/');
224 /* get the number after it */
225 if(ptr) {
226 FILE *stream;
227 char *filename;
229 if((strlen(doc) + strlen(request)) < 200)
230 sprintf(logbuf, "Got request: %s %s HTTP/%d.%d",
231 request, doc, prot_major, prot_minor);
232 else
233 sprintf(logbuf, "Got a *HUGE* request HTTP/%d.%d",
234 prot_major, prot_minor);
235 logmsg("%s", logbuf);
237 if(!strncmp("/verifiedserver", ptr, 15)) {
238 logmsg("Are-we-friendly question received");
239 req->testno = DOCNUMBER_WERULEZ;
240 return 1; /* done */
243 if(!strncmp("/quit", ptr, 5)) {
244 logmsg("Request-to-quit received");
245 req->testno = DOCNUMBER_QUIT;
246 return 1; /* done */
249 ptr++; /* skip the slash */
251 /* skip all non-numericals following the slash */
252 while(*ptr && !ISDIGIT(*ptr))
253 ptr++;
255 req->testno = strtol(ptr, &ptr, 10);
257 if(req->testno > 10000) {
258 req->partno = req->testno % 10000;
259 req->testno /= 10000;
261 else
262 req->partno = 0;
264 sprintf(logbuf, "Requested test number %ld part %ld",
265 req->testno, req->partno);
267 logmsg("%s", logbuf);
269 filename = test2file(req->testno);
271 stream=fopen(filename, "rb");
272 if(!stream) {
273 error = ERRNO;
274 logmsg("fopen() failed with error: %d %s", error, strerror(error));
275 logmsg("Error opening file: %s", filename);
276 logmsg("Couldn't open test file %d", req->testno);
277 req->open = FALSE; /* closes connection */
278 return 1; /* done */
280 else {
281 char *cmd = NULL;
282 size_t cmdsize = 0;
283 int num=0;
285 /* get the custom server control "commands" */
286 cmd = (char *)spitout(stream, "reply", "servercmd", &cmdsize);
287 fclose(stream);
289 if(cmdsize) {
290 logmsg("Found a reply-servercmd section!");
292 if(!strncmp(CMD_AUTH_REQUIRED, cmd, strlen(CMD_AUTH_REQUIRED))) {
293 logmsg("instructed to require authorization header");
294 req->auth_req = TRUE;
296 else if(!strncmp(CMD_IDLE, cmd, strlen(CMD_IDLE))) {
297 logmsg("instructed to idle");
298 req->rcmd = RCMD_IDLE;
299 req->open = TRUE;
301 else if(!strncmp(CMD_STREAM, cmd, strlen(CMD_STREAM))) {
302 logmsg("instructed to stream");
303 req->rcmd = RCMD_STREAM;
305 else if(1 == sscanf(cmd, "pipe: %d", &num)) {
306 logmsg("instructed to allow a pipe size %d", num);
307 req->pipe = num-1; /* decrease by one since we don't count the
308 first request in this number */
310 else if(1 == sscanf(cmd, "skip: %d", &num)) {
311 logmsg("instructed to skip this number of bytes %d", num);
312 req->skip = num;
314 else {
315 logmsg("funny instruction found: %s", cmd);
317 free(cmd);
321 else {
322 if(sscanf(req->reqbuf, "CONNECT %" MAXDOCNAMELEN_TXT "s HTTP/%d.%d",
323 doc, &prot_major, &prot_minor) == 3) {
324 sprintf(logbuf, "Received a CONNECT %s HTTP/%d.%d request",
325 doc, prot_major, prot_minor);
326 logmsg("%s", logbuf);
328 if(req->prot_version == 10)
329 req->open = FALSE; /* HTTP 1.0 closes connection by default */
331 if(!strncmp(doc, "bad", 3))
332 /* if the host name starts with bad, we fake an error here */
333 req->testno = DOCNUMBER_BADCONNECT;
334 else if(!strncmp(doc, "test", 4)) {
335 /* if the host name starts with test, the port number used in the
336 CONNECT line will be used as test number! */
337 char *portp = strchr(doc, ':');
338 if(portp)
339 req->testno = atoi(portp+1);
340 else
341 req->testno = DOCNUMBER_CONNECT;
343 else
344 req->testno = DOCNUMBER_CONNECT;
346 else {
347 logmsg("Did not find test number in PATH");
348 req->testno = DOCNUMBER_404;
353 if(!end) {
354 /* we don't have a complete request yet! */
355 logmsg("ProcessRequest returned without a complete request");
356 return 0;
358 logmsg("ProcessRequest found a complete request");
360 if(req->pipe)
361 /* we do have a full set, advance the checkindex to after the end of the
362 headers, for the pipelining case mostly */
363 req->checkindex += (end - line) + strlen(END_OF_HEADERS);
365 /* **** Persistence ****
367 * If the request is a HTTP/1.0 one, we close the connection unconditionally
368 * when we're done.
370 * If the request is a HTTP/1.1 one, we MUST check for a "Connection:"
371 * header that might say "close". If it does, we close a connection when
372 * this request is processed. Otherwise, we keep the connection alive for X
373 * seconds.
376 do {
377 if((req->cl<=0) && curlx_strnequal("Content-Length:", line, 15)) {
378 /* If we don't ignore content-length, we read it and we read the whole
379 request including the body before we return. If we've been told to
380 ignore the content-length, we will return as soon as all headers
381 have been received */
382 size_t cl = strtol(line+15, &line, 10);
383 req->cl = cl - req->skip;
385 logmsg("Found Content-Length: %d in the request", cl);
386 if(req->skip)
387 logmsg("... but will abort after %d bytes", req->cl);
388 break;
390 else if(curlx_strnequal("Transfer-Encoding: chunked", line,
391 strlen("Transfer-Encoding: chunked"))) {
392 /* chunked data coming in */
393 chunked = TRUE;
396 if(chunked) {
397 if(strstr(req->reqbuf, "\r\n0\r\n\r\n"))
398 /* end of chunks reached */
399 return 1; /* done */
400 else
401 return 0; /* not done */
404 line = strchr(line, '\n');
405 if(line)
406 line++;
407 } while(line);
409 if(!req->auth && strstr(req->reqbuf, "Authorization:")) {
410 req->auth = TRUE; /* Authorization: header present! */
411 if(req->auth_req)
412 logmsg("Authorization header found, as required");
415 if(!req->digest && strstr(req->reqbuf, "Authorization: Digest")) {
416 /* If the client is passing this Digest-header, we set the part number
417 to 1000. Not only to spice up the complexity of this, but to make
418 Digest stuff to work in the test suite. */
419 req->partno += 1000;
420 req->digest = TRUE; /* header found */
421 logmsg("Received Digest request, sending back data %d", req->partno);
423 else if(!req->ntlm &&
424 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAD")) {
425 /* If the client is passing this type-3 NTLM header */
426 req->partno += 1002;
427 req->ntlm = TRUE; /* NTLM found */
428 logmsg("Received NTLM type-3, sending back data %d", req->partno);
429 if(req->cl) {
430 logmsg(" Expecting %d POSTed bytes", req->cl);
433 else if(!req->ntlm &&
434 strstr(req->reqbuf, "Authorization: NTLM TlRMTVNTUAAB")) {
435 /* If the client is passing this type-1 NTLM header */
436 req->partno += 1001;
437 req->ntlm = TRUE; /* NTLM found */
438 logmsg("Received NTLM type-1, sending back data %d", req->partno);
440 if(strstr(req->reqbuf, "Connection: close"))
441 req->open = FALSE; /* close connection after this request */
443 if(!req->pipe &&
444 req->open &&
445 req->prot_version >= 11 &&
446 end &&
447 req->reqbuf + req->offset > end + strlen(END_OF_HEADERS) &&
448 (!strncmp(req->reqbuf, "GET", strlen("GET")) ||
449 !strncmp(req->reqbuf, "HEAD", strlen("HEAD")))) {
450 /* If we have a persistent connection, HTTP version >= 1.1
451 and GET/HEAD request, enable pipelining. */
452 req->checkindex = (end - req->reqbuf) + strlen(END_OF_HEADERS);
453 req->pipelining = TRUE;
456 while(req->pipe) {
457 /* scan for more header ends within this chunk */
458 line = &req->reqbuf[req->checkindex];
459 end = strstr(line, END_OF_HEADERS);
460 if(!end)
461 break;
462 req->checkindex += (end - line) + strlen(END_OF_HEADERS);
463 req->pipe--;
467 /* If authentication is required and no auth was provided, end now. This
468 makes the server NOT wait for PUT/POST data and you can then make the
469 test case send a rejection before any such data has been sent. Test case
470 154 uses this.*/
471 if(req->auth_req && !req->auth)
472 return 1;
474 if(req->cl > 0) {
475 if(req->cl <= req->offset - (end - req->reqbuf) - strlen(END_OF_HEADERS))
476 return 1; /* done */
477 else
478 return 0; /* not complete yet */
481 return 1; /* done */
484 /* store the entire request in a file */
485 void storerequest(char *reqbuf, ssize_t totalsize)
487 int res;
488 int error;
489 ssize_t written;
490 ssize_t writeleft;
491 FILE *dump;
493 if (reqbuf == NULL)
494 return;
496 if (totalsize == 0)
497 return;
498 else if (totalsize < 0) {
499 logmsg("Invalid size (%d bytes) for request input. Not written to %s",
500 totalsize, REQUEST_DUMP);
501 return;
504 do {
505 dump = fopen(REQUEST_DUMP, "ab");
506 } while ((dump == NULL) && ((error = ERRNO) == EINTR));
507 if (dump == NULL) {
508 logmsg("Error opening file %s error: %d %s",
509 REQUEST_DUMP, error, strerror(error));
510 logmsg("Failed to write request input to " REQUEST_DUMP);
511 return;
514 writeleft = totalsize;
515 do {
516 written = (ssize_t)fwrite((void *) &reqbuf[totalsize-writeleft],
517 1, (size_t)writeleft, dump);
518 if (written > 0)
519 writeleft -= written;
520 } while ((writeleft > 0) && ((error = ERRNO) == EINTR));
522 if (writeleft > 0) {
523 logmsg("Error writing file %s error: %d %s",
524 REQUEST_DUMP, error, strerror(error));
525 logmsg("Wrote only (%d bytes) of (%d bytes) request input to %s",
526 totalsize-writeleft, totalsize, REQUEST_DUMP);
529 do {
530 res = fclose(dump);
531 } while(res && ((error = ERRNO) == EINTR));
532 if(res)
533 logmsg("Error closing file %s error: %d %s",
534 REQUEST_DUMP, error, strerror(error));
536 if(!writeleft)
537 logmsg("Wrote request (%d bytes) input to " REQUEST_DUMP,
538 totalsize);
541 /* return 0 on success, non-zero on failure */
542 static int get_request(curl_socket_t sock, struct httprequest *req)
544 int fail = 0;
545 char *reqbuf = req->reqbuf;
546 ssize_t got = 0;
548 char *pipereq;
549 int pipereq_length = 0;
551 if(req->pipelining) {
552 pipereq = reqbuf + req->checkindex;
553 pipereq_length = req->offset - req->checkindex;
556 /*** Init the httprequest structure properly for the upcoming request ***/
558 req->checkindex = 0;
559 req->offset = 0;
560 req->testno = DOCNUMBER_NOTHING;
561 req->partno = 0;
562 req->open = TRUE;
563 req->auth_req = FALSE;
564 req->auth = FALSE;
565 req->cl = 0;
566 req->digest = FALSE;
567 req->ntlm = FALSE;
568 req->pipe = 0;
569 req->skip = 0;
570 req->rcmd = RCMD_NORMALREQ;
571 req->prot_version = 0;
572 req->pipelining = FALSE;
574 /*** end of httprequest init ***/
576 while (req->offset < REQBUFSIZ-1) {
577 if(pipereq_length) {
578 memmove(reqbuf, pipereq, pipereq_length);
579 got = pipereq_length;
580 pipereq_length = 0;
582 else {
583 if(req->skip)
584 /* we are instructed to not read the entire thing, so we make sure to only
585 read what we're supposed to and NOT read the enire thing the client
586 wants to send! */
587 got = sread(sock, reqbuf + req->offset, req->cl);
588 else
589 got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
591 if (got <= 0) {
592 if (got < 0) {
593 logmsg("recv() returned error: %d", SOCKERRNO);
594 return DOCNUMBER_INTERNAL;
596 logmsg("Connection closed by client");
597 reqbuf[req->offset] = '\0';
599 /* dump the request receivied so far to the external file */
600 storerequest(reqbuf, req->offset);
601 return DOCNUMBER_INTERNAL;
604 logmsg("Read %d bytes", got);
606 req->offset += got;
607 reqbuf[req->offset] = '\0';
609 if(ProcessRequest(req)) {
610 if(req->pipe--) {
611 logmsg("Waiting for another piped request");
612 continue;
614 break;
618 if((req->offset == REQBUFSIZ-1) && (got > 0)) {
619 logmsg("Request would overflow buffer, closing connection");
620 /* dump request received so far to external file anyway */
621 reqbuf[REQBUFSIZ-1] = '\0';
622 fail = 1;
624 else if(req->offset > REQBUFSIZ-1) {
625 logmsg("Request buffer overflow, closing connection");
626 /* dump request received so far to external file anyway */
627 reqbuf[REQBUFSIZ-1] = '\0';
628 fail = 1;
630 else
631 reqbuf[req->offset] = '\0';
633 /* dump the request to an external file */
634 storerequest(reqbuf, req->pipelining ? req->checkindex : req->offset);
636 return fail; /* return 0 on success */
639 /* returns -1 on failure */
640 static int send_doc(curl_socket_t sock, struct httprequest *req)
642 ssize_t written;
643 size_t count;
644 const char *buffer;
645 char *ptr=NULL;
646 FILE *stream;
647 char *cmd=NULL;
648 size_t cmdsize=0;
649 FILE *dump;
650 bool persistant = TRUE;
651 bool sendfailure = FALSE;
652 size_t responsesize;
653 int error;
654 int res;
656 static char weare[256];
658 char partbuf[80]="data";
660 logmsg("Send response number %d part %d", req->testno, req->partno);
662 switch(req->rcmd) {
663 default:
664 case RCMD_NORMALREQ:
665 break; /* continue with business as usual */
666 case RCMD_STREAM:
667 #define STREAMTHIS "a string to stream 01234567890\n"
668 count = strlen(STREAMTHIS);
669 while(1) {
670 written = swrite(sock, STREAMTHIS, count);
671 if(written != (ssize_t)count) {
672 logmsg("Stopped streaming");
673 break;
676 return -1;
677 case RCMD_IDLE:
678 /* Do nothing. Sit idle. Pretend it rains. */
679 return 0;
682 req->open = FALSE;
684 if(req->testno < 0) {
685 size_t msglen;
686 char msgbuf[64];
688 switch(req->testno) {
689 case DOCNUMBER_QUIT:
690 logmsg("Replying to QUIT");
691 buffer = docquit;
692 break;
693 case DOCNUMBER_WERULEZ:
694 /* we got a "friends?" question, reply back that we sure are */
695 logmsg("Identifying ourselves as friends");
696 sprintf(msgbuf, "WE ROOLZ: %ld\r\n", (long)getpid());
697 msglen = strlen(msgbuf);
698 sprintf(weare, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s",
699 msglen, msgbuf);
700 buffer = weare;
701 break;
702 case DOCNUMBER_INTERNAL:
703 logmsg("Bailing out due to internal error");
704 return -1;
705 case DOCNUMBER_CONNECT:
706 logmsg("Replying to CONNECT");
707 buffer = docconnect;
708 break;
709 case DOCNUMBER_BADCONNECT:
710 logmsg("Replying to a bad CONNECT");
711 buffer = docbadconnect;
712 break;
713 case DOCNUMBER_404:
714 default:
715 logmsg("Replying to with a 404");
716 buffer = doc404;
717 break;
719 ptr = NULL;
720 stream=NULL;
722 count = strlen(buffer);
724 else {
725 char *filename = test2file(req->testno);
727 if(0 != req->partno)
728 sprintf(partbuf, "data%ld", req->partno);
730 stream=fopen(filename, "rb");
731 if(!stream) {
732 error = ERRNO;
733 logmsg("fopen() failed with error: %d %s", error, strerror(error));
734 logmsg("Error opening file: %s", filename);
735 logmsg("Couldn't open test file");
736 return 0;
738 else {
739 buffer = spitout(stream, "reply", partbuf, &count);
740 ptr = (char *)buffer;
741 fclose(stream);
744 /* re-open the same file again */
745 stream=fopen(filename, "rb");
746 if(!stream) {
747 error = ERRNO;
748 logmsg("fopen() failed with error: %d %s", error, strerror(error));
749 logmsg("Error opening file: %s", filename);
750 logmsg("Couldn't open test file");
751 return 0;
753 else {
754 /* get the custom server control "commands" */
755 cmd = (char *)spitout(stream, "reply", "postcmd", &cmdsize);
756 fclose(stream);
760 dump = fopen(RESPONSE_DUMP, "ab"); /* b is for windows-preparing */
761 if(!dump) {
762 error = ERRNO;
763 logmsg("fopen() failed with error: %d %s", error, strerror(error));
764 logmsg("Error opening file: %s", RESPONSE_DUMP);
765 logmsg("couldn't create logfile: " RESPONSE_DUMP);
766 return -1;
769 /* If the word 'swsclose' is present anywhere in the reply chunk, the
770 connection will be closed after the data has been sent to the requesting
771 client... */
772 if(strstr(buffer, "swsclose") || !count) {
773 persistant = FALSE;
774 logmsg("connection close instruction \"swsclose\" found in response");
776 if(strstr(buffer, "swsbounce")) {
777 prevbounce = TRUE;
778 logmsg("enable \"swsbounce\" in the next request");
780 else
781 prevbounce = FALSE;
784 responsesize = count;
785 do {
786 /* Ok, we send no more than 200 bytes at a time, just to make sure that
787 larger chunks are split up so that the client will need to do multiple
788 recv() calls to get it and thus we exercise that code better */
789 size_t num = count;
790 if(num > 200)
791 num = 200;
792 written = swrite(sock, buffer, num);
793 if (written < 0) {
794 sendfailure = TRUE;
795 break;
797 else {
798 logmsg("Sent off %d bytes", written);
800 /* write to file as well */
801 fwrite(buffer, 1, written, dump);
803 count -= written;
804 buffer += written;
805 } while(count>0);
807 do {
808 res = fclose(dump);
809 } while(res && ((error = ERRNO) == EINTR));
810 if(res)
811 logmsg("Error closing file %s error: %d %s",
812 RESPONSE_DUMP, error, strerror(error));
814 if(sendfailure) {
815 logmsg("Sending response failed. Only (%d bytes) of (%d bytes) were sent",
816 responsesize-count, responsesize);
817 if(ptr)
818 free(ptr);
819 if(cmd)
820 free(cmd);
821 return -1;
824 logmsg("Response sent (%d bytes) and written to " RESPONSE_DUMP,
825 responsesize);
827 if(ptr)
828 free(ptr);
830 if(cmdsize > 0 ) {
831 char command[32];
832 int num;
833 ptr=cmd;
834 do {
835 if(2 == sscanf(ptr, "%31s %d", command, &num)) {
836 if(!strcmp("wait", command)) {
837 logmsg("Told to sleep for %d seconds", num);
838 sleep(num); /* wait this many seconds */
840 else
841 logmsg("Unknown command in reply command section");
843 ptr = strchr(ptr, '\n');
844 if(ptr)
845 ptr++;
846 else
847 ptr = NULL;
848 } while(ptr && *ptr);
850 if(cmd)
851 free(cmd);
853 req->open = persistant;
855 prevtestno = req->testno;
856 prevpartno = req->partno;
858 return 0;
861 bool use_ipv6=FALSE;
863 int main(int argc, char *argv[])
865 struct sockaddr_in me;
866 #ifdef ENABLE_IPV6
867 struct sockaddr_in6 me6;
868 #endif /* ENABLE_IPV6 */
869 curl_socket_t sock, msgsock;
870 int flag;
871 unsigned short port = DEFAULT_PORT;
872 char *pidname= (char *)".http.pid";
873 struct httprequest req;
874 int rc;
875 int arg=1;
876 #ifdef CURL_SWS_FORK_ENABLED
877 bool use_fork = FALSE;
878 #endif
880 while(argc>arg) {
881 if(!strcmp("--version", argv[arg])) {
882 printf("sws IPv4%s"
883 #ifdef CURL_SWS_FORK_ENABLED
884 " FORK"
885 #endif
886 "\n"
888 #ifdef ENABLE_IPV6
889 "/IPv6"
890 #else
892 #endif
894 return 0;
896 else if(!strcmp("--pidfile", argv[arg])) {
897 arg++;
898 if(argc>arg)
899 pidname = argv[arg++];
901 else if(!strcmp("--ipv6", argv[arg])) {
902 #ifdef ENABLE_IPV6
903 use_ipv6=TRUE;
904 #endif
905 arg++;
907 #ifdef CURL_SWS_FORK_ENABLED
908 else if(!strcmp("--fork", argv[arg])) {
909 use_fork=TRUE;
910 arg++;
912 #endif
913 else if(argc>arg) {
915 if(atoi(argv[arg]))
916 port = (unsigned short)atoi(argv[arg++]);
918 if(argc>arg)
919 path = argv[arg++];
923 #ifdef WIN32
924 win32_init();
925 atexit(win32_cleanup);
926 #else
928 #ifdef SIGPIPE
929 #ifdef HAVE_SIGNAL
930 signal(SIGPIPE, sigpipe_handler);
931 #endif
932 #ifdef HAVE_SIGINTERRUPT
933 siginterrupt(SIGPIPE, 1);
934 #endif
935 #endif
936 #endif
938 #ifdef ENABLE_IPV6
939 if(!use_ipv6)
940 #endif
941 sock = socket(AF_INET, SOCK_STREAM, 0);
942 #ifdef ENABLE_IPV6
943 else
944 sock = socket(AF_INET6, SOCK_STREAM, 0);
945 #endif
947 if (CURL_SOCKET_BAD == sock) {
948 logmsg("Error opening socket: %d", SOCKERRNO);
949 return 1;
952 flag = 1;
953 if (0 != setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
954 (void *) &flag, sizeof(flag))) {
955 logmsg("setsockopt(SO_REUSEADDR) failed: %d", SOCKERRNO);
956 sclose(sock);
957 return 1;
960 #ifdef ENABLE_IPV6
961 if(!use_ipv6) {
962 #endif
963 memset(&me, 0, sizeof(me));
964 me.sin_family = AF_INET;
965 me.sin_addr.s_addr = INADDR_ANY;
966 me.sin_port = htons(port);
967 rc = bind(sock, (struct sockaddr *) &me, sizeof(me));
968 #ifdef ENABLE_IPV6
970 else {
971 memset(&me6, 0, sizeof(me6));
972 me6.sin6_family = AF_INET6;
973 me6.sin6_addr = in6addr_any;
974 me6.sin6_port = htons(port);
975 rc = bind(sock, (struct sockaddr *) &me6, sizeof(me6));
977 #endif /* ENABLE_IPV6 */
978 if(0 != rc) {
979 logmsg("Error binding socket: %d", SOCKERRNO);
980 sclose(sock);
981 return 1;
984 if(!write_pidfile(pidname)) {
985 sclose(sock);
986 return 1;
989 logmsg("Running IPv%d version on port %d",
990 #ifdef ENABLE_IPV6
991 (use_ipv6?6:4)
992 #else
994 #endif
995 , port );
997 /* start accepting connections */
998 rc = listen(sock, 5);
999 if(0 != rc) {
1000 logmsg("listen() failed with error: %d", SOCKERRNO);
1001 sclose(sock);
1002 return 1;
1005 while (1) {
1006 msgsock = accept(sock, NULL, NULL);
1008 if (CURL_SOCKET_BAD == msgsock) {
1009 printf("MAJOR ERROR: accept() failed with error: %d\n", SOCKERRNO);
1010 break;
1013 set_advisor_read_lock(SERVERLOGS_LOCK);
1015 #ifdef CURL_SWS_FORK_ENABLED
1016 if(use_fork) {
1017 /* The fork enabled version just forks off the child and don't care
1018 about it anymore, so don't assume otherwise. Beware and don't do
1019 this at home. */
1020 rc = fork();
1021 if(-1 == rc) {
1022 printf("MAJOR ERROR: fork() failed!\n");
1023 break;
1026 else
1027 /* not a fork, just set rc so the following proceeds nicely */
1028 rc = 0;
1029 /* 0 is returned to the child */
1030 if(0 == rc) {
1031 #endif
1032 logmsg("====> Client connect");
1034 #ifdef TCP_NODELAY
1036 * Disable the Nagle algorithm to make it easier to send out a large
1037 * response in many small segments to torture the clients more.
1039 flag = 1;
1040 if (setsockopt(msgsock, IPPROTO_TCP, TCP_NODELAY,
1041 (void *)&flag, sizeof(flag)) == -1) {
1042 logmsg("====> TCP_NODELAY failed");
1044 #endif
1046 /* initialization of httprequest struct is done in get_request(), but due
1047 to pipelining treatment the pipelining struct field must be initialized
1048 previously to FALSE every time a new connection arrives. */
1050 req.pipelining = FALSE;
1052 do {
1053 if(get_request(msgsock, &req))
1054 /* non-zero means error, break out of loop */
1055 break;
1057 if(prevbounce) {
1058 /* bounce treatment requested */
1059 if((req.testno == prevtestno) &&
1060 (req.partno == prevpartno)) {
1061 req.partno++;
1062 logmsg("BOUNCE part number to %ld", req.partno);
1064 else {
1065 prevbounce = FALSE;
1066 prevtestno = -1;
1067 prevpartno = -1;
1071 send_doc(msgsock, &req);
1073 if((req.testno < 0) && (req.testno != DOCNUMBER_CONNECT)) {
1074 logmsg("special request received, no persistency");
1075 break;
1077 if(!req.open) {
1078 logmsg("instructed to close connection after server-reply");
1079 break;
1082 if(req.open)
1083 logmsg("=> persistant connection request ended, awaits new request");
1084 /* if we got a CONNECT, loop and get another request as well! */
1085 } while(req.open || (req.testno == DOCNUMBER_CONNECT));
1087 logmsg("====> Client disconnect");
1088 sclose(msgsock);
1090 clear_advisor_read_lock(SERVERLOGS_LOCK);
1092 if (req.testno == DOCNUMBER_QUIT)
1093 break;
1094 #ifdef CURL_SWS_FORK_ENABLED
1096 #endif
1099 sclose(sock);
1101 clear_advisor_read_lock(SERVERLOGS_LOCK);
1103 return 0;