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