Annotation of libwww/Library/src/HTTP.c, revision 1.181
1.74 frystyk 1: /* HTTP.c
2: ** MULTITHREADED IMPLEMENTATION OF HTTP CLIENT
1.2 timbl 3: **
1.84 frystyk 4: ** (c) COPYRIGHT MIT 1995.
1.74 frystyk 5: ** Please first read the full copyright statement in the file COPYRIGH.
1.181 ! frystyk 6: ** @(#) $Id: HTTP.c,v 1.180 1999年02月27日 01:28:40 frystyk Exp $
1.74 frystyk 7: **
8: ** This module implments the HTTP protocol as a state machine
1.55 frystyk 9: **
10: ** History:
1.59 frystyk 11: ** < May 24 94 ?? Unknown - but obviously written
1.56 frystyk 12: ** May 24 94 HF Made reentrent and cleaned up a bit. Implemented
13: ** Forward, redirection, error handling and referer field
1.67 duns 14: ** 8 Jul 94 FM Insulate free() from _free structure element.
1.71 frystyk 15: ** Jul 94 HFN Written on top of HTTP.c, Henrik Frystyk
1.55 frystyk 16: **
1.1 timbl 17: */
18:
1.78 frystyk 19: /* Library include files */
1.161 frystyk 20: #include "wwwsys.h"
1.123 frystyk 21: #include "WWWUtil.h"
22: #include "WWWCore.h"
1.177 frystyk 23: #include "HTHeader.h"
24: #include "HTMIMERq.h"
1.94 frystyk 25: #include "HTReqMan.h"
1.179 frystyk 26: #include "HTNetMan.h"
1.109 frystyk 27: #include "HTTPUtil.h"
1.80 frystyk 28: #include "HTTPReq.h"
1.55 frystyk 29: #include "HTTP.h" /* Implements */
30:
31: /* Macros and other defines */
1.94 frystyk 32: #ifndef HTTP_PORT
33: #define HTTP_PORT 80 /* Allocated to http by Jon Postel/ISI 24-Jan-92 */
34: #endif
35:
1.71 frystyk 36: #define PUTC(c) (*me->target->isa->put_character)(me->target, c)
37: #define PUTS(s) (*me->target->isa->put_string)(me->target, s)
38: #define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target, b, l)
39: #define FREE_TARGET (*me->target->isa->_free)(me->target)
1.74 frystyk 40: #define ABORT_TARGET (*me->target->isa->abort)(me->target, e)
1.2 timbl 41:
1.178 frystyk 42: #ifdef HTDEBUG
1.177 frystyk 43: #include "WWWStream.h"
1.147 frystyk 44: #define HTTP_OUTPUT "w3chttp.out"
1.162 frystyk 45: PRIVATE FILE * htfp = NULL;
1.140 frystyk 46: #endif
1.139 frystyk 47:
1.59 frystyk 48: /* Type definitions and global variables etc. local to this module */
1.94 frystyk 49:
50: /* Final states have negative value */
1.59 frystyk 51: typedef enum _HTTPState {
1.167 frystyk 52: HTTP_KILL_PIPE = -4,
1.141 frystyk 53: HTTP_RECOVER_PIPE = -3,
1.134 frystyk 54: HTTP_ERROR = -2,
55: HTTP_OK = -1,
1.71 frystyk 56: HTTP_BEGIN = 0,
1.144 frystyk 57: HTTP_NEED_STREAM,
1.141 frystyk 58: HTTP_CONNECTED
1.59 frystyk 59: } HTTPState;
1.55 frystyk 60:
1.94 frystyk 61: /* This is the context structure for the this module */
1.55 frystyk 62: typedef struct _http_info {
1.81 frystyk 63: HTTPState state; /* Current State of the connection */
64: HTTPState next; /* Next state */
1.134 frystyk 65: int result; /* Result to report to the after filter */
1.139 frystyk 66: BOOL lock; /* Block for writing */
1.141 frystyk 67: HTNet * net;
1.157 frystyk 68: HTRequest * request;
69: HTTimer * timer;
1.165 frystyk 70: BOOL usedTimer;
1.181 ! frystyk 71: BOOL repetitive_writing;
1.55 frystyk 72: } http_info;
73:
1.88 frystyk 74: #define MAX_STATUS_LEN 100 /* Max nb of chars to check StatusLine */
1.55 frystyk 75:
1.71 frystyk 76: struct _HTStream {
1.119 frystyk 77: const HTStreamClass * isa;
1.71 frystyk 78: HTStream * target;
1.157 frystyk 79: HTStream * info_target; /* For 100 codes */
1.71 frystyk 80: HTRequest * request;
81: http_info * http;
1.121 frystyk 82: HTEOLState state;
1.71 frystyk 83: BOOL transparent;
1.150 frystyk 84: BOOL cont;
1.81 frystyk 85: char * version; /* Should we save this? */
1.71 frystyk 86: int status;
1.81 frystyk 87: char * reason;
1.71 frystyk 88: char buffer[MAX_STATUS_LEN+1];
1.80 frystyk 89: int buflen;
1.146 frystyk 90: int startLen;/* buflen when put_block was called */
1.71 frystyk 91: };
1.21 luotonen 92:
1.123 frystyk 93: struct _HTInputStream {
94: const HTInputStreamClass * isa;
95: };
96:
1.169 frystyk 97: /* How long to wait before writing the body in PUT and POST requests */
98: #define DEFAULT_FIRST_WRITE_DELAY 2000
99: #define DEFAULT_SECOND_WRITE_DELAY 3000
1.181 ! frystyk 100: #define DEFAULT_REPEAT_WRITE 30
1.169 frystyk 101:
102: PRIVATE ms_t HTFirstWriteDelay = DEFAULT_FIRST_WRITE_DELAY;
103: PRIVATE ms_t HTSecondWriteDelay = DEFAULT_SECOND_WRITE_DELAY;
1.181 ! frystyk 104: PRIVATE ms_t HTRepeatWrite = DEFAULT_REPEAT_WRITE;
1.169 frystyk 105:
1.147 frystyk 106: #ifdef HT_NO_PIPELINING
1.156 frystyk 107: PRIVATE HTTPConnectionMode ConnectionMode = HTTP_11_NO_PIPELINING;
1.147 frystyk 108: #else
1.156 frystyk 109: #ifdef HT_MUX
110: PRIVATE HTTPConnectionMode ConnectionMode = HTTP_11_MUX;
111: #else
112: #ifdef HT_FORCE_10
113: PRIVATE HTTPConnectionMode ConnectionMode = HTTP_FORCE_10;
114: #else
115: PRIVATE HTTPConnectionMode ConnectionMode = HTTP_11_PIPELINING;
116: #endif
117: #endif
1.147 frystyk 118: #endif
119:
1.71 frystyk 120: /* ------------------------------------------------------------------------- */
121: /* Help Functions */
122: /* ------------------------------------------------------------------------- */
1.21 luotonen 123:
1.94 frystyk 124: /* HTTPCleanup
125: ** -----------
1.55 frystyk 126: ** This function closes the connection and frees memory.
1.94 frystyk 127: ** Returns YES on OK, else NO
1.1 timbl 128: */
1.94 frystyk 129: PRIVATE int HTTPCleanup (HTRequest *req, int status)
1.1 timbl 130: {
1.126 frystyk 131: HTNet * net = HTRequest_net(req);
132: http_info * http = (http_info *) HTNet_context(net);
133: HTStream * input = HTRequest_inputStream(req);
1.80 frystyk 134:
1.178 frystyk 135: HTTRACE(PROT_TRACE, "HTTP Clean.. Called with status %d, net %p\n" _ status _ net);
1.144 frystyk 136:
1.164 frystyk 137: if (status == HT_INTERRUPTED) {
138: HTAlertCallback * cbf = HTAlert_find(HT_PROG_INTERRUPT);
139: if (cbf) (*cbf)(req, HT_PROG_INTERRUPT,
140: HT_MSG_NULL, NULL, NULL, NULL);
1.166 frystyk 141: } else if (status == HT_TIMEOUT) {
142: HTAlertCallback * cbf = HTAlert_find(HT_PROG_TIMEOUT);
143: if (cbf) (*cbf)(req, HT_PROG_TIMEOUT,
144: HT_MSG_NULL, NULL, NULL, NULL);
1.164 frystyk 145: }
1.166 frystyk 146:
1.94 frystyk 147: /* Free stream with data TO network */
1.167 frystyk 148: if (input) {
1.166 frystyk 149: if (status==HT_INTERRUPTED || status==HT_RECOVER_PIPE || status==HT_TIMEOUT)
1.126 frystyk 150: (*input->isa->abort)(input, NULL);
1.94 frystyk 151: else
1.126 frystyk 152: (*input->isa->_free)(input);
153: HTRequest_setInputStream(req, NULL);
1.94 frystyk 154: }
1.88 frystyk 155:
1.144 frystyk 156: /*
1.157 frystyk 157: ** Remove if we have registered an upload function as a callback
158: */
159: if (http->timer) {
160: HTTimer_delete(http->timer);
161: http->timer = NULL;
162: }
163:
164: /*
1.144 frystyk 165: ** Remove the request object and our own context structure for http.
166: */
167: if (status != HT_RECOVER_PIPE) {
1.153 frystyk 168: HTNet_delete(net, status);
1.141 frystyk 169: HT_FREE(http);
170: }
1.94 frystyk 171: return YES;
1.55 frystyk 172: }
173:
1.71 frystyk 174: /*
1.130 frystyk 175: ** Informational 1xx codes are handled separately
1.136 frystyk 176: ** Returns YES if we should continue, NO if we should stop
1.55 frystyk 177: */
1.130 frystyk 178: PRIVATE BOOL HTTPInformation (HTStream * me)
1.55 frystyk 179: {
1.136 frystyk 180: http_info * http = me->http;
1.71 frystyk 181: switch (me->status) {
182:
1.136 frystyk 183: case 100:
1.161 frystyk 184: #if 0
1.136 frystyk 185: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_CONTINUE,
186: me->reason, (int) strlen(me->reason),
187: "HTTPInformation");
1.161 frystyk 188: #endif
1.136 frystyk 189: return YES;
190: break;
191:
1.130 frystyk 192: case 101:
1.136 frystyk 193: /*
194: ** We consider 101 Switching as a final state and exit this request
195: */
1.130 frystyk 196: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_SWITCHING,
1.127 frystyk 197: me->reason, (int) strlen(me->reason),
1.130 frystyk 198: "HTTPInformation");
1.136 frystyk 199: http->next = HTTP_OK;
200: http->result = HT_UPGRADE;
1.180 frystyk 201: return YES;
1.127 frystyk 202: break;
203:
1.130 frystyk 204: default:
1.136 frystyk 205: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_BAD_REPLY,
206: (void *) me->buffer, me->buflen, "HTTPNextState");
207: http->next = HTTP_ERROR;
208: http->result = HT_ERROR;
1.127 frystyk 209: break;
1.130 frystyk 210: }
1.136 frystyk 211: return NO;
1.130 frystyk 212: }
213:
1.158 frystyk 214:
215:
1.130 frystyk 216: /*
217: ** This is a big switch handling all HTTP return codes. It puts in any
218: ** appropiate error message and decides whether we should expect data
219: ** or not.
220: */
221: PRIVATE void HTTPNextState (HTStream * me)
222: {
1.134 frystyk 223: http_info * http = me->http;
1.158 frystyk 224: int error_class = me->status / 100;
225: switch (error_class) {
1.127 frystyk 226:
1.158 frystyk 227: case 0: /* 0.9 response */
228: case 2:
1.112 frystyk 229:
1.158 frystyk 230: switch (me->status) {
1.112 frystyk 231:
1.158 frystyk 232: case 201: /* Created */
233: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_CREATED,
234: me->reason, (int) strlen(me->reason),
235: "HTTPNextState");
236: http->next = HTTP_OK;
237: http->result = HT_CREATED;
238: break;
239:
240: case 202: /* Accepted */
241: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_ACCEPTED,
242: me->reason, (int) strlen(me->reason),
243: "HTTPNextState");
244: http->next = HTTP_OK;
245: http->result = HT_ACCEPTED;
246: break;
247:
248: case 203: /* Non-authoritative information */
249: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_NON_AUTHORITATIVE,
250: me->reason, (int) strlen(me->reason),
251: "HTTPNextState");
252: http->next = HTTP_OK;
253: http->result = HT_LOADED;
254: break;
255:
256: case 204: /* No Response */
257: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_NO_CONTENT,
258: me->reason, (int) strlen(me->reason),
259: "HTTPNextState");
260: http->next = HTTP_OK;
261: http->result = HT_NO_DATA;
262: break;
263:
264: case 205: /* Reset Content */
265: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_RESET,
266: me->reason, (int) strlen(me->reason),
267: "HTTPNextState");
268: http->next = HTTP_OK;
269: http->result = HT_RESET_CONTENT;
270: break;
271:
272: case 206: /* Partial Content */
273: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_PARTIAL,
274: me->reason, (int) strlen(me->reason),
275: "HTTPNextState");
276: http->next = HTTP_OK;
277: http->result = HT_PARTIAL_CONTENT;
278: break;
279:
280: case 207: /* Partial Update OK */
281: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_PARTIAL_OK,
282: me->reason, (int) strlen(me->reason),
283: "HTTPNextState");
284: http->next = HTTP_OK;
285: http->result = HT_PARTIAL_CONTENT;
286: break;
287:
288: default:
289: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_OK,
290: me->reason, (int) strlen(me->reason),
291: "HTTPNextState");
292: http->next = HTTP_OK;
293: http->result = HT_LOADED;
294: break;
295: }
1.134 frystyk 296: break;
297:
1.158 frystyk 298: case 3:
1.134 frystyk 299:
1.158 frystyk 300: switch (me->status) {
1.78 frystyk 301:
1.158 frystyk 302: case 301: /* Moved */
303: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_MOVED,
304: me->reason, (int) strlen(me->reason),
305: "HTTPNextState");
306: http->next = HTTP_ERROR;
307: http->result = HT_PERM_REDIRECT;
308: break;
309:
310: case 302: /* Found */
311: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_FOUND,
312: me->reason, (int) strlen(me->reason),
313: "HTTPNextState");
314: http->next = HTTP_ERROR;
315: http->result = HT_FOUND;
316: break;
1.55 frystyk 317:
1.158 frystyk 318: case 303: /* Method */
319: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_METHOD,
320: me->reason, (int) strlen(me->reason),
321: "HTTPNextState");
322: http->next = HTTP_ERROR;
323: http->result = HT_SEE_OTHER;
324: break;
325:
326: case 304: /* Not Modified */
327: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_NOT_MODIFIED,
328: me->reason, (int) strlen(me->reason),
329: "HTTPNextState");
330: http->next = HTTP_OK;
331: http->result = HT_NOT_MODIFIED;
332: break;
1.134 frystyk 333:
1.158 frystyk 334: case 305: /* Use proxy */
335: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_USE_PROXY,
336: me->reason, (int) strlen(me->reason),
337: "HTTPNextState");
338: http->next = HTTP_ERROR;
339: http->result = HT_USE_PROXY;
340: break;
1.55 frystyk 341:
1.152 frystyk 342: #if 0
1.158 frystyk 343: case 306: /* Use proxy */
344: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_PROXY_REDIRECT,
345: me->reason, (int) strlen(me->reason),
346: "HTTPNextState");
347: http->next = HTTP_ERROR;
348: http->result = HT_USE_PROXY;
349: break;
1.152 frystyk 350: #endif
351:
1.158 frystyk 352: case 307: /* Use proxy */
353: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_TEMP_REDIRECT,
354: me->reason, (int) strlen(me->reason),
355: "HTTPNextState");
356: http->next = HTTP_ERROR;
357: http->result = HT_TEMP_REDIRECT;
358: break;
359:
360: default:
361: HTRequest_addError(me->request, ERR_INFO, NO, HTERR_MULTIPLE,
362: me->reason, (int) strlen(me->reason),
363: "HTTPNextState");
364: http->next = HTTP_OK;
365: http->result = HT_LOADED;
366: break;
367: }
368: break;
369:
370: case 4:
371:
372: switch (me->status) {
373: case 401:
374: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_UNAUTHORIZED,
375: me->reason, (int) strlen(me->reason), "HTTPNextState");
376: http->next = HTTP_ERROR;
377: http->result = HT_NO_ACCESS;
378: break;
1.152 frystyk 379:
1.158 frystyk 380: case 402: /* Payment required */
381: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_PAYMENT_REQUIRED,
382: me->reason, (int) strlen(me->reason), "HTTPNextState");
383: http->next = HTTP_ERROR;
384: http->result = -402;
385: break;
1.71 frystyk 386:
1.158 frystyk 387: case 403: /* Forbidden */
388: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_FORBIDDEN,
389: me->reason, (int) strlen(me->reason), "HTTPNextState");
390: http->next = HTTP_ERROR;
391: http->result = HT_FORBIDDEN;
392: break;
1.55 frystyk 393:
1.158 frystyk 394: case 404: /* Not Found */
395: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_NOT_FOUND,
396: me->reason, (int) strlen(me->reason), "HTTPNextState");
397: http->next = HTTP_ERROR;
398: http->result = -404;
399: break;
1.55 frystyk 400:
1.158 frystyk 401: case 405: /* Not Allowed */
402: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_NOT_ALLOWED,
403: me->reason, (int) strlen(me->reason), "HTTPNextState");
404: http->next = HTTP_ERROR;
405: http->result = -405;
406: break;
407:
408: case 406: /* None Acceptable */
409: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_NONE_ACCEPTABLE,
410: me->reason, (int) strlen(me->reason), "HTTPNextState");
411: http->next = HTTP_ERROR;
412: http->result = HT_NOT_ACCEPTABLE;
413: break;
414:
415: case 407: /* Proxy Authentication Required */
416: HTRequest_addError(me->request, ERR_FATAL, NO,HTERR_PROXY_UNAUTHORIZED,
417: me->reason, (int) strlen(me->reason), "HTTPNextState");
418: http->next = HTTP_ERROR;
419: http->result = HT_NO_PROXY_ACCESS;
420: break;
421:
422: case 408: /* Request Timeout */
423: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_TIMEOUT,
424: me->reason, (int) strlen(me->reason), "HTTPNextState");
425: http->next = HTTP_ERROR;
426: http->result = -408;
427: break;
428:
429: case 409: /* Conflict */
430: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_CONFLICT,
431: me->reason, (int) strlen(me->reason), "HTTPNextState");
432: http->next = HTTP_ERROR;
433: http->result = HT_CONFLICT;
434: break;
435:
436: case 410: /* Gone */
437: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_GONE,
438: me->reason, (int) strlen(me->reason), "HTTPNextState");
439: http->next = HTTP_ERROR;
440: http->result = -410;
441: break;
442:
443: case 411: /* Length Required */
444: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_LENGTH_REQUIRED,
445: me->reason, (int) strlen(me->reason), "HTTPNextState");
446: http->next = HTTP_ERROR;
447: http->result = HT_LENGTH_REQUIRED;
448: break;
449:
450: case 412: /* Precondition failed */
451: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_PRECON_FAILED,
452: me->reason, (int) strlen(me->reason), "HTTPNextState");
453: http->next = HTTP_ERROR;
454: http->result = -412;
455: break;
456:
457: case 413: /* Request entity too large */
458: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_TOO_BIG,
459: me->reason, (int) strlen(me->reason), "HTTPNextState");
460: http->next = HTTP_ERROR;
461: http->result = -413;
462: break;
463:
464: case 414: /* Request-URI too long */
465: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_URI_TOO_BIG,
466: me->reason, (int) strlen(me->reason), "HTTPNextState");
467: http->next = HTTP_ERROR;
468: http->result = -414;
469: break;
470:
471: case 415: /* Unsupported */
472: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_UNSUPPORTED,
473: me->reason, (int) strlen(me->reason), "HTTPNextState");
474: http->next = HTTP_ERROR;
475: http->result = -415;
476: break;
477:
478: case 416: /* Request Range not satisfiable */
479: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_BAD_RANGE,
480: me->reason, (int) strlen(me->reason), "HTTPNextState");
481: http->next = HTTP_ERROR;
482: http->result = -416;
483: break;
484:
485: case 417: /* Expectation Failed */
486: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_EXPECTATION_FAILED,
487: me->reason, (int) strlen(me->reason), "HTTPNextState");
488: http->next = HTTP_ERROR;
489: http->result = -417;
490: break;
491:
492: case 418: /* Reauthentication required */
493: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_REAUTH,
494: me->reason, (int) strlen(me->reason), "HTTPNextState");
495: http->next = HTTP_ERROR;
496: http->result = -418;
497: break;
498:
499: case 419: /* Proxy Reauthentication required */
500: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_PROXY_REAUTH,
501: me->reason, (int) strlen(me->reason), "HTTPNextState");
502: http->next = HTTP_ERROR;
503: http->result = -419;
504: break;
505:
506: default:
507: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_BAD_REQUEST,
508: me->reason, (int) strlen(me->reason), "HTTPNextState");
509: http->next = HTTP_ERROR;
510: http->result = -400;
511: break;
512: }
1.134 frystyk 513: break;
514:
1.158 frystyk 515: case 5:
1.134 frystyk 516:
1.158 frystyk 517: switch (me->status) {
518: case 501:
519: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_NOT_IMPLEMENTED,
520: me->reason, (int) strlen(me->reason), "HTTPNextState");
521: http->next = HTTP_ERROR;
522: http->result = -501;
523: break;
524:
525: case 502:
526: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_BAD_GATE,
527: me->reason, (int) strlen(me->reason), "HTTPNextState");
528: http->next = HTTP_ERROR;
529: http->result = -502;
530: break;
531:
532: case 503:
533: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_DOWN,
534: me->reason, (int) strlen(me->reason), "HTTPNextState");
535: http->next = HTTP_ERROR;
536:
537: /*
538: ** If Retry-After header is found then return HT_RETRY else HT_ERROR.
539: ** The caller may want to reissue the request at a later point in time.
540: */
541: {
542: HTResponse * response = HTRequest_response(me->request);
543: if (HTResponse_retryTime(response))
544: http->result = HT_RETRY;
545: else
546: http->result = -500;
547: }
548: break;
549:
550: case 504:
551: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_GATE_TIMEOUT,
552: me->reason, (int) strlen(me->reason), "HTTPNextState");
553: http->next = HTTP_ERROR;
554: http->result = -504;
555: break;
556:
557: case 505: /* Unsupported protocol version */
558: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_BAD_VERSION,
559: me->reason, (int) strlen(me->reason), "HTTPNextState");
560: http->next = HTTP_ERROR;
561: http->result = HT_BAD_VERSION;
562: break;
563:
564: case 506: /* Partial update Not Implemented */
565: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_NO_PARTIAL_UPDATE,
566: me->reason, (int) strlen(me->reason), "HTTPNextState");
567: http->next = HTTP_ERROR;
568: http->result = HT_BAD_VERSION;
569: break;
570:
571: default: /* bad number */
572: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_INTERNAL,
573: me->reason, (int) strlen(me->reason), "HTTPNextState");
574: http->next = HTTP_ERROR;
575: http->result = -500;
576: break;
1.140 frystyk 577: }
1.78 frystyk 578: break;
579:
1.158 frystyk 580: default:
1.104 frystyk 581: HTRequest_addError(me->request, ERR_FATAL, NO, HTERR_BAD_REPLY,
1.158 frystyk 582: (void *) me->buffer, me->buflen, "HTTPNextState");
1.134 frystyk 583: http->next = HTTP_ERROR;
1.155 frystyk 584: http->result = -(me->status);
1.71 frystyk 585: break;
1.55 frystyk 586: }
587: }
588:
1.71 frystyk 589: /* ------------------------------------------------------------------------- */
590: /* HTTP Status Line Stream */
591: /* ------------------------------------------------------------------------- */
1.55 frystyk 592:
1.71 frystyk 593: /*
1.141 frystyk 594: ** Analyze the stream we have read. If it is a HTTP 1.0 or higher
1.71 frystyk 595: ** then create a MIME-stream, else create a Guess stream to find out
596: ** what the 0.9 server is sending. We need to copy the buffer as we don't
597: ** know if we can modify the contents or not.
1.78 frystyk 598: **
599: ** Stream handling is a function of the status code returned from the
600: ** server:
601: ** 200: Use `output_stream' in HTRequest structure
1.94 frystyk 602: ** else: Use `debug_stream' in HTRequest structure
1.80 frystyk 603: **
604: ** Return: YES if buffer should be written out. NO otherwise
1.56 frystyk 605: */
1.157 frystyk 606: PRIVATE int stream_pipe (HTStream * me, int length)
1.56 frystyk 607: {
1.136 frystyk 608: HTRequest * request = me->request;
609: HTNet * net = HTRequest_net(request);
1.123 frystyk 610: HTHost * host = HTNet_host(net);
1.141 frystyk 611:
1.153 frystyk 612: #if 0
613: {
614: char * uri = HTAnchor_address((HTAnchor *) HTRequest_anchor(request));
615: fprintf(stderr, "HTTP header: %s for '%s'\n", me->buffer, uri);
616: HT_FREE(uri);
617: }
618: #endif
619:
1.132 frystyk 620: /*
621: ** Just check for HTTP and not HTTP/ as NCSA server chokes on 1.1 replies
622: ** Thanks to Markku Savela <msa@msa.tte.vtt.fi>
1.141 frystyk 623:
1.132 frystyk 624: */
625: if (strncasecomp(me->buffer, "http", 4)) {
1.80 frystyk 626: int status;
1.136 frystyk 627: HTRequest_addError(request, ERR_INFO, NO, HTERR_HTTP09,
1.80 frystyk 628: (void *) me->buffer, me->buflen, "HTTPStatusStream");
1.136 frystyk 629: me->target = HTStreamStack(WWW_UNKNOWN,
630: HTRequest_outputFormat(request),
631: HTRequest_outputStream(request),
632: request, NO);
1.134 frystyk 633: me->http->next = HTTP_OK;
1.80 frystyk 634: if ((status = PUTBLOCK(me->buffer, me->buflen)) == HT_OK)
635: me->transparent = YES;
1.123 frystyk 636: HTHost_setVersion(host, HTTP_09);
1.157 frystyk 637: if (length > 0) HTHost_setConsumed(host, length);
1.178 frystyk 638: HTTRACE(PROT_TRACE, "HTTP Status. `%s\' is probably a broken 1.0 server that doesn't understand HEAD\n" _
1.171 frystyk 639: HTHost_name(host));
640: return HT_ERROR;
1.80 frystyk 641: } else {
1.140 frystyk 642: HTResponse * response = HTRequest_response(request);
1.155 frystyk 643: char * ptr = me->buffer+5; /* Skip the HTTP part */
644: char * vptr = NULL;
645: int major = 0;
646: int minor = 0;
647: me->version = vptr = HTNextField(&ptr);
648: if (vptr) {
649: major = (int) strtol(me->version, &vptr, 10);
650: if (vptr++) minor = strtol(vptr, NULL, 10);
651: }
1.95 frystyk 652:
1.130 frystyk 653: /* Here we want to find out when to use persistent connection */
1.171 frystyk 654: if (major > 1 && major < 100) {
1.178 frystyk 655: HTTRACE(PROT_TRACE, "HTTP Status. Major version number is %d\n" _ major);
1.155 frystyk 656: me->target = HTErrorStream();
1.158 frystyk 657: me->status = 9999;
1.155 frystyk 658: HTTPNextState(me); /* Get next state */
1.158 frystyk 659: return HT_ERROR;
1.155 frystyk 660: } else if (minor <= 0) {
1.171 frystyk 661: if (major > 100) {
1.178 frystyk 662: HTTRACE(PROT_TRACE, "HTTP Status. This is a *BROKEN* HTTP/1.0 server\n");
1.171 frystyk 663: me->status = 200;
664: } else {
1.178 frystyk 665: HTTRACE(PROT_TRACE, "HTTP Status. This is an HTTP/1.0 server\n");
1.171 frystyk 666: me->status = atoi(HTNextField(&ptr));
667: }
1.128 frystyk 668: HTHost_setVersion(host, HTTP_10);
1.155 frystyk 669: } else { /* 1.x, x>0 family */
670: HTHost_setVersion(host, HTTP_11); /* Best we can do */
1.156 frystyk 671: if (ConnectionMode & HTTP_11_NO_PIPELINING) {
1.178 frystyk 672: HTTRACE(PROT_TRACE, "HTTP........ Mode is HTTP/1.1 with NO PIPELINING\n");
1.147 frystyk 673: HTNet_setPersistent(net, YES, HT_TP_SINGLE);
1.156 frystyk 674: } else if (ConnectionMode & HTTP_11_MUX) {
1.178 frystyk 675: HTTRACE(PROT_TRACE, "HTTP........ Mode is HTTP/1.1 with MUXING\n");
1.156 frystyk 676: HTNet_setPersistent(net, YES, HT_TP_INTERLEAVE);
1.147 frystyk 677: } else if (ConnectionMode & HTTP_FORCE_10) {
1.178 frystyk 678: HTTRACE(PROT_TRACE, "HTTP........ Mode is FORCE HTTP/1.0\n");
1.147 frystyk 679: HTHost_setVersion(host, HTTP_10);
680: HTNet_setPersistent(net, NO, HT_TP_SINGLE);
681: } else
682: HTNet_setPersistent(net, YES, HT_TP_PIPELINE);
1.171 frystyk 683: me->status = atoi(HTNextField(&ptr));
1.128 frystyk 684: }
1.95 frystyk 685:
1.81 frystyk 686: me->reason = ptr;
687: if ((ptr = strchr(me->reason, '\r')) != NULL) /* Strip \r and \n */
688: *ptr = '0円';
689: else if ((ptr = strchr(me->reason, '\n')) != NULL)
690: *ptr = '0円';
691:
1.130 frystyk 692: /*
693: ** If it is a 1xx code then find out what to do and return until we
694: ** get the next code. In the case of Upgrade we may not get back here
695: ** at all. If we are uploading an entity then continue doing that
696: */
697: if (me->status/100 == 1) {
1.136 frystyk 698: if (HTTPInformation(me) == YES) {
1.180 frystyk 699: if (me->status==100) {
700: me->buflen = 0;
701: me->state = EOL_BEGIN;
702: if (me->info_target) (*me->info_target->isa->_free)(me->info_target);
703: me->info_target = HTStreamStack(WWW_MIME_CONT,
704: HTRequest_debugFormat(request),
705: HTRequest_debugStream(request),
706: request, NO);
707: if (length > 0) HTHost_setConsumed(host, length);
708: return HT_OK;
709: } else if (me->status==101) {
710: if (me->info_target) (*me->info_target->isa->_free)(me->info_target);
711: me->target = HTStreamStack(WWW_MIME_UPGRADE,
712: HTRequest_outputFormat(request),
713: HTRequest_outputStream(request),
714: request, NO);
715: if (length > 0) HTHost_setConsumed(host, length);
716: me->transparent = YES;
717: return HT_OK;
718: }
1.103 frystyk 719: }
1.56 frystyk 720: }
1.133 frystyk 721:
722: /*
723: ** As we are getting fresh metainformation in the HTTP response,
724: ** we clear the old metainfomation in order not to mix it with the new
725: ** one. This is particularly important for the content-length and the
1.139 frystyk 726: ** like. The TRACE and OPTIONS method just adds to the current
727: ** metainformation so in that case we don't clear the anchor.
1.133 frystyk 728: */
1.136 frystyk 729: if (me->status==200 || me->status==203 || me->status==300) {
1.140 frystyk 730: /*
731: ** 200, 203 and 300 are all fully cacheable responses. No byte
732: ** ranges or anything else make life hard in this case.
733: */
1.148 frystyk 734: HTAnchor_clearHeader(HTRequest_anchor(request));
1.166 frystyk 735: HTResponse_setCachable(response, HT_CACHE_ALL);
1.136 frystyk 736: me->target = HTStreamStack(WWW_MIME,
737: HTRequest_outputFormat(request),
738: HTRequest_outputStream(request),
739: request, NO);
1.176 frystyk 740: } else if (me->status==204) {
741: HTResponse_setCachable(response, HT_CACHE_ALL);
742: me->target = HTStreamStack(WWW_MIME_HEAD,
743: HTRequest_debugFormat(request),
744: HTRequest_debugStream(request),
745: request, NO);
1.140 frystyk 746: } else if (me->status==206) {
747: /*
748: ** We got a partial response and now we must check whether
749: ** we issued a cache If-Range request or it was a new
750: ** partial response which we don't have in cache. In the latter
751: ** case, we don't cache the object and in the former we append
752: ** the result to the already existing cache entry.
753: */
754: HTReload reload = HTRequest_reloadMode(request);
755: if (reload == HT_CACHE_RANGE_VALIDATE) {
1.166 frystyk 756: HTResponse_setCachable(response, HT_CACHE_ALL);
1.140 frystyk 757: me->target = HTStreamStack(WWW_MIME_PART,
758: HTRequest_outputFormat(request),
759: HTRequest_outputStream(request),
760: request, NO);
761: } else {
1.149 frystyk 762: HTAnchor_clearHeader(HTRequest_anchor(request));
1.140 frystyk 763: me->target = HTStreamStack(WWW_MIME,
764: HTRequest_outputFormat(request),
765: HTRequest_outputStream(request),
766: request, NO);
767: }
1.176 frystyk 768: } else if (me->status==304) {
769: HTResponse_setCachable(response, HT_CACHE_NOT_MODIFIED);
1.136 frystyk 770: me->target = HTStreamStack(WWW_MIME_HEAD,
771: HTRequest_debugFormat(request),
772: HTRequest_debugStream(request),
773: request, NO);
774: } else if (HTRequest_debugStream(request)) {
1.175 frystyk 775: HTResponse_setCachable(response,
776: (me->status == 201) ? HT_CACHE_ETAG : HT_NO_CACHE);
1.136 frystyk 777: me->target = HTStreamStack(WWW_MIME,
778: HTRequest_debugFormat(request),
779: HTRequest_debugStream(request),
780: request, NO);
781: } else {
1.140 frystyk 782: /*
783: ** We still need to parse the MIME part in order to find any
784: ** valuable meta information which is needed from the response.
785: */
1.175 frystyk 786: HTResponse_setCachable(response,
787: (me->status == 201) ? HT_CACHE_ETAG : HT_NO_CACHE);
1.136 frystyk 788: me->target = HTStreamStack(WWW_MIME,
789: HTRequest_debugFormat(request),
790: HTRequest_debugStream(request),
791: request, NO);
1.133 frystyk 792: }
1.56 frystyk 793: }
1.113 frystyk 794: if (!me->target) me->target = HTErrorStream();
1.81 frystyk 795: HTTPNextState(me); /* Get next state */
1.80 frystyk 796: me->transparent = YES;
1.157 frystyk 797: if (length > 0) HTHost_setConsumed(HTNet_host(HTRequest_net(me->request)), length);
1.80 frystyk 798: return HT_OK;
1.71 frystyk 799: }
1.56 frystyk 800:
1.80 frystyk 801: /*
802: ** Searches for HTTP header line until buffer fills up or a CRLF or LF
803: ** is found
804: */
1.119 frystyk 805: PRIVATE int HTTPStatus_put_block (HTStream * me, const char * b, int l)
1.71 frystyk 806: {
1.150 frystyk 807: int status = HT_OK;
1.153 frystyk 808: int length = l;
1.146 frystyk 809: me->startLen = me->buflen;
1.80 frystyk 810: while (!me->transparent && l-- > 0) {
1.157 frystyk 811: if (me->info_target) {
812:
813: /* Put data down the 1xx return code parser until we are done. */
814: status = (*me->info_target->isa->put_block)(me->info_target, b, l+1);
815: if (status != HT_CONTINUE) return status;
816:
817: /* Now free the info stream */
818: (*me->info_target->isa->_free)(me->info_target);
819: me->info_target = NULL;
820:
821: /* Update where we are in the stream */
822: l = HTHost_remainingRead(HTNet_host(HTRequest_net(me->request)));
1.163 frystyk 823: b += (length-l);
1.159 frystyk 824: length = l;
1.163 frystyk 825: if (l <= 0) break;
1.157 frystyk 826:
1.80 frystyk 827: } else {
828: *(me->buffer+me->buflen++) = *b;
829: if (me->state == EOL_FCR) {
830: if (*b == LF) { /* Line found */
1.157 frystyk 831: if ((status = stream_pipe(me, length-l)) != HT_OK) return status;
1.80 frystyk 832: } else {
833: me->state = EOL_BEGIN;
834: }
835: } else if (*b == CR) {
836: me->state = EOL_FCR;
837: } else if (*b == LF) {
1.157 frystyk 838: if ((status = stream_pipe(me, length-l)) != HT_OK) return status;
1.71 frystyk 839: } else {
1.80 frystyk 840: if (me->buflen >= MAX_STATUS_LEN) {
1.157 frystyk 841: if ((status = stream_pipe(me, length-l)) != HT_OK) return status;
1.80 frystyk 842: }
1.71 frystyk 843: }
1.80 frystyk 844: b++;
1.71 frystyk 845: }
1.56 frystyk 846: }
1.153 frystyk 847:
1.159 frystyk 848: if (!me->transparent && length != l)
849: HTHost_setConsumed(HTNet_host(HTRequest_net(me->request)), length-l);
850:
1.99 frystyk 851: if (l > 0) return PUTBLOCK(b, l);
1.150 frystyk 852: return status;
1.56 frystyk 853: }
854:
1.119 frystyk 855: PRIVATE int HTTPStatus_put_string (HTStream * me, const char * s)
1.71 frystyk 856: {
1.80 frystyk 857: return HTTPStatus_put_block(me, s, (int) strlen(s));
1.71 frystyk 858: }
1.56 frystyk 859:
1.100 frystyk 860: PRIVATE int HTTPStatus_put_character (HTStream * me, char c)
1.71 frystyk 861: {
1.80 frystyk 862: return HTTPStatus_put_block(me, &c, 1);
863: }
864:
1.100 frystyk 865: PRIVATE int HTTPStatus_flush (HTStream * me)
1.80 frystyk 866: {
867: return (*me->target->isa->flush)(me->target);
1.71 frystyk 868: }
869:
1.100 frystyk 870: PRIVATE int HTTPStatus_free (HTStream * me)
1.71 frystyk 871: {
1.87 frystyk 872: int status = HT_OK;
873: if (me->target) {
874: if ((status = (*me->target->isa->_free)(me->target))==HT_WOULD_BLOCK)
875: return HT_WOULD_BLOCK;
876: }
1.115 frystyk 877: HT_FREE(me);
1.100 frystyk 878: return status;
1.71 frystyk 879: }
880:
1.104 frystyk 881: PRIVATE int HTTPStatus_abort (HTStream * me, HTList * e)
1.71 frystyk 882: {
883: if (me->target)
1.74 frystyk 884: ABORT_TARGET;
1.115 frystyk 885: HT_FREE(me);
1.178 frystyk 886: HTTRACE(PROT_TRACE, "HTTPStatus.. ABORTING...\n");
1.80 frystyk 887: return HT_ERROR;
1.71 frystyk 888: }
889:
890: /* HTTPStatus Stream
891: ** -----------------
892: */
1.119 frystyk 893: PRIVATE const HTStreamClass HTTPStatusClass =
1.71 frystyk 894: {
895: "HTTPStatus",
1.80 frystyk 896: HTTPStatus_flush,
1.71 frystyk 897: HTTPStatus_free,
898: HTTPStatus_abort,
899: HTTPStatus_put_character,
900: HTTPStatus_put_string,
901: HTTPStatus_put_block
902: };
903:
1.113 frystyk 904: PUBLIC HTStream * HTTPStatus_new (HTRequest * request,
905: void * param,
906: HTFormat input_format,
907: HTFormat output_format,
908: HTStream * output_stream)
1.71 frystyk 909: {
1.115 frystyk 910: HTStream * me;
911: if ((me = (HTStream *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
912: HT_OUTOFMEM("HTTPStatus_new");
1.71 frystyk 913: me->isa = &HTTPStatusClass;
1.113 frystyk 914: if (request) {
1.126 frystyk 915: HTNet * net = HTRequest_net(request);
1.125 frystyk 916: /* Get existing copy */
917: http_info * http = (http_info *) HTNet_context(net);
1.113 frystyk 918: me->request = request;
919: me->http = http;
920: http->next = HTTP_ERROR;
921: me->state = EOL_BEGIN;
922: return me;
923: } else
924: return HTErrorStream();
1.71 frystyk 925: }
926:
927: /* ------------------------------------------------------------------------- */
928:
929: /* Load Document from HTTP Server HTLoadHTTP
1.55 frystyk 930: ** ==============================
931: **
932: ** Given a hypertext address, this routine loads a document.
933: **
934: ** On entry,
935: ** request This is the request structure
1.94 frystyk 936: ** returns HT_ERROR Error has occured in call back
937: ** HT_OK Call back was OK
1.55 frystyk 938: */
1.141 frystyk 939: PRIVATE int HTTPEvent (SOCKET soc, void * pVoid, HTEventType type);
940:
941: PUBLIC int HTLoadHTTP (SOCKET soc, HTRequest * request)
1.55 frystyk 942: {
943: http_info *http; /* Specific protocol information */
1.112 frystyk 944: HTParentAnchor *anchor = HTRequest_anchor(request);
1.141 frystyk 945: HTNet * net = HTRequest_net(request);
1.112 frystyk 946:
1.94 frystyk 947: /*
948: ** Initiate a new http structure and bind to request structure
949: ** This is actually state HTTP_BEGIN, but it can't be in the state
950: ** machine as we need the structure first.
951: */
1.178 frystyk 952: HTTRACE(PROT_TRACE, "HTTP........ Looking for `%s\'\n" _
1.141 frystyk 953: HTAnchor_physical(anchor));
954: if ((http = (http_info *) HT_CALLOC(1, sizeof(http_info))) == NULL)
955: HT_OUTOFMEM("HTLoadHTTP");
956: http->net = net;
1.157 frystyk 957: http->request = request;
1.141 frystyk 958: HTNet_setContext(net, http);
959: HTNet_setEventCallback(net, HTTPEvent);
960: HTNet_setEventParam(net, http); /* callbacks get http* */
961:
1.157 frystyk 962: return HTTPEvent(soc, http, HTEvent_BEGIN); /* get it started - ops is ignored */
963: }
964:
965: PRIVATE int FlushPutEvent (HTTimer * timer, void * param, HTEventType type)
966: {
967: http_info * http = (http_info *) param;
968: HTStream * input = HTRequest_inputStream(http->request);
969: HTPostCallback * pcbf = HTRequest_postCallback(http->request);
1.181 ! frystyk 970: int status = HT_ERROR;
1.157 frystyk 971:
1.165 frystyk 972: http->usedTimer = YES;
1.160 frystyk 973: if (timer != http->timer)
1.178 frystyk 974: HTDEBUGBREAK("HTTP timer %p not in sync\n" _ timer);
975: HTTRACE(PROT_TRACE, "Uploading... Flushing %p with timer %p\n" _ http _ timer);
1.157 frystyk 976:
977: /*
1.181 ! frystyk 978: ** Call the callback that will provide the data to save
! 979: ** If the callback returns HT_OK then call it again until
! 980: ** it returns something else than HT_OK.
1.157 frystyk 981: */
1.181 ! frystyk 982: if (http && input && pcbf) {
! 983: status = (*pcbf)(http->request, input);
! 984: HTTRACE(PROT_TRACE, "Uploading... Callback returned %d\n" _ status);
! 985: }
1.157 frystyk 986:
987: /*
1.181 ! frystyk 988: ** If the callback returned something else than HT_OK then delete
! 989: ** the timer, otherwise update it to a much shorter expiration
! 990: ** time so that we can write some more data to the net.
1.157 frystyk 991: */
1.181 ! frystyk 992: if (status != HT_OK) {
! 993: HTTimer_delete(http->timer);
! 994: http->timer = NULL;
! 995: } else if (!http->repetitive_writing) {
! 996: http->timer = HTTimer_new(NULL, FlushPutEvent, http, HTRepeatWrite, YES, YES);
! 997: http->repetitive_writing = YES;
! 998: }
1.157 frystyk 999: return HT_OK;
1.141 frystyk 1000: }
1001:
1002: PRIVATE int HTTPEvent (SOCKET soc, void * pVoid, HTEventType type)
1003: {
1004: http_info * http = (http_info *)pVoid;
1005: int status = HT_ERROR;
1006: HTNet * net = http->net;
1007: HTRequest * request = HTNet_request(net);
1.144 frystyk 1008: HTParentAnchor * anchor = HTRequest_anchor(request);
1009: HTHost * host = HTNet_host(net);
1.141 frystyk 1010:
1011: /*
1.166 frystyk 1012: ** Check whether we have been interrupted or timed out
1.141 frystyk 1013: */
1014: if (type == HTEvent_BEGIN) {
1.134 frystyk 1015: http->next = HTTP_OK;
1016: http->result = HT_ERROR;
1.141 frystyk 1017: } else if (type == HTEvent_CLOSE) {
1.179 frystyk 1018: long read_len = HTNet_bytesRead(net);
1019: long doc_len = HTAnchor_length(anchor);
1020:
1021: /*
1022: ** It is OK to get a close if a) we don't pipeline and b)
1023: ** we have the expected amount of data. In case we don't
1024: ** know how much data to expect, we must accept it asis.
1025: */
1026: if (HTHost_numberOfOutstandingNetObjects(host) == 1 &&
1027: (doc_len<0 || doc_len==read_len)) {
1028: HTTPCleanup(request, HT_LOADED);
1029: } else {
1030: HTRequest_addError(request, ERR_FATAL, NO, HTERR_INTERRUPTED,
1031: NULL, 0, "HTLoadHTTP");
1032: HTTPCleanup(request, HT_INTERRUPTED);
1033: }
1.166 frystyk 1034: return HT_OK;
1035: } else if (type == HTEvent_TIMEOUT) {
1036: HTRequest_addError(request, ERR_FATAL, NO, HTERR_TIME_OUT,
1037: NULL, 0, "HTLoadHTTP");
1038: HTTPCleanup(request, HT_TIMEOUT);
1.94 frystyk 1039: return HT_OK;
1.141 frystyk 1040: } else if (type == HTEvent_END) {
1041: HTTPCleanup(request, http->result);
1042: return HT_OK;
1043: } else if (type == HTEvent_RESET) {
1044: HTTPCleanup(request, HT_RECOVER_PIPE);
1.145 frystyk 1045: http->state = HTTP_BEGIN;
1.141 frystyk 1046: return HT_OK;
1047: }
1048:
1.71 frystyk 1049: /* Now jump into the machine. We know the state from the previous run */
1050: while (1) {
1051: switch (http->state) {
1.134 frystyk 1052: case HTTP_BEGIN:
1.144 frystyk 1053: status = HTHost_connect(host, net, HTAnchor_physical(anchor), HTTP_PORT);
1054: host = HTNet_host(net);
1.153 frystyk 1055: if (status == HT_OK) {
1.140 frystyk 1056:
1.123 frystyk 1057: /*
1.140 frystyk 1058: ** Check the protocol class to see if we have connected to a
1059: ** the right class of server, in this case HTTP. If we don't
1060: ** know the server then assume a HTTP/1.0
1.123 frystyk 1061: */
1062: {
1063: char * s_class = HTHost_class(host);
1.140 frystyk 1064: if (!s_class) {
1.174 frystyk 1065: if (HTRequest_proxy(request) == NULL) {
1066: HTAssocList * alist = HTRequest_connection(request);
1067: if (!(alist && HTAssocList_findObject(alist, "close")))
1068: HTRequest_addConnection(request, "Keep-Alive", "");
1069: }
1.140 frystyk 1070: HTHost_setClass(host, "http");
1071: } else if (strcasecomp(s_class, "http")) {
1.123 frystyk 1072: HTRequest_addError(request, ERR_FATAL, NO, HTERR_CLASS,
1073: NULL, 0, "HTLoadHTTP");
1074: http->state = HTTP_ERROR;
1075: break;
1076: }
1.95 frystyk 1077: }
1.147 frystyk 1078:
1.156 frystyk 1079: if (ConnectionMode & HTTP_11_NO_PIPELINING) {
1.178 frystyk 1080: HTTRACE(PROT_TRACE, "HTTP........ Mode is HTTP/1.1 WITH NO PIPELINING\n");
1.147 frystyk 1081: HTRequest_setFlush(request, YES);
1082: } else if (ConnectionMode & HTTP_FORCE_10) {
1.178 frystyk 1083: HTTRACE(PROT_TRACE, "HTTP........ Mode is FORCE HTTP/1.0\n");
1.147 frystyk 1084: HTHost_setVersion(host, HTTP_10);
1085: }
1086:
1.151 frystyk 1087: if (HTNet_preemptive(net)) {
1.178 frystyk 1088: HTTRACE(PROT_TRACE, "HTTP........ Force flush on preemptive load\n");
1.151 frystyk 1089: HTRequest_setFlush(request, YES);
1090: }
1091:
1.147 frystyk 1092: /* Jump to next state */
1.144 frystyk 1093: http->state = HTTP_NEED_STREAM;
1.159 frystyk 1094: } else if (status == HT_WOULD_BLOCK || status == HT_PENDING) {
1.144 frystyk 1095: return HT_OK;
1.171 frystyk 1096: } else if (status == HT_NO_HOST) {
1097: http->result = HT_NO_HOST;
1098: http->state = HTTP_ERROR;
1.159 frystyk 1099: } else
1.144 frystyk 1100: http->state = HTTP_ERROR; /* Error or interrupt */
1101: break;
1102:
1103: case HTTP_NEED_STREAM:
1.138 frystyk 1104:
1.144 frystyk 1105: /*
1106: ** Create the stream pipe FROM the channel to the application.
1107: ** The target for the input stream pipe is set up using the
1108: ** stream stack.
1109: */
1.167 frystyk 1110: {
1111: /*
1112: ** during a recovery, we might keep the same HTNet object.
1113: ** if so, reuse it's read stream
1114: */
1.176 frystyk 1115: HTStream * me = HTNet_readStream( net );
1.167 frystyk 1116: if ( me == NULL ) {
1.173 frystyk 1117: me = HTStreamStack(WWW_HTTP,
1118: HTRequest_outputFormat(request),
1119: HTRequest_outputStream(request),
1120: request, YES);
1.178 frystyk 1121: #ifdef HTDEBUG
1.173 frystyk 1122: if (PROT_TRACE) {
1123: if (!htfp) htfp = fopen(HTTP_OUTPUT, "ab");
1124: if (htfp) {
1125: me = HTTee(me, HTFWriter_new(request, htfp, YES), NULL);
1.178 frystyk 1126: HTTRACE(PROT_TRACE, "HTTP........ Dumping response to `%s\'\n" _ HTTP_OUTPUT);
1.173 frystyk 1127: }
1128: }
1.178 frystyk 1129: #endif /* HTDEBUG */
1.173 frystyk 1130:
1131: HTNet_setReadStream(net, me);
1.167 frystyk 1132: }
1133: HTRequest_setOutputConnected(request, YES);
1.144 frystyk 1134: }
1.127 frystyk 1135:
1.144 frystyk 1136: /*
1137: ** Create the stream pipe TO the channel from the application
1138: ** and hook it up to the request object
1139: */
1140: {
1141: HTChannel * channel = HTHost_channel(host);
1142: HTOutputStream * output = HTChannel_getChannelOStream(channel);
1143: int version = HTHost_version(host);
1144: HTStream * app = NULL;
1145:
1.178 frystyk 1146: #ifdef HTDEBUG
1.144 frystyk 1147: if (PROT_TRACE) {
1.162 frystyk 1148: if (!htfp) htfp = fopen(HTTP_OUTPUT, "ab");
1149: if (htfp) {
1.144 frystyk 1150: output = (HTOutputStream *)
1.162 frystyk 1151: HTTee((HTStream *) output, HTFWriter_new(request, htfp, YES), NULL);
1.178 frystyk 1152: HTTRACE(PROT_TRACE, "HTTP........ Dumping request to `%s\'\n" _ HTTP_OUTPUT);
1.144 frystyk 1153: }
1154: }
1.178 frystyk 1155: #endif /* HTDEBUG */
1.144 frystyk 1156: app = HTMethod_hasEntity(HTRequest_method(request)) ?
1157: HTMIMERequest_new(request,
1158: HTTPRequest_new(request, (HTStream *) output, NO,
1159: version),
1160: YES) :
1161: HTTPRequest_new(request, (HTStream *) output, YES, version);
1162: HTRequest_setInputStream(request, app);
1163: }
1.88 frystyk 1164:
1.144 frystyk 1165: /*
1166: ** Set up concurrent read/write if this request isn't the
1167: ** source for a PUT or POST. As source we don't start reading
1168: ** before all destinations are ready. If destination then
1169: ** register the input stream and get ready for read
1170: */
1171: if (HTRequest_isDestination(request)) {
1172: HTHost_register(host, net, HTEvent_READ);
1173: HTRequest_linkDestination(request);
1174: }
1175: http->state = HTTP_CONNECTED;
1176: type = HTEvent_WRITE; /* fresh, so try a write */
1.71 frystyk 1177: break;
1178:
1.87 frystyk 1179: /* As we can do simultanous read and write this is now one state */
1.141 frystyk 1180: case HTTP_CONNECTED:
1181: if (type == HTEvent_WRITE) {
1.157 frystyk 1182: HTStream * input = HTRequest_inputStream(request);
1183: HTPostCallback * pcbf = HTRequest_postCallback(request);
1184: status = HTRequest_flush(request) ?
1185: HTHost_forceFlush(host) : (*input->isa->flush)(input);
1186:
1187: /*
1188: ** Check to see if we are uploading something or just a normal
1189: ** GET kind of thing.
1190: */
1191: if (pcbf) {
1192: if (http->lock == NO) {
1193: int retrys = HTRequest_retrys(request);
1.169 frystyk 1194: ms_t delay = retrys > 3 ? HTSecondWriteDelay : HTFirstWriteDelay;
1.165 frystyk 1195: if (!http->timer && !http->usedTimer) {
1.162 frystyk 1196: http->timer = HTTimer_new(NULL, FlushPutEvent,
1197: http, delay, YES, NO);
1.178 frystyk 1198: HTTRACE(PROT_TRACE, "Uploading... Holding %p for %lu ms using time %p\n" _
1199: http _ delay _ http->timer);
1.157 frystyk 1200: HTHost_register(host, net, HTEvent_READ);
1201: }
1202: http->lock = YES;
1203: }
1204: type = HTEvent_READ;
1205: } else {
1206:
1207: /*
1208: ** Check to see if we can start a new request
1209: ** pending in the host object.
1210: */
1211: HTHost_launchPending(host);
1212: type = HTEvent_READ;
1213: }
1214:
1215: /* Now check the status code */
1216: if (status == HT_WOULD_BLOCK)
1217: return HT_OK;
1218: else if (status == HT_PAUSE || status == HT_LOADED) {
1219: type = HTEvent_READ;
1.168 frystyk 1220: } else if (status==HT_ERROR)
1.157 frystyk 1221: http->state = HTTP_RECOVER_PIPE;
1222: } else if (type == HTEvent_FLUSH) {
1223: HTStream * input = HTRequest_inputStream(request);
1224: if (input == NULL)
1225: return HT_ERROR;
1226: return (*input->isa->flush)(input);
1227: } else if (type == HTEvent_READ) {
1228: status = HTHost_read(host, net);
1229: if (status == HT_WOULD_BLOCK)
1230: return HT_OK;
1231: else if (status == HT_CONTINUE) {
1.178 frystyk 1232: HTTRACE(PROT_TRACE, "HTTP........ Continuing\n");
1.157 frystyk 1233: http->lock = NO;
1234: continue;
1235: } else if (status==HT_LOADED)
1236: http->state = http->next; /* Jump to next state (OK or ERROR) */
1237: else if (status==HT_CLOSED)
1238: http->state = HTTP_RECOVER_PIPE;
1.167 frystyk 1239: else if (status == HT_ERROR)
1240: http->state = HTTP_KILL_PIPE;
1.157 frystyk 1241: else
1242: http->state = HTTP_ERROR;
1243: } else {
1244: http->state = HTTP_ERROR; /* don't know how to handle OOB */
1245: }
1246: break;
1.141 frystyk 1247:
1.134 frystyk 1248: case HTTP_OK:
1249: HTTPCleanup(request, http->result);
1.94 frystyk 1250: return HT_OK;
1.71 frystyk 1251: break;
1.141 frystyk 1252:
1253: case HTTP_RECOVER_PIPE:
1254: {
1255: /*
1256: ** If this is a persistent connection and we get a close
1257: ** then it is an error and we should recover from it by
1258: ** restarting the pipe line of requests if any
1259: */
1.151 frystyk 1260: if (HTHost_isPersistent(host) && !HTHost_closeNotification(host)) {
1.143 frystyk 1261: if (host == NULL) return HT_ERROR;
1.144 frystyk 1262: HTRequest_setFlush(request, YES);
1263: HTHost_recoverPipe(host);
1264: return HT_OK;
1.141 frystyk 1265: } else
1266: http->state = HTTP_OK;
1267: }
1.143 frystyk 1268: break;
1.141 frystyk 1269:
1.167 frystyk 1270: case HTTP_KILL_PIPE:
1271: if (host == NULL) return HT_ERROR;
1272: HTHost_killPipe(host);
1273: return HT_OK;
1274: break;
1275:
1.71 frystyk 1276: case HTTP_ERROR:
1.143 frystyk 1277: HTTPCleanup(request, http->result);
1278: return HT_OK;
1279: break;
1280:
1.141 frystyk 1281: default:
1.178 frystyk 1282: HTDEBUGBREAK("Bad http state %d\n" _ http->state);
1.71 frystyk 1283: }
1284: } /* End of while(1) */
1285: }
1.88 frystyk 1286:
1.147 frystyk 1287: PUBLIC void HTTP_setConnectionMode (HTTPConnectionMode mode)
1288: {
1289: ConnectionMode = mode;
1290: }
1291:
1292: PUBLIC HTTPConnectionMode HTTP_connectionMode (void)
1293: {
1294: return ConnectionMode;
1295: }
1.21 luotonen 1296:
1.169 frystyk 1297: PUBLIC BOOL HTTP_setBodyWriteDelay (ms_t first_try, ms_t second_try)
1298: {
1299: if (first_try > 20 && second_try >= first_try) {
1300: HTFirstWriteDelay = first_try;
1301: HTSecondWriteDelay = second_try;
1302: return YES;
1303: }
1304: return NO;
1305: }
1306:
1307: PUBLIC void HTTP_bodyWriteDelay (ms_t * first_try, ms_t * second_try)
1308: {
1309: *first_try = HTFirstWriteDelay;
1310: *second_try = HTSecondWriteDelay;
1.170 frystyk 1311: }
1312:
Webmaster