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