Annotation of libwww/Library/src/HTSocket.c, revision 2.10
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: */
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");
2.7 frystyk 63: isoc->sockfd = file_number;
64: isoc->write = isoc->read = isoc->buffer;
2.1 frystyk 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 {
2.7 frystyk 74: if (isoc->write >= isoc->read) {
75: int status = NETREAD(isoc->sockfd, isoc->buffer,INPUT_BUFFER_SIZE);
2.1 frystyk 76: if (status <= 0) {
77: if (status == 0)
78: return EOF;
79: if (PROT_TRACE)
80: fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
81: return EOF; /* -1 is returned by UCX at end of HTTP link */
82: }
2.7 frystyk 83: isoc->write = isoc->buffer;
84: isoc->read = isoc->buffer + status;
2.1 frystyk 85: }
2.7 frystyk 86: ch = (unsigned char) *isoc->write++;
2.1 frystyk 87: } while (ch == 13); /* Ignore ASCII carriage return */
88:
89: return FROMASCII(ch);
90: }
91:
92: PUBLIC void HTInputSocket_free ARGS1(HTInputSocket *, me)
93: {
94: if (me) free(me);
95: }
96:
97:
98: PUBLIC char * HTInputSocket_getBlock ARGS2(HTInputSocket*, isoc,
99: int *, len)
100: {
2.7 frystyk 101: if (isoc->write >= isoc->read) {
102: int status = NETREAD(isoc->sockfd,
103: isoc->buffer,
2.1 frystyk 104: ((*len < INPUT_BUFFER_SIZE) ?
105: *len : INPUT_BUFFER_SIZE));
106: if (status <= 0) {
2.7 frystyk 107: isoc->read = isoc->buffer;
2.1 frystyk 108: if (status < 0) {
109: if (PROT_TRACE)
110: fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
111: }
112: *len = 0;
113: return NULL;
114: }
115: else {
116: *len = status;
2.7 frystyk 117: return isoc->buffer;
2.1 frystyk 118: }
119: }
120: else {
2.7 frystyk 121: char * ret = isoc->write;
122: *len = isoc->read - isoc->write;
123: isoc->write = isoc->read;
2.1 frystyk 124: return ret;
125: }
126: }
127:
128:
129: PRIVATE int fill_in_buffer ARGS1(HTInputSocket *, isoc)
130: {
131: if (isoc) {
132: int status;
133:
2.7 frystyk 134: isoc->write = isoc->buffer;
135: status = NETREAD(isoc->sockfd,
136: isoc->buffer,
2.1 frystyk 137: INPUT_BUFFER_SIZE);
138: if (status <= 0) {
2.7 frystyk 139: isoc->read = isoc->buffer;
2.1 frystyk 140: if (status < 0) {
141: if (PROT_TRACE)
142: fprintf(TDEST, "Read Socket. READ ERROR %d\n", socerrno);
143: }
144: }
145: else
2.7 frystyk 146: isoc->read = isoc->buffer + status;
2.1 frystyk 147: return status;
148: }
149: return -1;
150: }
151:
152:
153: PRIVATE void ascii_cat ARGS3(char **, linep,
154: char *, start,
155: char *, end)
156: {
157: if (linep && start && end && start <= end) {
158: char *ptr;
159:
160: if (*linep) {
161: int len = strlen(*linep);
162: *linep = (char*)realloc(*linep, len + end-start + 1);
163: ptr = *linep + len;
164: }
165: else {
166: ptr = *linep = (char*)malloc(end-start + 1);
167: }
168:
169: while (start < end) {
170: *ptr = FROMASCII(*start);
171: ptr++;
172: start++;
173: }
174: *ptr = 0;
175: }
176: }
177:
178:
179: PRIVATE char * get_some_line ARGS2(HTInputSocket *, isoc,
180: BOOL, unfold)
181: {
182: if (!isoc)
183: return NULL;
184: else {
185: BOOL check_unfold = NO;
186: int prev_cr = 0;
2.7 frystyk 187: char *start = isoc->write;
188: char *cur = isoc->write;
2.1 frystyk 189: char * line = NULL;
190:
191: for(;;) {
192: /*
193: ** Get more if needed to complete line
194: */
2.7 frystyk 195: if (cur >= isoc->read) { /* Need more data */
2.1 frystyk 196: ascii_cat(&line, start, cur);
197: if (fill_in_buffer(isoc) <= 0)
198: return line;
2.7 frystyk 199: start = cur = isoc->write;
2.1 frystyk 200: } /* if need more data */
201:
202: /*
203: ** Find a line feed if there is one
204: */
2.7 frystyk 205: for(; cur < isoc->read; cur++) {
2.1 frystyk 206: char c = FROMASCII(*cur);
207: if (!c) {
208: if (line) free(line); /* Leak fixed AL 6 Feb 94 */
209: return NULL; /* Panic! read a 0! */
210: }
211: if (check_unfold && c != ' ' && c != '\t') {
2.7 frystyk 212: return line; /* Note: didn't update isoc->write */
2.1 frystyk 213: }
214: else {
215: check_unfold = NO;
216: }
217:
218: if (c=='\r') {
219: prev_cr = 1;
220: }
221: else {
222: if (c=='\n') { /* Found a line feed */
223: ascii_cat(&line, start, cur-prev_cr);
2.7 frystyk 224: start = isoc->write = cur+1;
2.1 frystyk 225:
226: if (line && (int) strlen(line) > 0 && unfold) {
227: check_unfold = YES;
228: }
229: else {
230: return line;
231: }
232: } /* if NL */
233: /* else just a regular character */
234: prev_cr = 0;
235: } /* if not CR */
236: } /* while characters in buffer remain */
237: } /* until line read or end-of-file */
238: } /* valid parameters to function */
239: }
240:
241: /* The returned string must be freed by the caller */
242: PUBLIC char * HTInputSocket_getLine ARGS1(HTInputSocket *, isoc)
243: {
244: return get_some_line(isoc, NO);
245: }
246:
247: /* The returned string must be freed by the caller */
248: PUBLIC char * HTInputSocket_getUnfoldedLine ARGS1(HTInputSocket *, isoc)
249: {
250: return get_some_line(isoc, YES);
251: }
252:
253:
254: /* Push data from a socket down a stream
255: ** -------------------------------------
256: **
257: ** This routine is responsible for creating and PRESENTING any
258: ** graphic (or other) objects described by the file.
259: **
260: ** The file number given is assumed to be a TELNET stream ie containing
261: ** CRLF at the end of lines which need to be stripped to LF for unix
262: ** when the format is textual.
263: **
264: ** RETURNS the number of bytes transferred.
265: **
266: */
267: PUBLIC int HTCopy ARGS2(
268: SOCKFD, file_number,
269: HTStream*, sink)
270: {
271: HTStreamClass targetClass;
272: HTInputSocket * isoc;
273: int cnt = 0;
274:
275: /* Push the data down the stream
276: **
277: */
278: targetClass = *(sink->isa); /* Copy pointers to procedures */
279: isoc = HTInputSocket_new(file_number);
280:
281: /* Push binary from socket down sink
282: **
283: ** This operation could be put into a main event loop
284: */
285: for(;;) {
286: int status = NETREAD(
2.7 frystyk 287: file_number, isoc->buffer, INPUT_BUFFER_SIZE);
2.1 frystyk 288: if (status <= 0) {
289: if (status == 0) break;
290: if (TRACE) fprintf(TDEST,
291: "Socket Copy. Read error, read returns %d with errno=%d\n",
292: status, socerrno);
293: break;
294: }
295:
296: #ifdef NOT_ASCII
297: {
298: char * p;
2.7 frystyk 299: for(p = isoc->buffer; p < isoc->buffer+status; p++) {
2.1 frystyk 300: *p = FROMASCII(*p);
301: }
302: }
303: #endif
304:
2.7 frystyk 305: (*targetClass.put_block)(sink, isoc->buffer, status);
2.1 frystyk 306: cnt += status;
307: } /* next bufferload */
308:
309: HTInputSocket_free(isoc);
310:
311: return cnt;
312: }
313:
314:
315:
316: /* Push data from a file pointer down a stream
317: ** -------------------------------------
318: **
319: ** This routine is responsible for creating and PRESENTING any
320: ** graphic (or other) objects described by the file.
321: **
322: **
323: */
324: PUBLIC void HTFileCopy ARGS2(
325: FILE *, fp,
326: HTStream*, sink)
327: {
328: HTStreamClass targetClass;
2.7 frystyk 329: char buffer[INPUT_BUFFER_SIZE];
2.1 frystyk 330:
331: /* Push the data down the stream
332: **
333: */
334: targetClass = *(sink->isa); /* Copy pointers to procedures */
335:
336: /* Push binary from socket down sink
337: */
338: for(;;) {
339: int status = fread(
2.7 frystyk 340: buffer, 1, INPUT_BUFFER_SIZE, fp);
2.1 frystyk 341: if (status == 0) { /* EOF or error */
342: if (ferror(fp) == 0) break;
343: if (TRACE) fprintf(TDEST,
344: "File Copy... Read error, read returns %d\n", ferror(fp));
345: break;
346: }
2.7 frystyk 347: (*targetClass.put_block)(sink, buffer, status);
2.1 frystyk 348: } /* next bufferload */
349: }
350:
351:
352:
353:
354: /* Push data from a socket down a stream STRIPPING CR
355: ** --------------------------------------------------
356: **
357: ** This routine is responsible for creating and PRESENTING any
358: ** graphic (or other) objects described by the socket.
359: **
360: ** The file number given is assumed to be a TELNET stream ie containing
361: ** CRLF at the end of lines which need to be stripped to LF for unix
362: ** when the format is textual.
363: **
364: ** Character handling is now of type int, Henrik, May 09-94
365: */
366: PUBLIC void HTCopyNoCR ARGS2(
367: SOCKFD, file_number,
368: HTStream*, sink)
369: {
370: HTStreamClass targetClass;
371: HTInputSocket * isoc;
372: int ch;
373:
374: /* Push the data, ignoring CRLF, down the stream
375: **
376: */
377: targetClass = *(sink->isa); /* Copy pointers to procedures */
378:
379: /* Push text from telnet socket down sink
380: **
381: ** @@@@@ To push strings could be faster? (especially is we
382: ** cheat and don't ignore CR! :-}
383: */
384: isoc = HTInputSocket_new(file_number);
385: while ((ch = HTInputSocket_getCharacter(isoc)) >= 0)
386: (*targetClass.put_character)(sink, ch);
387: HTInputSocket_free(isoc);
388: }
389:
390:
391: /* Parse a socket given format and file number
392: **
393: ** This routine is responsible for creating and PRESENTING any
394: ** graphic (or other) objects described by the file.
395: **
396: ** The file number given is assumed to be a TELNET stream ie containing
397: ** CRLF at the end of lines which need to be stripped to LF for unix
398: ** when the format is textual.
399: **
400: ** Returns <0 on error, HT_LOADED on success.
401: */
402:
403: /* The parameter to this function and HTParsefile should be HTRequest */
404:
405: PUBLIC int HTParseSocket ARGS3(
406: HTFormat, rep_in,
407: SOCKFD, file_number,
408: HTRequest *, request)
409: {
410: HTStream * stream;
411: HTStreamClass targetClass;
412:
413: if (request->error_stack) {
414: if (TRACE) fprintf(TDEST, "ParseSocket. Called whith non-empty error stack, so I return right away!\n");
415: return -1;
416: }
417:
418: /* Set up stream stack */
419: if ((stream = HTStreamStack(rep_in, request->output_format,
420: request->output_stream,
421: request, YES)) == NULL)
422: return -1;
423:
424: /* Push the data, ignoring CRLF if necessary, down the stream
425: **
426: **
427: ** @@ Bug: This decision ought to be made based on "encoding"
428: ** rather than on format. @@@ When we handle encoding.
429: ** The current method smells anyway.
430: */
431: targetClass = *(stream->isa); /* Copy pointers to procedures */
432: if (rep_in == WWW_BINARY || rep_in == WWW_UNKNOWN
433: || (HTAnchor_encoding(request->anchor) != HTAtom_for("8bit") &&
434: HTAnchor_encoding(request->anchor) != HTAtom_for("7bit"))
435: || strstr(HTAtom_name(rep_in), "image/")
436: || strstr(HTAtom_name(rep_in), "video/")) { /* @@@@@@ */
437: HTCopy(file_number, stream);
438: } else
439: HTCopyNoCR(file_number, stream);
440: (*targetClass._free)(stream);
441:
442: return HT_LOADED;
443: }
444:
445:
446:
447: /* Parse a file given format and file pointer
448: **
449: ** This routine is responsible for creating and PRESENTING any
450: ** graphic (or other) objects described by the file.
451: **
452: ** The file number given is assumed to be a TELNET stream ie containing
453: ** CRLF at the end of lines which need to be stripped to \n for unix
454: ** when the format is textual.
455: **
456: */
2.2 frystyk 457: PRIVATE int HTParseFile ARGS3(
2.1 frystyk 458: HTFormat, rep_in,
459: FILE *, fp,
460: HTRequest *, request)
461: {
462: HTStream * stream;
463: HTStreamClass targetClass;
464:
465: if (request->error_stack) {
466: if (TRACE) fprintf(TDEST, "ParseFile... Called whith non-empty error stack, so I return right away!\n");
467: return -1;
468: }
469:
470: /* Set up stream stack */
471: if ((stream = HTStreamStack(rep_in, request->output_format,
472: request->output_stream, request, YES)) == NULL)
473: return -1;
474:
475: /* Push the data down the stream
476: **
477: **
478: ** @@ Bug: This decision ought to be made based on "encoding"
479: ** rather than on content-type. @@@ When we handle encoding.
480: ** The current method smells anyway.
481: */
482: targetClass = *(stream->isa); /* Copy pointers to procedures */
483: HTFileCopy(fp, stream);
484: (*targetClass._free)(stream);
485:
486: return HT_LOADED;
487: }
488:
489:
490: /* ------------------------------------------------------------------------- */
491: /* MULTI THREADED IMPLEMENTATIONS */
492: /* ------------------------------------------------------------------------- */
493:
494: /* Push data from a socket down a stream
495: ** -------------------------------------
496: **
497: ** This routine is responsible for creating and PRESENTING any
498: ** graphic (or other) objects described by the file. As this function
499: ** max reads a chunk of data on size INPUT_BUFFER_SIZE, it can be used
500: ** with both blocking or non-blocking sockets. It will always return to
501: ** the event loop, however if we are using blocking I/O then we get a full
502: ** buffer read, otherwise we get what's available.
503: **
504: ** Returns HT_LOADED if finished reading
2.3 frystyk 505: ** HT_OK if OK, but more to read
2.1 frystyk 506: ** HT_ERROR if error,
507: ** HT_WOULD_BLOCK if read would block
508: */
2.10 ! frystyk 509: PUBLIC int HTSocketRead (HTRequest * request, HTNet * net)
2.1 frystyk 510: {
2.7 frystyk 511: HTInputSocket *isoc = net->isoc;
2.10 ! frystyk 512: HTStream *target = net->target;
2.7 frystyk 513: int b_read = isoc->read - isoc->buffer;
2.1 frystyk 514: int status;
2.7 frystyk 515: if (!isoc || isoc->sockfd==INVSOC) {
2.1 frystyk 516: if (PROT_TRACE) fprintf(TDEST, "Read Socket. Bad argument\n");
517: return HT_ERROR;
518: }
519:
2.3 frystyk 520: /* Read from socket if we got rid of all the data previously read */
2.4 frystyk 521: do {
2.7 frystyk 522: if (isoc->write >= isoc->read) {
523: if ((b_read = NETREAD(isoc->sockfd, isoc->buffer,
2.4 frystyk 524: INPUT_BUFFER_SIZE)) < 0) {
2.1 frystyk 525: #ifdef EAGAIN
2.4 frystyk 526: if (socerrno==EAGAIN || socerrno==EWOULDBLOCK) /* POSIX */
2.1 frystyk 527: #else
2.4 frystyk 528: if (socerrno==EWOULDBLOCK) /* BSD */
2.1 frystyk 529: #endif
2.4 frystyk 530: {
531: if (PROT_TRACE)
532: fprintf(TDEST, "Read Socket. WOULD BLOCK soc %d\n",
2.7 frystyk 533: isoc->sockfd);
534: HTEvent_Register(isoc->sockfd, request,
535: (SockOps) FD_READ, net->cbf,
536: net->priority);
2.4 frystyk 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: }
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