Annotation of libwww/Library/src/HTTPServ.c, revision 2.19
2.1 frystyk 1: /* HTTPServ.c
2: ** HTTP SERVER MODULE
3: **
4: ** (c) COPYRIGHT MIT 1995.
5: ** Please first read the full copyright statement in the file COPYRIGH.
2.19 ! frystyk 6: ** @(#) $Id: HTTPServ.c,v 2.18 1996/07/02 22:55:14 frystyk Exp $
2.1 frystyk 7: **
8: ** This module implments the HTTP protocol as a state machine
9: **
10: ** History:
11: ** Dec 95 HFN Written with Christmas in my eyes
12: **
13: */
14:
15: /* Library include files */
2.14 frystyk 16: #include "sysdep.h"
2.16 frystyk 17: #include "WWWUtil.h"
18: #include "WWWCore.h"
19: #include "WWWMIME.h"
20: #include "WWWStream.h"
2.18 frystyk 21: #include "WWWTrans.h"
2.1 frystyk 22: #include "HTNetMan.h"
23: #include "HTTPUtil.h"
2.2 frystyk 24: #include "HTTPRes.h"
2.1 frystyk 25: #include "HTTPServ.h" /* Implements */
26:
27: /* Macros and other defines */
28: #define PUTC(c) (*me->target->isa->put_character)(me->target, c)
29: #define PUTS(s) (*me->target->isa->put_string)(me->target, s)
30: #define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target, b, l)
31:
32: /* Final states have negative value */
33: typedef enum _HTTPState {
2.6 frystyk 34: HTTPS_ERROR = -2,
35: HTTPS_OK = -1,
2.1 frystyk 36: HTTPS_BEGIN = 0,
2.18 frystyk 37: HTTPS_NEED_REQUEST,
38: HTTPS_LOAD_CLIENT
2.1 frystyk 39: } HTTPState;
40:
41: /* This is the context object for the this module */
42: typedef struct _https_info {
2.18 frystyk 43: HTRequest * server; /* The server request */
44: HTList * clients; /* List of client requests */
2.1 frystyk 45: HTTPState state; /* Current State of the connection */
46: } https_info;
47:
48: /* The HTTP Receive Stream */
49: struct _HTStream {
2.14 frystyk 50: const HTStreamClass * isa;
2.1 frystyk 51: HTStream * target;
52: HTRequest * request;
2.9 frystyk 53: https_info * http;
2.15 frystyk 54: HTEOLState state;
2.1 frystyk 55: HTChunk * buffer;
56: BOOL transparent;
57: };
58:
2.16 frystyk 59: struct _HTInputStream {
60: const HTInputStreamClass * isa;
61: };
62:
2.1 frystyk 63: /* ------------------------------------------------------------------------- */
2.8 frystyk 64:
65: /* ServerCleanup
66: ** -------------
67: ** This function cleans up after the request
68: ** Returns YES on OK, else NO
69: */
70: PRIVATE int ServerCleanup (HTRequest * req, HTNet * net, int status)
71: {
2.18 frystyk 72: https_info * http = (https_info *) HTNet_context(net);
73: HTStream * input = HTRequest_inputStream(req);
74: HTChannel * channel = HTNet_channel(net);
2.8 frystyk 75:
76: /* Free stream with data TO network */
2.18 frystyk 77: if (input) {
2.8 frystyk 78: if (status == HT_INTERRUPTED)
2.18 frystyk 79: (*input->isa->abort)(input, NULL);
2.8 frystyk 80: else
2.18 frystyk 81: (*input->isa->_free)(input);
82: HTRequest_setInputStream(req, NULL);
2.8 frystyk 83: }
84:
2.18 frystyk 85: /* Kill all remaining requests */
86: if (http->clients) {
87: HTList * cur = http->clients;
88: HTRequest * pres;
89: while ((pres = HTList_nextObject(cur)) != NULL)
90: HTRequest_kill(pres);
91: HTList_delete(http->clients);
92: }
93:
94: /*
95: ** Remove the net object and our own context structure for http.
96: ** Also unregister all pending requests and close the connection
97: */
98: HTChannel_setSemaphore(channel, 0);
99: HTNet_delete(net, HT_IGNORE);
100:
2.11 frystyk 101: HT_FREE(http);
2.8 frystyk 102: return YES;
103: }
104:
105: /* ------------------------------------------------------------------------- */
2.6 frystyk 106: /* REPLY STREAM */
107: /* ------------------------------------------------------------------------- */
2.1 frystyk 108:
2.6 frystyk 109: /*
110: ** This is our handle to the server reply stream when data is coming
2.9 frystyk 111: ** back from our "client" request. It is responsible for setting up the
112: ** remaining streams in order to produce a complete HTTP output.
113: ** If we have a HTTP 1.x response then forward untouched.
2.6 frystyk 114: */
2.18 frystyk 115: PRIVATE int MakeReplyPipe (HTStream * me, HTRequest * client)
2.9 frystyk 116: {
117: char * response_line = NULL;
118:
119: /* Generate the Response line */
120: {
121: HTAlertCallback *cbf = HTAlert_find(HT_A_MESSAGE);
122: if (cbf) {
123: HTAlertPar * reply = HTAlert_newReply();
124: if ((*cbf)(client, HT_A_MESSAGE, HT_MSG_NULL, NULL,
2.18 frystyk 125: HTRequest_error(client), reply))
2.9 frystyk 126: response_line = HTAlert_replyMessage(reply);
127: HTAlert_deleteReply(reply);
128: }
129:
130: /* Output the response */
131: if (response_line) {
132: PUTS(response_line);
2.11 frystyk 133: HT_FREE(response_line);
2.9 frystyk 134: } else {
2.18 frystyk 135: PUTS(HTTP_VERSION);
136: PUTS(" 500 Internal");
2.9 frystyk 137: PUTC(CR);
138: PUTC(LF);
139: }
140: }
141:
142: /*
2.19 ! frystyk 143: ** We now have to create the rest of the response stream. We see whether
! 144: ** there is a data object or not by looking at the Content Type of the
! 145: ** client anchor.
2.9 frystyk 146: */
2.19 ! frystyk 147: {
! 148: HTParentAnchor * anchor = HTRequest_anchor(client);
! 149: HTFormat format = HTAnchor_format(anchor);
! 150: me->target = (format == WWW_UNKNOWN) ?
! 151: HTTPResponse_new(client, me->target, YES) :
! 152: HTMIMERequest_new(client,
! 153: HTTPResponse_new(client,me->target,NO), YES);
! 154: }
2.18 frystyk 155: return HT_OK;
2.9 frystyk 156: }
2.6 frystyk 157:
2.14 frystyk 158: PRIVATE int HTTPReply_put_block (HTStream * me, const char * b, int l)
2.1 frystyk 159: {
2.9 frystyk 160: if (me->transparent)
161: return b ? PUTBLOCK(b, l) : HT_OK;
162: else {
2.18 frystyk 163: MakeReplyPipe(me, me->request);
164: me->transparent = YES;
2.9 frystyk 165: return b ? PUTBLOCK(b, l) : HT_OK;
166: }
2.6 frystyk 167: }
168:
2.14 frystyk 169: PRIVATE int HTTPReply_put_string (HTStream * me, const char * s)
2.6 frystyk 170: {
2.9 frystyk 171: return HTTPReply_put_block(me, s, strlen(s));
2.6 frystyk 172: }
173:
174: PRIVATE int HTTPReply_put_character (HTStream * me, char c)
175: {
2.9 frystyk 176: return HTTPReply_put_block(me, &c, 1);
2.6 frystyk 177: }
178:
179: PRIVATE int HTTPReply_flush (HTStream * me)
180: {
2.9 frystyk 181: int status = HTTPReply_put_block(me, NULL, 0);
182: return status==HT_OK ? (*me->target->isa->flush)(me->target) : status;
2.6 frystyk 183: }
2.1 frystyk 184:
2.6 frystyk 185: PRIVATE int HTTPReply_free (HTStream * me)
186: {
2.18 frystyk 187: int status = HTTPReply_put_block(me, NULL, 0);
2.12 eric 188: if (STREAM_TRACE) HTTrace("HTTPReply... Freeing server stream\n");
2.18 frystyk 189: return status==HT_OK ? (*me->target->isa->_free)(me->target) : status;
2.6 frystyk 190: }
191:
192: PRIVATE int HTTPReply_abort (HTStream * me, HTList * e)
193: {
2.12 eric 194: if (STREAM_TRACE) HTTrace("HTTPReply... ABORTING\n");
2.18 frystyk 195: if (me->target) (*me->target->isa->abort)(me->target, e);
2.11 frystyk 196: HT_FREE(me);
2.6 frystyk 197: return HT_ERROR;
198: }
199:
200: /* HTTPReply Stream
201: ** -----------------
202: */
2.14 frystyk 203: PRIVATE const HTStreamClass HTTPReplyClass =
2.6 frystyk 204: {
205: "HTTPReply",
206: HTTPReply_flush,
207: HTTPReply_free,
208: HTTPReply_abort,
209: HTTPReply_put_character,
210: HTTPReply_put_string,
211: HTTPReply_put_block
212: };
2.1 frystyk 213:
2.18 frystyk 214: PRIVATE HTStream * HTTPReply_new (HTRequest * request, https_info * http,
2.9 frystyk 215: HTStream * target)
2.6 frystyk 216: {
2.11 frystyk 217: HTStream * me;
218: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
219: HT_OUTOFMEM("HTTPReply_new");
2.6 frystyk 220: me->isa = &HTTPReplyClass;
2.18 frystyk 221: me->request = request;
222: me->http = http;
2.6 frystyk 223: me->target = target;
2.18 frystyk 224: if (STREAM_TRACE) HTTrace("HTTP Reply.. Stream %p created\n", me);
2.6 frystyk 225: return me;
2.1 frystyk 226: }
227:
2.6 frystyk 228: /* ------------------------------------------------------------------------- */
229: /* RECEIVE STREAM */
230: /* ------------------------------------------------------------------------- */
231:
2.1 frystyk 232: /*
233: ** Scan the request line for METHOD, URI and VERSION
234: ** Returns: HT_OK if 1.x request and OK
235: ** HT_LOADED if 0.9 request and OK
236: ** HT_ERROR if invalid request line
237: */
238: PRIVATE int ParseRequest (HTStream * me)
239: {
2.18 frystyk 240: HTRequest * client = HTList_firstObject(me->http->clients);
2.2 frystyk 241: char * line = HTChunk_data(me->buffer);
2.6 frystyk 242: char * method_str = HTNextField(&line);
243: char * request_uri = HTNextField(&line);
244: char * version_str = HTNextField(&line);
2.18 frystyk 245: HTMethod method;
2.6 frystyk 246:
247: /* Check if method is allowed */
2.18 frystyk 248: if (!method_str || (method = HTMethod_enum(method_str))==METHOD_INVALID) {
249: HTRequest_addError(client, ERR_FATAL, NO, HTERR_NOT_ALLOWED,
2.6 frystyk 250: NULL, 0, "ParseRequest");
251: return HT_ERROR;
252: }
2.18 frystyk 253: HTRequest_setMethod(client, method);
2.2 frystyk 254:
2.6 frystyk 255: /* Find an anchor for the request URI */
256: if (request_uri) {
257: char * uri = HTParse(request_uri, "file:", PARSE_ALL);
2.18 frystyk 258: HTRequest_setAnchor(client, HTAnchor_findAddress(uri));
2.11 frystyk 259: HT_FREE(uri);
2.1 frystyk 260: } else {
2.18 frystyk 261: HTRequest_addError(client, ERR_FATAL, NO, HTERR_BAD_REQUEST,
2.1 frystyk 262: NULL, 0, "ParseRequest");
263: return HT_ERROR;
264: }
265:
2.6 frystyk 266: /* Get ready to get the rest of the request */
267: if (version_str) {
2.18 frystyk 268: me->target = HTStreamStack(WWW_MIME_HEAD,
269: HTRequest_debugFormat(client),
270: HTRequest_debugStream(client),
271: client, NO);
2.1 frystyk 272: return HT_OK;
2.2 frystyk 273: } else {
2.18 frystyk 274: HTRequest_addError(client, ERR_FATAL, NO, HTERR_BAD_VERSION,
275: NULL, 0, "ParseRequest");
276: return HT_ERROR;
2.1 frystyk 277: }
278: }
279:
280: /*
281: ** Searches for HTTP Request Line before going into transparent mode
282: */
2.14 frystyk 283: PRIVATE int HTTPReceive_put_block (HTStream * me, const char * b, int l)
2.1 frystyk 284: {
2.6 frystyk 285: if (!me->transparent) {
2.14 frystyk 286: const char *p=b;
2.6 frystyk 287: while (l>0 && *p!=CR && *p!=LF) l--, p++;
288: HTChunk_putb(me->buffer, b, p-b);
289: if (*p==CR || *p==LF) {
290: int status = ParseRequest(me);
291: HTChunk_clear(me->buffer);
292: if (status != HT_OK) return status;
293: me->transparent = YES;
294: b=p;
295: }
296: }
297: if (l > 0) {
298: int status = PUTBLOCK(b, l);
299: if (status == HT_LOADED) me->transparent = NO;
300: return status;
2.1 frystyk 301: }
302: return HT_OK;
303: }
304:
2.14 frystyk 305: PRIVATE int HTTPReceive_put_string (HTStream * me, const char * s)
2.1 frystyk 306: {
307: return HTTPReceive_put_block(me, s, (int) strlen(s));
308: }
309:
310: PRIVATE int HTTPReceive_put_character (HTStream * me, char c)
311: {
312: return HTTPReceive_put_block(me, &c, 1);
313: }
314:
315: PRIVATE int HTTPReceive_flush (HTStream * me)
316: {
317: return (*me->target->isa->flush)(me->target);
318: }
319:
320: PRIVATE int HTTPReceive_free (HTStream * me)
321: {
322: int status = HT_OK;
323: if (me->target) {
324: if ((status = (*me->target->isa->_free)(me->target)) == HT_WOULD_BLOCK)
325: return HT_WOULD_BLOCK;
326: }
2.2 frystyk 327: HTChunk_delete(me->buffer);
2.11 frystyk 328: HT_FREE(me);
2.1 frystyk 329: return status;
330: }
331:
332: PRIVATE int HTTPReceive_abort (HTStream * me, HTList * e)
333: {
2.18 frystyk 334: if (me->target) (*me->target->isa->abort)(me->target, e);
2.2 frystyk 335: HTChunk_delete(me->buffer);
2.11 frystyk 336: HT_FREE(me);
2.12 eric 337: if (PROT_TRACE) HTTrace("HTTPReceive. ABORTING...\n");
2.1 frystyk 338: return HT_ERROR;
339: }
340:
341: /* HTTPReceive Stream
342: ** -----------------
343: */
2.14 frystyk 344: PRIVATE const HTStreamClass HTTPReceiveClass =
2.1 frystyk 345: {
346: "HTTPReceive",
347: HTTPReceive_flush,
348: HTTPReceive_free,
349: HTTPReceive_abort,
350: HTTPReceive_put_character,
351: HTTPReceive_put_string,
352: HTTPReceive_put_block
353: };
354:
355: PRIVATE HTStream * HTTPReceive_new (HTRequest * request, https_info * http)
356: {
2.11 frystyk 357: HTStream * me;
358: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
359: HT_OUTOFMEM("HTTPReceive_new");
2.1 frystyk 360: me->isa = &HTTPReceiveClass;
361: me->request = request;
2.9 frystyk 362: me->http = http;
2.1 frystyk 363: me->state = EOL_BEGIN;
2.2 frystyk 364: me->buffer = HTChunk_new(128); /* Sufficiant for most URLs */
2.18 frystyk 365: if (STREAM_TRACE) HTTrace("HTTP Request Stream %p created\n", me);
2.1 frystyk 366: return me;
367: }
368:
369: /* ------------------------------------------------------------------------- */
370:
371: /* HTServHTTP
372: ** ----------
373: ** Serv Document using HTTP.
374: ** returns HT_ERROR or HT_OK
375: */
376: PUBLIC int HTServHTTP (SOCKET soc, HTRequest * request, SockOps ops)
377: {
2.18 frystyk 378: HTNet * net = HTRequest_net(request);
2.1 frystyk 379: int status = HT_ERROR;
380: https_info * http; /* Specific protocol information */
2.18 frystyk 381: if (!net || !request) {
382: if (PROT_TRACE) HTTrace("Serv HTTP... Invalid argument\n");
383: return HT_ERROR;
384: }
385:
2.1 frystyk 386: /*
387: ** Initiate a new https object and bind to request object
388: ** This is actually state HTTPS_BEGIN, but it can't be in the state
389: ** machine as we need the object first (chicken and egg problem).
390: */
391: if (ops == FD_NONE) {
2.18 frystyk 392: if (PROT_TRACE) HTTrace("Serv HTTP... on socket %d\n", soc);
2.11 frystyk 393: if ((http = (https_info *) HT_CALLOC(1, sizeof(https_info))) == NULL)
394: HT_OUTOFMEM("HTServHTTP");
2.18 frystyk 395: http->server = request;
396: http->state = HTTPS_BEGIN;
397: http->clients = HTList_new();
398: HTNet_setContext(net, http);
399:
400: /*
401: ** Create the stream pipe FROM the channel to the server request.
402: */
403: HTNet_getInput(net, HTTPReceive_new(request, http), NULL, 0);
404: HTRequest_setOutputConnected(request, YES);
2.1 frystyk 405: http->state = HTTPS_BEGIN;
2.18 frystyk 406:
2.1 frystyk 407: } else if (ops == FD_CLOSE) { /* Interrupted */
2.6 frystyk 408: ServerCleanup(request, net, HT_INTERRUPTED);
2.1 frystyk 409: return HT_OK;
410: } else
2.18 frystyk 411: http = (https_info *) HTNet_context(net); /* Get existing copy */
2.1 frystyk 412:
413: /* Now jump into the machine. We know the state from the previous run */
414: while (1) {
415: switch (http->state) {
2.18 frystyk 416: case HTTPS_BEGIN:
417: {
418: /*
419: ** Create the request to handle the request and inherit the old
420: ** context
421: */
422: HTRequest * client = HTRequest_new();
423: void * context = HTRequest_context(request);
424: if (context) HTRequest_setContext(client, context);
2.19 ! frystyk 425: HTRequest_setOutputConnected(client, NO);
! 426: HTRequest_setGnHd(client, HTRequest_gnHd(request));
! 427: HTRequest_setRsHd(client, HTRequest_rsHd(request));
! 428: HTRequest_setEnHd(client, HTRequest_enHd(request));
2.18 frystyk 429: HTList_addObject(http->clients, client);
430:
431: /*
432: ** Create the HTTP output stream for generating the reply
433: ** FROM the client request to the channel
434: */
435: {
436: HTOutputStream * output = HTNet_getOutput(net, NULL, 0);
437: HTStream * app = HTTPReply_new(client, http,(HTStream*)output);
438: HTRequest_setOutputStream(client, app);
439: HTRequest_setOutputFormat(client, WWW_SOURCE);
440: }
441: http->state = HTTPS_NEED_REQUEST;
442: }
443: break;
2.1 frystyk 444:
2.18 frystyk 445: case HTTPS_NEED_REQUEST:
2.3 frystyk 446: if (ops == FD_READ || ops == FD_NONE) {
2.16 frystyk 447: status = (*net->input->isa->read)(net->input);
2.3 frystyk 448: if (status == HT_WOULD_BLOCK)
449: return HT_OK;
2.18 frystyk 450: else if (status == HT_CLOSED)
2.6 frystyk 451: http->state = HTTPS_OK;
2.18 frystyk 452: else if (status==HT_LOADED || status==HT_PAUSE) {
453: http->state = HTTPS_LOAD_CLIENT;
454: } else
2.3 frystyk 455: http->state = HTTPS_ERROR;
456: } else
2.2 frystyk 457: http->state = HTTPS_ERROR;
2.1 frystyk 458: break;
459:
2.18 frystyk 460: case HTTPS_LOAD_CLIENT:
461: {
462: HTRequest * client = HTList_removeFirstObject(http->clients);
463: HTLoad(client, NO);
464: http->state = HTTPS_BEGIN;
465: break;
466: }
467:
468: case HTTPS_OK:
2.8 frystyk 469: ServerCleanup(request, net, HT_IGNORE);
2.18 frystyk 470: return HT_OK;
2.6 frystyk 471:
2.18 frystyk 472: case HTTPS_ERROR:
2.6 frystyk 473: ServerCleanup(request, net, HT_ERROR);
2.1 frystyk 474: return HT_OK;
475: }
476: }
2.3 frystyk 477: }
2.6 frystyk 478:
Webmaster