Annotation of libwww/Library/src/HTSocket.c, revision 2.12
2.1 frystyk 1: /* HTSocket.c
2: ** MANAGES READ AND WRITE TO AND FROM THE NETWORK
3: **
4: ** (c) COPYRIGHT MIT 1995.
5: ** Please first read the full copyright statement in the file COPYRIGH.
6: **
7: **
8: ** HISTORY:
9: ** 6 June 95 HFN Spawned off from HTFormat
10: */
11:
12: /* Library Include files */
13: #include "tcp.h"
14: #include "HTUtils.h"
15: #include "HTString.h"
2.7 frystyk 16: #include "HTReqMan.h"
2.4 frystyk 17: #include "HTProt.h"
2.1 frystyk 18: #include "HTTCP.h"
19: #include "HTStream.h"
2.6 frystyk 20: #include "HTAlert.h"
2.1 frystyk 21: #include "HTFormat.h"
2.8 frystyk 22: #include "HTNetMan.h"
2.1 frystyk 23: #include "HTError.h"
24: #include "HTSocket.h" /* Implemented here */
25:
2.2 frystyk 26: struct _HTInputSocket {
2.7 frystyk 27: char buffer[INPUT_BUFFER_SIZE];
28: char * write; /* Last byte written */
29: char * read; /* Last byte read */
30: SOCKFD sockfd;
2.2 frystyk 31: };
32:
2.1 frystyk 33: struct _HTStream {
34: CONST HTStreamClass * isa;
35: };
36:
37: /* ------------------------------------------------------------------------- */
38: /* SOCKET INPUT BUFFERING */
39: /* ------------------------------------------------------------------------- */
40: /*
41: ** This code is used because one cannot in general open a
42: ** file descriptor for a socket.
43: **
44: ** The input file is read using the macro which can read from
45: ** a socket or a file, but this should not be used for files
46: ** as fopen() etc is more portable of course.
47: **
48: ** The input buffer size, if large will give greater efficiency and
49: ** release the server faster, and if small will save space on PCs etc.
50: */
51:
52:
53: /* Set up the buffering
54: **
55: ** These routines are public because they are in fact needed by
56: ** many parsers, and on PCs and Macs we should not duplicate
57: ** the static buffer area.
58: */
2.12 ! frystyk 59: PUBLIC HTInputSocket * HTInputSocket_new (SOCKFD file_number)
2.1 frystyk 60: {
61: HTInputSocket *isoc = (HTInputSocket *)calloc(1, sizeof(*isoc));
62: if (!isoc) outofmem(__FILE__, "HTInputSocket_new");
2.7 frystyk 63: isoc->sockfd = file_number;
64: isoc->write = isoc->read = isoc->buffer;
2.1 frystyk 65: return isoc;
66: }
67:
2.12 ! frystyk 68: PUBLIC void HTInputSocket_free (HTInputSocket * me)
! 69: {
! 70: if (me) free(me);
! 71: }
! 72:
! 73: /* ------------------------------------------------------------------------- */
! 74:
2.1 frystyk 75: /* This should return HT_INTERRUPTED if interrupted BUT the connection
76: MUST not be closed */
77: PUBLIC int HTInputSocket_getCharacter ARGS1(HTInputSocket*, isoc)
78: {
79: int ch;
80: do {
2.7 frystyk 81: if (isoc->write >= isoc->read) {
82: int status = NETREAD(isoc->sockfd, isoc->buffer,INPUT_BUFFER_SIZE);
2.1 frystyk 83: if (status <= 0) {
84: if (status == 0)
85: return EOF;
86: if (PROT_TRACE)
87: fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
88: return EOF; /* -1 is returned by UCX at end of HTTP link */
89: }
2.7 frystyk 90: isoc->write = isoc->buffer;
91: isoc->read = isoc->buffer + status;
2.1 frystyk 92: }
2.7 frystyk 93: ch = (unsigned char) *isoc->write++;
2.1 frystyk 94: } while (ch == 13); /* Ignore ASCII carriage return */
95:
96: return FROMASCII(ch);
97: }
98:
99: PUBLIC char * HTInputSocket_getBlock ARGS2(HTInputSocket*, isoc,
100: int *, len)
101: {
2.7 frystyk 102: if (isoc->write >= isoc->read) {
103: int status = NETREAD(isoc->sockfd,
104: isoc->buffer,
2.1 frystyk 105: ((*len < INPUT_BUFFER_SIZE) ?
106: *len : INPUT_BUFFER_SIZE));
107: if (status <= 0) {
2.7 frystyk 108: isoc->read = isoc->buffer;
2.1 frystyk 109: if (status < 0) {
110: if (PROT_TRACE)
111: fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
112: }
113: *len = 0;
114: return NULL;
115: }
116: else {
117: *len = status;
2.7 frystyk 118: return isoc->buffer;
2.1 frystyk 119: }
120: }
121: else {
2.7 frystyk 122: char * ret = isoc->write;
123: *len = isoc->read - isoc->write;
124: isoc->write = isoc->read;
2.1 frystyk 125: return ret;
126: }
127: }
128:
129:
130: PRIVATE int fill_in_buffer ARGS1(HTInputSocket *, isoc)
131: {
132: if (isoc) {
133: int status;
134:
2.7 frystyk 135: isoc->write = isoc->buffer;
136: status = NETREAD(isoc->sockfd,
137: isoc->buffer,
2.1 frystyk 138: INPUT_BUFFER_SIZE);
139: if (status <= 0) {
2.7 frystyk 140: isoc->read = isoc->buffer;
2.1 frystyk 141: if (status < 0) {
142: if (PROT_TRACE)
143: fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
144: }
145: }
146: else
2.7 frystyk 147: isoc->read = isoc->buffer + status;
2.1 frystyk 148: return status;
149: }
150: return -1;
151: }
152:
153:
154: PRIVATE void ascii_cat ARGS3(char **, linep,
155: char *, start,
156: char *, end)
157: {
158: if (linep && start && end && start <= end) {
159: char *ptr;
160:
161: if (*linep) {
162: int len = strlen(*linep);
163: *linep = (char*)realloc(*linep, len + end-start + 1);
164: ptr = *linep + len;
165: }
166: else {
167: ptr = *linep = (char*)malloc(end-start + 1);
168: }
169:
170: while (start < end) {
171: *ptr = FROMASCII(*start);
172: ptr++;
173: start++;
174: }
175: *ptr = 0;
176: }
177: }
178:
179:
180: PRIVATE char * get_some_line ARGS2(HTInputSocket *, isoc,
181: BOOL, unfold)
182: {
183: if (!isoc)
184: return NULL;
185: else {
186: BOOL check_unfold = NO;
187: int prev_cr = 0;
2.7 frystyk 188: char *start = isoc->write;
189: char *cur = isoc->write;
2.1 frystyk 190: char * line = NULL;
191:
192: for(;;) {
193: /*
194: ** Get more if needed to complete line
195: */
2.7 frystyk 196: if (cur >= isoc->read) { /* Need more data */
2.1 frystyk 197: ascii_cat(&line, start, cur);
198: if (fill_in_buffer(isoc) <= 0)
199: return line;
2.7 frystyk 200: start = cur = isoc->write;
2.1 frystyk 201: } /* if need more data */
202:
203: /*
204: ** Find a line feed if there is one
205: */
2.7 frystyk 206: for(; cur < isoc->read; cur++) {
2.1 frystyk 207: char c = FROMASCII(*cur);
208: if (!c) {
209: if (line) free(line); /* Leak fixed AL 6 Feb 94 */
210: return NULL; /* Panic! read a 0! */
211: }
212: if (check_unfold && c != ' ' && c != '\t') {
2.7 frystyk 213: return line; /* Note: didn't update isoc->write */
2.1 frystyk 214: }
215: else {
216: check_unfold = NO;
217: }
218:
219: if (c=='\r') {
220: prev_cr = 1;
221: }
222: else {
223: if (c=='\n') { /* Found a line feed */
224: ascii_cat(&line, start, cur-prev_cr);
2.7 frystyk 225: start = isoc->write = cur+1;
2.1 frystyk 226:
227: if (line && (int) strlen(line) > 0 && unfold) {
228: check_unfold = YES;
229: }
230: else {
231: return line;
232: }
233: } /* if NL */
234: /* else just a regular character */
235: prev_cr = 0;
236: } /* if not CR */
237: } /* while characters in buffer remain */
238: } /* until line read or end-of-file */
239: } /* valid parameters to function */
240: }
241:
242: /* The returned string must be freed by the caller */
243: PUBLIC char * HTInputSocket_getLine ARGS1(HTInputSocket *, isoc)
244: {
245: return get_some_line(isoc, NO);
246: }
247:
248: /* The returned string must be freed by the caller */
249: PUBLIC char * HTInputSocket_getUnfoldedLine ARGS1(HTInputSocket *, isoc)
250: {
251: return get_some_line(isoc, YES);
252: }
253:
254:
255: /* Push data from a socket down a stream
256: ** -------------------------------------
257: **
258: ** This routine is responsible for creating and PRESENTING any
259: ** graphic (or other) objects described by the file.
260: **
261: ** The file number given is assumed to be a TELNET stream ie containing
262: ** CRLF at the end of lines which need to be stripped to LF for unix
263: ** when the format is textual.
264: **
265: ** RETURNS the number of bytes transferred.
266: **
267: */
2.12 ! frystyk 268: PRIVATE int HTCopy ARGS2(
2.1 frystyk 269: SOCKFD, file_number,
270: HTStream*, sink)
271: {
272: HTStreamClass targetClass;
273: HTInputSocket * isoc;
274: int cnt = 0;
275:
276: /* Push the data down the stream
277: **
278: */
279: targetClass = *(sink->isa); /* Copy pointers to procedures */
280: isoc = HTInputSocket_new(file_number);
281:
282: /* Push binary from socket down sink
283: **
284: ** This operation could be put into a main event loop
285: */
286: for(;;) {
287: int status = NETREAD(
2.7 frystyk 288: file_number, isoc->buffer, INPUT_BUFFER_SIZE);
2.1 frystyk 289: if (status <= 0) {
290: if (status == 0) break;
2.12 ! frystyk 291: if (WWWTRACE) fprintf(TDEST,
2.1 frystyk 292: "Socket Copy. Read error, read returns %d with errno=%d\n",
293: status, socerrno);
294: break;
295: }
296:
297: #ifdef NOT_ASCII
298: {
299: char * p;
2.7 frystyk 300: for(p = isoc->buffer; p < isoc->buffer+status; p++) {
2.1 frystyk 301: *p = FROMASCII(*p);
302: }
303: }
304: #endif
305:
2.7 frystyk 306: (*targetClass.put_block)(sink, isoc->buffer, status);
2.1 frystyk 307: cnt += status;
308: } /* next bufferload */
309:
310: HTInputSocket_free(isoc);
311:
312: return cnt;
313: }
314:
315:
316:
317: /* Push data from a file pointer down a stream
318: ** -------------------------------------
319: **
320: ** This routine is responsible for creating and PRESENTING any
321: ** graphic (or other) objects described by the file.
322: **
323: **
324: */
2.12 ! frystyk 325: PRIVATE void HTFileCopy ARGS2(
2.1 frystyk 326: FILE *, fp,
327: HTStream*, sink)
328: {
329: HTStreamClass targetClass;
2.7 frystyk 330: char buffer[INPUT_BUFFER_SIZE];
2.1 frystyk 331:
332: /* Push the data down the stream
333: **
334: */
335: targetClass = *(sink->isa); /* Copy pointers to procedures */
336:
337: /* Push binary from socket down sink
338: */
339: for(;;) {
340: int status = fread(
2.7 frystyk 341: buffer, 1, INPUT_BUFFER_SIZE, fp);
2.1 frystyk 342: if (status == 0) { /* EOF or error */
343: if (ferror(fp) == 0) break;
2.12 ! frystyk 344: if (WWWTRACE) fprintf(TDEST,
2.1 frystyk 345: "File Copy... Read error, read returns %d\n", ferror(fp));
346: break;
347: }
2.7 frystyk 348: (*targetClass.put_block)(sink, buffer, status);
2.1 frystyk 349: } /* next bufferload */
350: }
351:
352:
353:
354:
355: /* Push data from a socket down a stream STRIPPING CR
356: ** --------------------------------------------------
357: **
358: ** This routine is responsible for creating and PRESENTING any
359: ** graphic (or other) objects described by the socket.
360: **
361: ** The file number given is assumed to be a TELNET stream ie containing
362: ** CRLF at the end of lines which need to be stripped to LF for unix
363: ** when the format is textual.
364: **
365: ** Character handling is now of type int, Henrik, May 09-94
366: */
2.12 ! frystyk 367: PRIVATE void HTCopyNoCR ARGS2(
2.1 frystyk 368: SOCKFD, file_number,
369: HTStream*, sink)
370: {
371: HTStreamClass targetClass;
372: HTInputSocket * isoc;
373: int ch;
374:
375: /* Push the data, ignoring CRLF, down the stream
376: **
377: */
378: targetClass = *(sink->isa); /* Copy pointers to procedures */
379:
380: /* Push text from telnet socket down sink
381: **
382: ** @@@@@ To push strings could be faster? (especially is we
383: ** cheat and don't ignore CR! :-}
384: */
385: isoc = HTInputSocket_new(file_number);
386: while ((ch = HTInputSocket_getCharacter(isoc)) >= 0)
387: (*targetClass.put_character)(sink, ch);
388: HTInputSocket_free(isoc);
389: }
390:
391:
392: /* Parse a socket given format and file number
393: **
394: ** This routine is responsible for creating and PRESENTING any
395: ** graphic (or other) objects described by the file.
396: **
397: ** The file number given is assumed to be a TELNET stream ie containing
398: ** CRLF at the end of lines which need to be stripped to LF for unix
399: ** when the format is textual.
400: **
401: ** Returns <0 on error, HT_LOADED on success.
402: */
403:
404: /* The parameter to this function and HTParsefile should be HTRequest */
405:
406: PUBLIC int HTParseSocket ARGS3(
407: HTFormat, rep_in,
408: SOCKFD, file_number,
409: HTRequest *, request)
410: {
411: HTStream * stream;
412: HTStreamClass targetClass;
413:
414: if (request->error_stack) {
2.12 ! frystyk 415: if (WWWTRACE) fprintf(TDEST, "ParseSocket. Called whith non-empty error stack, so I return right away!\n");
2.1 frystyk 416: return -1;
417: }
418:
419: /* Set up stream stack */
420: if ((stream = HTStreamStack(rep_in, request->output_format,
421: request->output_stream,
422: request, YES)) == NULL)
423: return -1;
424:
425: /* Push the data, ignoring CRLF if necessary, down the stream
426: **
427: **
428: ** @@ Bug: This decision ought to be made based on "encoding"
429: ** rather than on format. @@@ When we handle encoding.
430: ** The current method smells anyway.
431: */
432: targetClass = *(stream->isa); /* Copy pointers to procedures */
433: if (rep_in == WWW_BINARY || rep_in == WWW_UNKNOWN
434: || (HTAnchor_encoding(request->anchor) != HTAtom_for("8bit") &&
435: HTAnchor_encoding(request->anchor) != HTAtom_for("7bit"))
436: || strstr(HTAtom_name(rep_in), "image/")
437: || strstr(HTAtom_name(rep_in), "video/")) { /* @@@@@@ */
438: HTCopy(file_number, stream);
439: } else
440: HTCopyNoCR(file_number, stream);
441: (*targetClass._free)(stream);
442:
443: return HT_LOADED;
444: }
445:
446:
2.12 ! frystyk 447: #if 0
2.1 frystyk 448: /* Parse a file given format and file pointer
449: **
450: ** This routine is responsible for creating and PRESENTING any
451: ** graphic (or other) objects described by the file.
452: **
453: ** The file number given is assumed to be a TELNET stream ie containing
454: ** CRLF at the end of lines which need to be stripped to \n for unix
455: ** when the format is textual.
456: **
457: */
2.2 frystyk 458: PRIVATE int HTParseFile ARGS3(
2.1 frystyk 459: HTFormat, rep_in,
460: FILE *, fp,
461: HTRequest *, request)
462: {
463: HTStream * stream;
464: HTStreamClass targetClass;
465:
466: if (request->error_stack) {
2.12 ! frystyk 467: if (WWWTRACE) fprintf(TDEST, "ParseFile... Called whith non-empty error stack, so I return right away!\n");
2.1 frystyk 468: return -1;
469: }
470:
471: /* Set up stream stack */
472: if ((stream = HTStreamStack(rep_in, request->output_format,
473: request->output_stream, request, YES)) == NULL)
474: return -1;
475:
476: /* Push the data down the stream
477: **
478: **
479: ** @@ Bug: This decision ought to be made based on "encoding"
480: ** rather than on content-type. @@@ When we handle encoding.
481: ** The current method smells anyway.
482: */
483: targetClass = *(stream->isa); /* Copy pointers to procedures */
484: HTFileCopy(fp, stream);
485: (*targetClass._free)(stream);
486:
487: return HT_LOADED;
488: }
2.12 ! frystyk 489: #endif
2.1 frystyk 490:
491: /* ------------------------------------------------------------------------- */
492: /* MULTI THREADED IMPLEMENTATIONS */
493: /* ------------------------------------------------------------------------- */
494:
495: /* Push data from a socket down a stream
496: ** -------------------------------------
497: **
498: ** This routine is responsible for creating and PRESENTING any
499: ** graphic (or other) objects described by the file. As this function
500: ** max reads a chunk of data on size INPUT_BUFFER_SIZE, it can be used
501: ** with both blocking or non-blocking sockets. It will always return to
502: ** the event loop, however if we are using blocking I/O then we get a full
503: ** buffer read, otherwise we get what's available.
504: **
505: ** Returns HT_LOADED if finished reading
2.3 frystyk 506: ** HT_OK if OK, but more to read
2.1 frystyk 507: ** HT_ERROR if error,
508: ** HT_WOULD_BLOCK if read would block
509: */
2.10 frystyk 510: PUBLIC int HTSocketRead (HTRequest * request, HTNet * net)
2.1 frystyk 511: {
2.7 frystyk 512: HTInputSocket *isoc = net->isoc;
2.10 frystyk 513: HTStream *target = net->target;
2.7 frystyk 514: int b_read = isoc->read - isoc->buffer;
2.1 frystyk 515: int status;
2.7 frystyk 516: if (!isoc || isoc->sockfd==INVSOC) {
2.1 frystyk 517: if (PROT_TRACE) fprintf(TDEST, "Read Socket. Bad argument\n");
518: return HT_ERROR;
519: }
520:
2.3 frystyk 521: /* Read from socket if we got rid of all the data previously read */
2.4 frystyk 522: do {
2.7 frystyk 523: if (isoc->write >= isoc->read) {
524: if ((b_read = NETREAD(isoc->sockfd, isoc->buffer,
2.4 frystyk 525: INPUT_BUFFER_SIZE)) < 0) {
2.1 frystyk 526: #ifdef EAGAIN
2.4 frystyk 527: if (socerrno==EAGAIN || socerrno==EWOULDBLOCK) /* POSIX */
2.1 frystyk 528: #else
2.11 frystyk 529: if (socerrno==EWOULDBLOCK) /* BSD */
530: #endif
531: {
532: if (PROT_TRACE)
533: fprintf(TDEST, "Read Socket. WOULD BLOCK soc %d\n",
534: isoc->sockfd);
535: HTEvent_Register(isoc->sockfd, request, (SockOps) FD_READ,
536: net->cbf, net->priority);
537: return HT_WOULD_BLOCK;
538: } else { /* We have a real error */
539: if (PROT_TRACE)
540: fprintf(TDEST, "Read Socket. READ ERROR %d\n",
541: socerrno);
542: return HT_ERROR;
543: }
2.4 frystyk 544: } else if (!b_read) {
2.3 frystyk 545: if (PROT_TRACE)
2.4 frystyk 546: fprintf(TDEST, "Read Socket. Finished loading socket %d\n",
2.7 frystyk 547: isoc->sockfd);
2.6 frystyk 548: HTProgress(request, HT_PROG_DONE, NULL);
2.7 frystyk 549: HTEvent_UnRegister(isoc->sockfd, FD_READ);
2.4 frystyk 550: return HT_LOADED;
2.1 frystyk 551: }
552:
2.4 frystyk 553: /* Remember how much we have read from the input socket */
2.7 frystyk 554: isoc->write = isoc->buffer;
555: isoc->read = isoc->buffer + b_read;
2.1 frystyk 556:
557: #ifdef NOT_ASCII
2.4 frystyk 558: {
2.7 frystyk 559: char *p = isoc->buffer;
560: while (p < isoc->read) {
2.4 frystyk 561: *p = FROMASCII(*p);
562: p++;
563: }
2.1 frystyk 564: }
565: #endif
2.3 frystyk 566: if (PROT_TRACE)
2.4 frystyk 567: fprintf(TDEST, "Read Socket. %d bytes read from socket %d\n",
2.7 frystyk 568: b_read, isoc->sockfd);
2.8 frystyk 569: net->bytes_read += b_read;
2.6 frystyk 570: HTProgress(request, HT_PROG_READ, NULL);
2.4 frystyk 571: }
572:
573: /* Now push the data down the stream */
2.7 frystyk 574: if ((status = (*target->isa->put_block)(target, isoc->buffer,
2.4 frystyk 575: b_read)) != HT_OK) {
576: if (status==HT_WOULD_BLOCK) {
577: if (PROT_TRACE)
2.8 frystyk 578: fprintf(TDEST, "Read Socket. Target WOULD BLOCK\n");
2.7 frystyk 579: HTEvent_UnRegister(isoc->sockfd, FD_READ);
2.4 frystyk 580: return HT_WOULD_BLOCK;
2.5 frystyk 581: } else if (status>0) { /* Stream specific return code */
582: if (PROT_TRACE)
2.8 frystyk 583: fprintf(TDEST, "Read Socket. Target returns %d\n", status);
2.10 frystyk 584: isoc->write = isoc->buffer + b_read;
2.5 frystyk 585: return status;
586: } else { /* We have a real error */
2.4 frystyk 587: if (PROT_TRACE)
2.8 frystyk 588: fprintf(TDEST, "Read Socket. Target ERROR\n");
2.4 frystyk 589: return status;
590: }
2.1 frystyk 591: }
2.7 frystyk 592: isoc->write = isoc->buffer + b_read;
593: } while (net->preemtive);
2.10 frystyk 594: HTEvent_Register(isoc->sockfd, request, (SockOps) FD_READ,
595: net->cbf, net->priority);
2.1 frystyk 596: return HT_WOULD_BLOCK;
597: }
2.2 frystyk 598:
599:
600:
601: /* Push data from an ANSI file descriptor down a stream
602: ** ----------------------------------------------------
603: **
604: ** This routine is responsible for creating and PRESENTING any
605: ** graphic (or other) objects described by the file.
606: **
607: ** Bugs: When we can wait on a file then this should also check interrupts!
608: **
609: ** Returns HT_LOADED if finished reading
610: ** HT_ERROR if error,
611: */
2.10 frystyk 612: PUBLIC int HTFileRead (HTRequest * request, HTNet * net, FILE * fp)
2.2 frystyk 613: {
2.10 frystyk 614: HTInputSocket *isoc = net->isoc;
615: HTStream *target = net->target;
2.2 frystyk 616: int b_read;
617: int status;
618: if (!fp) {
619: if (PROT_TRACE) fprintf(TDEST, "Read File... Bad argument\n");
620: return HT_ERROR;
621: }
622:
623: while(1) {
2.7 frystyk 624: if ((b_read = fread(isoc->buffer, 1, INPUT_BUFFER_SIZE, fp))==0){
2.2 frystyk 625: if (ferror(fp)) {
626: if (PROT_TRACE)
627: fprintf(TDEST, "Read File... READ ERROR\n");
628: } else
629: return HT_LOADED;
630: }
2.7 frystyk 631: isoc->write = isoc->buffer;
632: isoc->read = isoc->buffer + b_read;
2.2 frystyk 633: if (PROT_TRACE)
634: fprintf(TDEST, "Read File... %d bytes read from file %p\n",
635: b_read, fp);
636:
637: /* Now push the data down the stream (we use blocking I/O) */
2.7 frystyk 638: if ((status = (*target->isa->put_block)(target, isoc->buffer,
2.2 frystyk 639: b_read)) != HT_OK) {
640: if (PROT_TRACE)
2.8 frystyk 641: fprintf(TDEST, "Read File... Target ERROR\n");
2.2 frystyk 642: return status;
643: }
2.7 frystyk 644: isoc->write = isoc->buffer + b_read;
2.2 frystyk 645: }
646: }
647:
648:
Webmaster