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