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