1 /* Copyright information is at the end of the file. */
10 #include "mallocvar.h"
11 #include "xmlrpc-c/util_int.h"
12 #include "xmlrpc-c/string_int.h"
13 #include "xmlrpc-c/sleep_int.h"
14 #include "xmlrpc-c/abyss.h"
21 /*********************************************************************
23 *********************************************************************/
25 static TThreadProc connJob;
28 connJob(void * const userHandle) {
29 /*----------------------------------------------------------------------------
30 This is the root function for a thread that processes a connection
31 (performs HTTP transactions).
32 -----------------------------------------------------------------------------*/
33 TConn * const connectionP = userHandle;
35 (connectionP->job)(connectionP);
37 connectionP->finished = TRUE;
38 /* Note that if we are running in a forked process, setting
39 connectionP->finished has no effect, because it's just our own
40 copy of *connectionP. In this case, Parent must update his own
41 copy based on a SIGCHLD signal that the OS will generate right
51 connDone(TConn * const connectionP) {
53 /* In the forked case, this is designed to run in the parent
54 process after the child has terminated.
56 connectionP->finished = TRUE;
58 if (connectionP->done)
59 connectionP->done(connectionP);
64 static TThreadDoneFn threadDone;
67 threadDone(void * const userHandle) {
69 TConn * const connectionP = userHandle;
71 connDone(connectionP);
77 makeThread(TConn * const connectionP,
78 enum abyss_foreback const foregroundBackground,
79 abyss_bool const useSigchld,
80 const char ** const errorP) {
82 switch (foregroundBackground) {
83 case ABYSS_FOREGROUND:
84 connectionP->hasOwnThread = FALSE;
87 case ABYSS_BACKGROUND: {
89 connectionP->hasOwnThread = TRUE;
90 ThreadCreate(&connectionP->threadP, connectionP,
91 &connJob, &threadDone, useSigchld,
94 xmlrpc_asprintf(errorP, "Unable to create thread to "
95 "process connection. %s", error);
96 xmlrpc_strfree(error);
106 ConnCreate(TConn ** const connectionPP,
107 TServer * const serverP,
108 TSocket * const connectedSocketP,
109 TThreadProc * const job,
110 TThreadDoneFn * const done,
111 enum abyss_foreback const foregroundBackground,
112 abyss_bool const useSigchld,
113 const char ** const errorP) {
114 /*----------------------------------------------------------------------------
115 Create an HTTP connection.
117 A connection carries one or more HTTP transactions (request/response).
119 'connectedSocketP' transports the requests and responses.
121 The connection handles those HTTP requests.
123 The connection handles the requests primarily by running the
124 function 'job' once. Some connections can do that autonomously, as
125 soon as the connection is created. Others don't until Caller
126 subsequently calls ConnProcess. Some connections complete the
127 processing before ConnProcess return, while others may run the
128 connection asynchronously to the creator, in the background, via a
129 TThread thread. 'foregroundBackground' determines which.
131 'job' calls methods of the connection to get requests and send
134 Some time after the HTTP transactions are all done, 'done' gets
135 called in some context.
136 -----------------------------------------------------------------------------*/
139 MALLOCVAR(connectionP);
141 if (connectionP == NULL)
142 xmlrpc_asprintf(errorP, "Unable to allocate memory for a connection "
146 uint16_t peerPortNumber;
148 connectionP->server = serverP;
149 connectionP->socketP = connectedSocketP;
150 connectionP->buffersize = 0;
151 connectionP->bufferpos = 0;
152 connectionP->finished = FALSE;
153 connectionP->job = job;
154 connectionP->done = done;
155 connectionP->inbytes = 0;
156 connectionP->outbytes = 0;
157 connectionP->trace = getenv("ABYSS_TRACE_CONN");
159 SocketGetPeerName(connectedSocketP,
160 &connectionP->peerip, &peerPortNumber, &success);
163 makeThread(connectionP, foregroundBackground, useSigchld, errorP);
165 xmlrpc_asprintf(errorP, "Failed to get peer name from socket.");
167 *connectionPP = connectionP;
173 ConnProcess(TConn * const connectionP) {
174 /*----------------------------------------------------------------------------
175 Drive the main processing of a connection -- run the connection's
176 "job" function, which should read HTTP requests from the connection
177 and send HTTP responses.
179 If we succeed, we guarantee the connection's "done" function will get
180 called some time after all processing is complete. It might be before
181 we return or some time after. If we fail, we guarantee the "done"
182 function will not be called.
183 -----------------------------------------------------------------------------*/
186 if (connectionP->hasOwnThread) {
187 /* There's a background thread to handle this connection. Set
190 retval = ThreadRun(connectionP->threadP);
192 /* No background thread. We just handle it here while Caller waits. */
193 (connectionP->job)(connectionP);
194 connDone(connectionP);
203 ConnWaitAndRelease(TConn * const connectionP) {
204 if (connectionP->hasOwnThread)
205 ThreadWaitAndRelease(connectionP->threadP);
213 ConnKill(TConn * connectionP) {
214 connectionP->finished = TRUE;
215 return ThreadKill(connectionP->threadP);
221 ConnReadInit(TConn * const connectionP) {
222 if (connectionP->buffersize>connectionP->bufferpos) {
223 connectionP->buffersize -= connectionP->bufferpos;
224 memmove(connectionP->buffer,
225 connectionP->buffer+connectionP->bufferpos,
226 connectionP->buffersize);
227 connectionP->bufferpos = 0;
229 connectionP->buffersize=connectionP->bufferpos = 0;
231 connectionP->inbytes=connectionP->outbytes = 0;
237 traceBuffer(const char * const label,
238 const char * const buffer,
239 unsigned int const size) {
241 unsigned int nonPrintableCount;
244 nonPrintableCount = 0; /* Initial value */
246 for (i = 0; i < size; ++i) {
247 if (!isprint(buffer[i]) && buffer[i] != '\n' && buffer[i] != '\r')
250 if (nonPrintableCount > 0)
251 fprintf(stderr, "%s contains %u nonprintable characters.\n",
252 label, nonPrintableCount);
254 fprintf(stderr, "%s:\n", label);
255 fprintf(stderr, "%.*s\n", (int)size, buffer);
261 traceSocketRead(TConn * const connectionP,
262 unsigned int const size) {
264 if (connectionP->trace)
265 traceBuffer("READ FROM SOCKET",
266 connectionP->buffer + connectionP->buffersize, size);
272 traceSocketWrite(TConn * const connectionP,
273 const char * const buffer,
274 unsigned int const size,
275 abyss_bool const failed) {
277 if (connectionP->trace) {
278 const char * const label =
279 failed ? "FAILED TO WRITE TO SOCKET" : "WROTE TO SOCKET";
280 traceBuffer(label, buffer, size);
287 bufferSpace(TConn * const connectionP) {
289 return BUFFER_SIZE - connectionP->buffersize;
295 ConnRead(TConn * const connectionP,
296 uint32_t const timeout) {
297 /*----------------------------------------------------------------------------
298 Read some stuff on connection *connectionP from the socket.
300 Don't wait more than 'timeout' seconds for data to arrive. Fail if
301 nothing arrives within that time.
302 -----------------------------------------------------------------------------*/
303 time_t const deadline = time(NULL) + timeout;
305 abyss_bool cantGetData;
311 while (!gotData && !cantGetData) {
312 int const timeLeft = deadline - time(NULL);
319 rc = SocketWait(connectionP->socketP, TRUE, FALSE,
327 bytesAvail = SocketAvailableReadBytes(connectionP->socketP);
332 uint32_t const bytesToRead =
333 MIN(bytesAvail, bufferSpace(connectionP)-1);
337 bytesRead = SocketRead(
338 connectionP->socketP,
339 connectionP->buffer + connectionP->buffersize,
342 traceSocketRead(connectionP, bytesRead);
343 connectionP->inbytes += bytesRead;
344 connectionP->buffersize += bytesRead;
345 connectionP->buffer[connectionP->buffersize] = '\0';
361 ConnWrite(TConn * const connectionP,
362 const void * const buffer,
363 uint32_t const size) {
367 SocketWrite(connectionP->socketP, buffer, size, &failed);
369 traceSocketWrite(connectionP, buffer, size, failed);
372 connectionP->outbytes += size;
380 ConnWriteFromFile(TConn * const connectionP,
382 uint64_t const start,
385 uint32_t const buffersize,
386 uint32_t const rate) {
387 /*----------------------------------------------------------------------------
388 Write the contents of the file stream *fileP, from offset 'start'
389 up through 'last', to the HTTP connection *connectionP.
391 Meter the reading so as not to read more than 'rate' bytes per second.
393 Use the 'bufferSize' bytes at 'buffer' as an internal buffer for this.
394 -----------------------------------------------------------------------------*/
398 uint32_t readChunkSize;
401 readChunkSize = MIN(buffersize, rate); /* One second's worth */
402 waittime = (1000 * buffersize) / rate;
404 readChunkSize = buffersize;
408 success = FileSeek(fileP, start, SEEK_SET);
412 uint64_t const totalBytesToRead = last - start + 1;
415 bytesread = 0; /* initial value */
417 while (bytesread < totalBytesToRead) {
418 uint64_t const bytesLeft = totalBytesToRead - bytesread;
419 uint64_t const bytesToRead = MIN(readChunkSize, bytesLeft);
421 uint64_t bytesReadThisTime;
423 bytesReadThisTime = FileRead(fileP, buffer, bytesToRead);
424 bytesread += bytesReadThisTime;
426 if (bytesReadThisTime > 0)
427 ConnWrite(connectionP, buffer, bytesReadThisTime);
432 xmlrpc_millisecond_sleep(waittime);
434 retval = (bytesread >= totalBytesToRead);
442 processHeaderLine(char * const start,
443 const char * const headerStart,
444 TConn * const connectionP,
445 time_t const deadline,
446 abyss_bool * const gotHeaderP,
448 abyss_bool * const errorP) {
449 /*----------------------------------------------------------------------------
450 If there's enough data in the buffer, process a line of HTTP
453 It is part of a header that starts at 'headerStart' and has been
454 previously processed up to the line starting at 'start'. The data
455 in the buffer is terminated by a NUL.
457 Return as *nextP the location of the next header line to process
458 (same as 'start' if we didn't find a line to process).
461 -----------------------------------------------------------------------------*/
462 abyss_bool gotHeader;
468 gotHeader = FALSE; /* initial assumption */
470 lfPos = strchr(p, LF);
472 if ((*p != LF) && (*p != CR)) {
473 /* We're looking at a non-empty line */
474 if (*(lfPos+1) == '\0') {
475 /* There's nothing in the buffer after the line, so we
476 don't know if there's a continuation line coming.
479 int const timeLeft = deadline - time(NULL);
481 *errorP = !ConnRead(connectionP, timeLeft);
484 p = lfPos; /* Point to LF */
486 /* If the next line starts with whitespace, it's a
487 continuation line, so blank out the line
488 delimiter (LF or CRLF) so as to join the next
491 if ((*(p+1) == ' ') || (*(p+1) == '\t')) {
492 if (p > headerStart && *(p-1) == CR)
499 /* We're looking at an empty line (i.e. what marks the
502 p = lfPos; /* Point to LF */
508 /* 'p' points to the final LF */
510 /* Replace the LF or the CR in CRLF with NUL, so as to terminate
511 the string at 'headerStart' that is the full header.
513 if (p > headerStart && *(p-1) == CR)
514 *(p-1) = '\0'; /* NUL out CR in CRLF */
516 *p = '\0'; /* NUL out LF */
518 ++p; /* Point to next line in buffer */
520 *gotHeaderP = gotHeader;
527 ConnReadHeader(TConn * const connectionP,
528 char ** const headerP) {
529 /*----------------------------------------------------------------------------
530 Read an HTTP header on connection *connectionP.
532 An HTTP header is basically a line, except that if a line starts
533 with white space, it's a continuation of the previous line. A line
534 is delimited by either LF or CRLF.
536 In the course of reading, we read at least one character past the
537 line delimiter at the end of the header; we may read much more. We
538 leave everything after the header (and its line delimiter) in the
539 internal buffer, with the buffer pointer pointing to it.
541 We use stuff already in the internal buffer (perhaps left by a
542 previous call to this subroutine) before reading any more from from
545 Return as *headerP the header value. This is in the connection's
546 internal buffer. This contains no line delimiters.
547 -----------------------------------------------------------------------------*/
548 uint32_t const deadline = time(NULL) + connectionP->server->srvP->timeout;
554 abyss_bool gotHeader;
556 p = connectionP->buffer + connectionP->bufferpos;
562 while (!gotHeader && !error) {
563 int const timeLeft = deadline - time(NULL);
568 if (p >= connectionP->buffer + connectionP->buffersize
570 /* There is no line yet in the buffer.
571 Need more data from the socket to chew on
573 error = !ConnRead(connectionP, timeLeft);
576 assert(connectionP->buffer + connectionP->buffersize > p);
577 processHeaderLine(p, headerStart, connectionP, deadline,
578 &gotHeader, &p, &error);
583 /* We've consumed this part of the buffer (but be careful --
584 you can't reuse that part of the buffer because the string
585 we're returning is in it!
587 connectionP->bufferpos += p - headerStart;
588 *headerP = headerStart;
599 ConnServer(TConn * const connectionP) {
600 return connectionP->server;
605 /*******************************************************************************
609 ** This file is part of the ABYSS Web server project.
611 ** Copyright (C) 2000 by Moez Mahfoudh <mmoez@bigfoot.com>.
612 ** All rights reserved.
614 ** Redistribution and use in source and binary forms, with or without
615 ** modification, are permitted provided that the following conditions
617 ** 1. Redistributions of source code must retain the above copyright
618 ** notice, this list of conditions and the following disclaimer.
619 ** 2. Redistributions in binary form must reproduce the above copyright
620 ** notice, this list of conditions and the following disclaimer in the
621 ** documentation and/or other materials provided with the distribution.
622 ** 3. The name of the author may not be used to endorse or promote products
623 ** derived from this software without specific prior written permission.
625 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
626 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
627 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
628 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
629 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
630 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
631 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
632 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
633 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
634 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
637 ******************************************************************************/