1 /***************************************************************************
3 * Project ___| | | | _ \| |
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 */
38 #ifdef HAVE_SYS_SOCKET_H
39 #include <sys/socket.h>
41 #ifdef HAVE_NETINET_IN_H
42 #include <netinet/in.h>
44 #ifdef _XOPEN_SOURCE_EXTENDED
45 /* This define is "almost" required to build on HPUX 11 */
46 #include <arpa/inet.h>
51 #ifdef HAVE_NETINET_TCP_H
52 #include <netinet/tcp.h> /* for TCP_NODELAY */
55 #define ENABLE_CURLX_PRINTF
56 /* make the curlx header define all printf() functions to use the curlx_*
58 #include "curlx.h" /* from the private lib dir */
62 /* include memdebug.h last */
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
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
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 */
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"
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
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"
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"
146 DOCNUMBER_NOTHING
= -7,
148 DOCNUMBER_BADCONNECT
= -5,
149 DOCNUMBER_INTERNAL
= -4,
150 DOCNUMBER_CONNECT
= -3,
151 DOCNUMBER_WERULEZ
= -2,
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"
174 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
176 "<TITLE>404 Not Found</TITLE>\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";
183 static volatile int sigpipe
; /* Why? It's not used */
187 static void sigpipe_handler(int sig
)
189 (void)sig
; /* prevent warning */
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
];
201 int prot_major
, prot_minor
;
204 end
= strstr(line
, END_OF_HEADERS
);
206 logmsg("ProcessRequest() called");
208 /* try to figure out the request characteristics as soon as possible, but
210 if((req
->testno
== DOCNUMBER_NOTHING
) &&
212 "%" REQUEST_KEYWORD_SIZE_TXT
"s %" MAXDOCNAMELEN_TXT
"s HTTP/%d.%d",
219 req
->prot_version
= prot_major
*10 + prot_minor
;
221 /* find the last slash */
222 ptr
= strrchr(doc
, '/');
224 /* get the number after it */
229 if((strlen(doc
) + strlen(request
)) < 200)
230 sprintf(logbuf
, "Got request: %s %s HTTP/%d.%d",
231 request
, doc
, prot_major
, prot_minor
);
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
;
243 if(!strncmp("/quit", ptr
, 5)) {
244 logmsg("Request-to-quit received");
245 req
->testno
= DOCNUMBER_QUIT
;
249 ptr
++; /* skip the slash */
251 /* skip all non-numericals following the slash */
252 while(*ptr
&& !ISDIGIT(*ptr
))
255 req
->testno
= strtol(ptr
, &ptr
, 10);
257 if(req
->testno
> 10000) {
258 req
->partno
= req
->testno
% 10000;
259 req
->testno
/= 10000;
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");
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 */
285 /* get the custom server control "commands" */
286 cmd
= (char *)spitout(stream
, "reply", "servercmd", &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
;
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
);
315 logmsg("funny instruction found: %s", cmd
);
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
, ':');
339 req
->testno
= atoi(portp
+1);
341 req
->testno
= DOCNUMBER_CONNECT
;
344 req
->testno
= DOCNUMBER_CONNECT
;
347 logmsg("Did not find test number in PATH");
348 req
->testno
= DOCNUMBER_404
;
354 /* we don't have a complete request yet! */
355 logmsg("ProcessRequest returned without a complete request");
358 logmsg("ProcessRequest found a complete request");
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
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
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
);
387 logmsg("... but will abort after %d bytes", req
->cl
);
390 else if(curlx_strnequal("Transfer-Encoding: chunked", line
,
391 strlen("Transfer-Encoding: chunked"))) {
392 /* chunked data coming in */
397 if(strstr(req
->reqbuf
, "\r\n0\r\n\r\n"))
398 /* end of chunks reached */
401 return 0; /* not done */
404 line
= strchr(line
, '\n');
409 if(!req
->auth
&& strstr(req
->reqbuf
, "Authorization:")) {
410 req
->auth
= TRUE
; /* Authorization: header present! */
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. */
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 */
427 req
->ntlm
= TRUE
; /* NTLM found */
428 logmsg("Received NTLM type-3, sending back data %d", req
->partno
);
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 */
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 */
445 req
->prot_version
>= 11 &&
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
;
457 /* scan for more header ends within this chunk */
458 line
= &req
->reqbuf
[req
->checkindex
];
459 end
= strstr(line
, END_OF_HEADERS
);
462 req
->checkindex
+= (end
- line
) + strlen(END_OF_HEADERS
);
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
471 if(req
->auth_req
&& !req
->auth
)
475 if(req
->cl
<= req
->offset
- (end
- req
->reqbuf
) - strlen(END_OF_HEADERS
))
478 return 0; /* not complete yet */
484 /* store the entire request in a file */
485 void storerequest(char *reqbuf
, ssize_t totalsize
)
498 else if (totalsize
< 0) {
499 logmsg("Invalid size (%d bytes) for request input. Not written to %s",
500 totalsize
, REQUEST_DUMP
);
505 dump
= fopen(REQUEST_DUMP
, "ab");
506 } while ((dump
== NULL
) && ((error
= ERRNO
) == EINTR
));
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
);
514 writeleft
= totalsize
;
516 written
= (ssize_t
)fwrite((void *) &reqbuf
[totalsize
-writeleft
],
517 1, (size_t)writeleft
, dump
);
519 writeleft
-= written
;
520 } while ((writeleft
> 0) && ((error
= ERRNO
) == EINTR
));
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
);
531 } while(res
&& ((error
= ERRNO
) == EINTR
));
533 logmsg("Error closing file %s error: %d %s",
534 REQUEST_DUMP
, error
, strerror(error
));
537 logmsg("Wrote request (%d bytes) input to " REQUEST_DUMP
,
541 /* return 0 on success, non-zero on failure */
542 static int get_request(curl_socket_t sock
, struct httprequest
*req
)
545 char *reqbuf
= req
->reqbuf
;
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 ***/
560 req
->testno
= DOCNUMBER_NOTHING
;
563 req
->auth_req
= FALSE
;
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) {
578 memmove(reqbuf
, pipereq
, pipereq_length
);
579 got
= pipereq_length
;
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
587 got
= sread(sock
, reqbuf
+ req
->offset
, req
->cl
);
589 got
= sread(sock
, reqbuf
+ req
->offset
, REQBUFSIZ
-1 - req
->offset
);
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
);
607 reqbuf
[req
->offset
] = '\0';
609 if(ProcessRequest(req
)) {
611 logmsg("Waiting for another piped request");
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';
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';
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
)
650 bool persistant
= TRUE
;
651 bool sendfailure
= FALSE
;
656 static char weare
[256];
658 char partbuf
[80]="data";
660 logmsg("Send response number %d part %d", req
->testno
, req
->partno
);
665 break; /* continue with business as usual */
667 #define STREAMTHIS "a string to stream 01234567890\n"
668 count
= strlen(STREAMTHIS
);
670 written
= swrite(sock
, STREAMTHIS
, count
);
671 if(written
!= (ssize_t
)count
) {
672 logmsg("Stopped streaming");
678 /* Do nothing. Sit idle. Pretend it rains. */
684 if(req
->testno
< 0) {
688 switch(req
->testno
) {
690 logmsg("Replying to QUIT");
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",
702 case DOCNUMBER_INTERNAL
:
703 logmsg("Bailing out due to internal error");
705 case DOCNUMBER_CONNECT
:
706 logmsg("Replying to CONNECT");
709 case DOCNUMBER_BADCONNECT
:
710 logmsg("Replying to a bad CONNECT");
711 buffer
= docbadconnect
;
715 logmsg("Replying to with a 404");
722 count
= strlen(buffer
);
725 char *filename
= test2file(req
->testno
);
728 sprintf(partbuf
, "data%ld", req
->partno
);
730 stream
=fopen(filename
, "rb");
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");
739 buffer
= spitout(stream
, "reply", partbuf
, &count
);
740 ptr
= (char *)buffer
;
744 /* re-open the same file again */
745 stream
=fopen(filename
, "rb");
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");
754 /* get the custom server control "commands" */
755 cmd
= (char *)spitout(stream
, "reply", "postcmd", &cmdsize
);
760 dump
= fopen(RESPONSE_DUMP
, "ab"); /* b is for windows-preparing */
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
);
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
772 if(strstr(buffer
, "swsclose") || !count
) {
774 logmsg("connection close instruction \"swsclose\" found in response");
776 if(strstr(buffer
, "swsbounce")) {
778 logmsg("enable \"swsbounce\" in the next request");
784 responsesize
= count
;
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 */
792 written
= swrite(sock
, buffer
, num
);
798 logmsg("Sent off %d bytes", written
);
800 /* write to file as well */
801 fwrite(buffer
, 1, written
, dump
);
809 } while(res
&& ((error
= ERRNO
) == EINTR
));
811 logmsg("Error closing file %s error: %d %s",
812 RESPONSE_DUMP
, error
, strerror(error
));
815 logmsg("Sending response failed. Only (%d bytes) of (%d bytes) were sent",
816 responsesize
-count
, responsesize
);
824 logmsg("Response sent (%d bytes) and written to " RESPONSE_DUMP
,
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 */
841 logmsg("Unknown command in reply command section");
843 ptr
= strchr(ptr
, '\n');
848 } while(ptr
&& *ptr
);
853 req
->open
= persistant
;
855 prevtestno
= req
->testno
;
856 prevpartno
= req
->partno
;
863 int main(int argc
, char *argv
[])
865 struct sockaddr_in me
;
867 struct sockaddr_in6 me6
;
868 #endif /* ENABLE_IPV6 */
869 curl_socket_t sock
, msgsock
;
871 unsigned short port
= DEFAULT_PORT
;
872 char *pidname
= (char *)".http.pid";
873 struct httprequest req
;
876 #ifdef CURL_SWS_FORK_ENABLED
877 bool use_fork
= FALSE
;
881 if(!strcmp("--version", argv
[arg
])) {
883 #ifdef CURL_SWS_FORK_ENABLED
896 else if(!strcmp("--pidfile", argv
[arg
])) {
899 pidname
= argv
[arg
++];
901 else if(!strcmp("--ipv6", argv
[arg
])) {
907 #ifdef CURL_SWS_FORK_ENABLED
908 else if(!strcmp("--fork", argv
[arg
])) {
916 port
= (unsigned short)atoi(argv
[arg
++]);
925 atexit(win32_cleanup
);
930 signal(SIGPIPE
, sigpipe_handler
);
932 #ifdef HAVE_SIGINTERRUPT
933 siginterrupt(SIGPIPE
, 1);
941 sock
= socket(AF_INET
, SOCK_STREAM
, 0);
944 sock
= socket(AF_INET6
, SOCK_STREAM
, 0);
947 if (CURL_SOCKET_BAD
== sock
) {
948 logmsg("Error opening socket: %d", SOCKERRNO
);
953 if (0 != setsockopt(sock
, SOL_SOCKET
, SO_REUSEADDR
,
954 (void *) &flag
, sizeof(flag
))) {
955 logmsg("setsockopt(SO_REUSEADDR) failed: %d", SOCKERRNO
);
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
));
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 */
979 logmsg("Error binding socket: %d", SOCKERRNO
);
984 if(!write_pidfile(pidname
)) {
989 logmsg("Running IPv%d version on port %d",
997 /* start accepting connections */
998 rc
= listen(sock
, 5);
1000 logmsg("listen() failed with error: %d", SOCKERRNO
);
1006 msgsock
= accept(sock
, NULL
, NULL
);
1008 if (CURL_SOCKET_BAD
== msgsock
) {
1009 printf("MAJOR ERROR: accept() failed with error: %d\n", SOCKERRNO
);
1013 set_advisor_read_lock(SERVERLOGS_LOCK
);
1015 #ifdef CURL_SWS_FORK_ENABLED
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
1022 printf("MAJOR ERROR: fork() failed!\n");
1027 /* not a fork, just set rc so the following proceeds nicely */
1029 /* 0 is returned to the child */
1032 logmsg("====> Client connect");
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.
1040 if (setsockopt(msgsock
, IPPROTO_TCP
, TCP_NODELAY
,
1041 (void *)&flag
, sizeof(flag
)) == -1) {
1042 logmsg("====> TCP_NODELAY failed");
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
;
1053 if(get_request(msgsock
, &req
))
1054 /* non-zero means error, break out of loop */
1058 /* bounce treatment requested */
1059 if((req
.testno
== prevtestno
) &&
1060 (req
.partno
== prevpartno
)) {
1062 logmsg("BOUNCE part number to %ld", req
.partno
);
1071 send_doc(msgsock
, &req
);
1073 if((req
.testno
< 0) && (req
.testno
!= DOCNUMBER_CONNECT
)) {
1074 logmsg("special request received, no persistency");
1078 logmsg("instructed to close connection after server-reply");
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");
1090 clear_advisor_read_lock(SERVERLOGS_LOCK
);
1092 if (req
.testno
== DOCNUMBER_QUIT
)
1094 #ifdef CURL_SWS_FORK_ENABLED
1101 clear_advisor_read_lock(SERVERLOGS_LOCK
);