Annotation of libwww/Library/src/HTSocket.c, revision 2.28

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 */
2.25      frystyk    13: #include "sysdep.h"
2.1       frystyk    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.27      frystyk    26: /* A channel occupies exactly one socket */
                     27: struct _HTChannel {
                     28:     HTChannel *                next;
2.28    ! frystyk    29:     HTChannelMode      mode;
        !            30:     BOOL               active;                 /* Active or passive channel */
2.27      frystyk    31:     SOCKET             sockfd;
                     32:     char *             write;                          /* Last byte written */
                     33:     char *             read;                              /* Last byte read */
2.28    ! frystyk    34:     char               data [CHANNEL_BUFFER_SIZE];                /* buffer */
2.2       frystyk    35: };
                     36: 
2.1       frystyk    37: struct _HTStream {
2.25      frystyk    38:     const HTStreamClass *      isa;
2.1       frystyk    39: };
                     40: 
2.27      frystyk    41: #define PRIME_TABLE_SIZE       67
                     42: #define HASH(s)                        ((s) % PRIME_TABLE_SIZE) 
                     43: 
                     44: PRIVATE HTChannel * channels [PRIME_TABLE_SIZE];
                     45: 
2.28    ! frystyk    46: struct _HTFileBuffer {
        !            47:     char *     write;                                  /* Last byte written */
        !            48:     char *     read;                                      /* Last byte read */
        !            49:     char       data [CHANNEL_BUFFER_SIZE];
        !            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 HTFileBuffer * HTFileBuffer_new (void)
        !            59: {
        !            60:     HTFileBuffer * isoc;
        !            61:     if ((isoc = (HTFileBuffer  *) HT_CALLOC(1, sizeof(*isoc))) == NULL)
        !            62:         HT_OUTOFMEM("HTFileBuffer_new");
        !            63:     isoc->write = isoc->read = isoc->data;
        !            64:     return isoc;
        !            65: }
        !            66: 
        !            67: PUBLIC void HTFileBuffer_free (HTFileBuffer * me)
        !            68: {
        !            69:     HT_FREE(me);
        !            70: }
        !            71: 
2.1       frystyk    72: /* ------------------------------------------------------------------------- */
2.27      frystyk    73: /*                             CHANNEL CONTROL                              */
2.1       frystyk    74: /* ------------------------------------------------------------------------- */
2.27      frystyk    75: 
                     76: /*
                     77: **     Look for a channel object
                     78: */
                     79: PUBLIC HTChannel * HTChannel_find (SOCKET sockfd)
                     80: {
                     81:     if (sockfd != INVSOC) {
                     82:        int hash = HASH(sockfd);
                     83:        HTChannel *ch=NULL;
                     84:        for (ch = channels[hash]; ch; ch=ch->next) {
                     85:            if (ch->sockfd == sockfd) {
                     86:                if (PROT_TRACE) HTTrace("Channel..... Found %p\n", ch);
                     87:                return ch;
                     88:            }
                     89:        }
                     90:     }
                     91:     return NULL;
                     92: }
                     93: 
                     94: /*
                     95: **     A channel is uniquely identified by a socket.
                     96: **     If we are not using mux then a channel is simply a normal TCP socket.
                     97: **     Before creating a new channel, we check to see if it already exists.
                     98: **     You may get back an already existing channel - you're not promised a
2.28    ! frystyk    99: **     new one each time. ANSI C file channels can not be created by using
        !           100: **     the INVSOC socket descriptor.
2.1       frystyk   101: */
2.27      frystyk   102: PUBLIC HTChannel * HTChannel_new (SOCKET sockfd, HTChannelMode mode,
                    103:                                  BOOL active)
                    104: {
                    105:     if (sockfd != INVSOC) {
                    106:        int hash = HASH(sockfd);
                    107:        HTChannel *ch=NULL, **chp=NULL;
                    108:        for (chp = &channels[hash]; (ch = *chp) != NULL ; chp = &ch->next) {
                    109:            if (ch->sockfd == sockfd) break;
                    110:        }
                    111:        if (!ch) {
                    112:            if ((*chp=ch=(HTChannel *) HT_CALLOC(1, sizeof(HTChannel)))==NULL)
                    113:                HT_OUTOFMEM("HTChannel_new");       
                    114:            ch->sockfd = sockfd;
                    115:            ch->write = ch->read = ch->data;
                    116:            ch->mode = mode;
                    117:            ch->active = active;
                    118:            if (PROT_TRACE)
                    119:                HTTrace("Channel..... Created %p with id %d\n", ch,ch->sockfd);
                    120:        } else {
                    121:            if (PROT_TRACE)
                    122:                HTTrace("Channel..... Found %p with id %d\n", ch, ch->sockfd);
                    123:            ch->write = ch->read = ch->data;
                    124:        }
                    125:        return ch;
                    126:     }
                    127:     return NULL;
                    128: }
                    129: 
                    130: PUBLIC BOOL HTChannel_delete (SOCKET sockfd)
                    131: {
                    132:     if (sockfd != INVSOC) {
                    133:        int hash = HASH(sockfd);
                    134:        HTChannel *ch=NULL, **chp=NULL;
                    135:        for (chp = &channels[hash]; (ch = *chp) != NULL ; chp = &ch->next) {
                    136:            if (ch->sockfd == sockfd) {
                    137: 
                    138:                /* Walk through and ABORT all remaining sessions */
                    139: 
                    140:                if (PROT_TRACE) HTTrace("Channel..... Deleted %p with id %d\n",
                    141:                                        ch, ch->sockfd);
                    142:                *chp = ch->next;
                    143:                HT_FREE(ch);
                    144:                return YES;
                    145:            }
                    146:        }
                    147:     }
                    148:     return NO;
                    149: }
                    150: 
                    151: PUBLIC BOOL HTChannel_deleteAll (void)
                    152: {
                    153: 
                    154:     /* @@@ */
2.1       frystyk   155: 
2.27      frystyk   156:     return NO;
                    157: }
2.1       frystyk   158: 
2.27      frystyk   159: /*
                    160: **     Set and Get the mode of a channel. A channel may change mode in the 
                    161: **     middle of a connection.
2.1       frystyk   162: */
2.27      frystyk   163: PUBLIC HTChannelMode HTChannel_mode (SOCKET sockfd, BOOL *active)
                    164: {
                    165:     if (sockfd != INVSOC) {
                    166:        int hash = HASH(sockfd);
                    167:        HTChannel *ch=NULL, **chp=NULL;
                    168:        for (chp = &channels[hash]; (ch = *chp) != NULL ; chp = &ch->next) {
                    169:            if (ch->sockfd == sockfd) {
                    170:                if (active) *active = ch->active;
                    171:                return ch->mode;
                    172:            }
                    173:        }
                    174:     }
                    175:     return HT_CH_UNKNOWN;
                    176: }
                    177: 
                    178: PUBLIC BOOL HTChannel_setMode (SOCKET sockfd, HTChannelMode mode)
2.1       frystyk   179: {
2.27      frystyk   180:     if (sockfd != INVSOC) {
                    181:        int hash = HASH(sockfd);
                    182:        HTChannel *ch=NULL, **chp=NULL;
                    183:        for (chp = &channels[hash]; (ch = *chp) != NULL ; chp = &ch->next) {
                    184:            if (ch->sockfd == sockfd) {
                    185:                ch->mode = mode;
                    186:                return YES;
                    187:            }
                    188:        }
                    189:     }
                    190:     return NO;
2.1       frystyk   191: }
                    192: 
2.27      frystyk   193: PUBLIC BOOL HTChannel_addMode (SOCKET sockfd, HTChannelMode mode)
2.12      frystyk   194: {
2.27      frystyk   195:     if (sockfd != INVSOC) {
                    196:        int hash = HASH(sockfd);
                    197:        HTChannel *ch=NULL, **chp=NULL;
                    198:        for (chp = &channels[hash]; (ch = *chp) != NULL ; chp = &ch->next) {
                    199:            if (ch->sockfd == sockfd) {
                    200:                ch->mode |= mode;
                    201:                return YES;
                    202:            }
                    203:        }
                    204:     }
                    205:     return NO;
2.12      frystyk   206: }
                    207: 
2.27      frystyk   208: /* ------------------------------------------------------------------------- */
                    209: /*                             READ ROUTINES                                */
                    210: /* ------------------------------------------------------------------------- */
                    211: 
2.1       frystyk   212: /*     Push data from a socket down a stream
                    213: **     -------------------------------------
                    214: **
                    215: **   This routine is responsible for creating and PRESENTING any
                    216: **   graphic (or other) objects described by the file. As this function
2.27      frystyk   217: **   max reads a chunk of data on size CHANNEL_BUFFER_SIZE, it can be used
2.1       frystyk   218: **   with both blocking or non-blocking sockets. It will always return to
                    219: **   the event loop, however if we are using blocking I/O then we get a full
                    220: **   buffer read, otherwise we get what's available.
                    221: **
                    222: ** Returns      HT_LOADED      if finished reading
2.3       frystyk   223: **             HT_OK           if OK, but more to read
2.1       frystyk   224: **             HT_ERROR        if error,
2.21      frystyk   225: **                     HT_WOULD_BLOCK  if read or write would block
                    226: **             HT_PAUSE        if stream is paused
2.1       frystyk   227: */
2.27      frystyk   228: PUBLIC int HTChannel_readSocket (HTRequest * request, HTNet * net)
2.1       frystyk   229: {
2.27      frystyk   230:     HTChannel * ch = HTChannel_find(net->sockfd);
                    231:     int b_read = ch->read - ch->data;
2.1       frystyk   232:     int status;
                    233: 
2.3       frystyk   234:     /* Read from socket if we got rid of all the data previously read */
2.4       frystyk   235:     do {
2.27      frystyk   236:        if (ch->write >= ch->read) {
                    237:            if ((b_read = NETREAD(net->sockfd, ch->data,
                    238:                                  CHANNEL_BUFFER_SIZE)) < 0) {
2.1       frystyk   239: #ifdef EAGAIN
2.4       frystyk   240:                if (socerrno==EAGAIN || socerrno==EWOULDBLOCK)      /* POSIX */
2.1       frystyk   241: #else
2.11      frystyk   242:                if (socerrno==EWOULDBLOCK)                            /* BSD */
                    243: #endif 
                    244:                {
                    245:                    if (PROT_TRACE)
2.27      frystyk   246:                        HTTrace("Read Socket. WOULD BLOCK soc %d\n",net->sockfd);
                    247:                    HTEvent_Register(net->sockfd, request, (SockOps) FD_READ,
2.11      frystyk   248:                                     net->cbf, net->priority);
                    249:                    return HT_WOULD_BLOCK;
2.21      frystyk   250: #ifdef __svr4__
                    251:     /* 
                    252:     ** In Solaris envirnoment, SIGPOLL is used to signal end of buffer for
                    253:     ** /dev/audio.  If your process is also doing a socket read, it will cause
                    254:     ** an EINTR error.  This error will cause the www library request to 
                    255:     ** terminate prematurly.
                    256:     */
                    257:                 } else if (socerrno == EINTR) {
                    258:                     continue;
                    259: #endif
2.11      frystyk   260:                } else { /* We have a real error */
2.20      frystyk   261:                    HTRequest_addSystemError(request,  ERR_FATAL, socerrno, NO,
                    262:                                             "NETREAD");
2.11      frystyk   263:                    return HT_ERROR;
                    264:                }
2.4       frystyk   265:            } else if (!b_read) {
2.16      frystyk   266:                HTAlertCallback *cbf = HTAlert_find(HT_PROG_DONE);
2.3       frystyk   267:                if (PROT_TRACE)
2.24      eric      268:                    HTTrace("Read Socket. Finished loading socket %d\n",
2.27      frystyk   269:                             net->sockfd);
2.16      frystyk   270:                if(cbf)(*cbf)(request,HT_PROG_DONE,HT_MSG_NULL,NULL,NULL,NULL);
2.27      frystyk   271:                HTEvent_UnRegister(net->sockfd, FD_READ);
2.21      frystyk   272:                return HT_CLOSED;
2.1       frystyk   273:            }
                    274: 
2.4       frystyk   275:            /* Remember how much we have read from the input socket */
2.27      frystyk   276:            ch->write = ch->data;
                    277:            ch->read = ch->data + b_read;
2.1       frystyk   278: 
                    279: #ifdef NOT_ASCII
2.4       frystyk   280:            {
2.27      frystyk   281:                char *p = ch->data;
                    282:                while (p < ch->read) {
2.4       frystyk   283:                    *p = FROMASCII(*p);
                    284:                    p++;
                    285:                }
2.1       frystyk   286:            }
                    287: #endif
2.27      frystyk   288: 
2.3       frystyk   289:            if (PROT_TRACE)
2.24      eric      290:                HTTrace("Read Socket. %d bytes read from socket %d\n",
2.27      frystyk   291:                        b_read, net->sockfd);
2.16      frystyk   292: 
2.27      frystyk   293:            if (!(ch->mode & HT_CH_INTERLEAVED)) {
                    294:                HTAlertCallback * cbf = HTAlert_find(HT_PROG_READ);
                    295:                net->bytes_read += b_read;
2.16      frystyk   296:                if (cbf)
                    297:                    (*cbf)(request, HT_PROG_READ, HT_MSG_NULL,NULL,NULL,NULL);
                    298:            }
2.4       frystyk   299:        }
2.27      frystyk   300: 
2.4       frystyk   301:        /* Now push the data down the stream */
2.27      frystyk   302:        if ((status = (*net->target->isa->put_block)(net->target, ch->data,
                    303:                                                     b_read)) != HT_OK) {
2.4       frystyk   304:            if (status==HT_WOULD_BLOCK) {
                    305:                if (PROT_TRACE)
2.24      eric      306:                    HTTrace("Read Socket. Target WOULD BLOCK\n");
2.27      frystyk   307:                HTEvent_UnRegister(net->sockfd, FD_READ);
2.4       frystyk   308:                return HT_WOULD_BLOCK;
2.21      frystyk   309:            } else if (status == HT_PAUSE) {
2.24      eric      310:                if (PROT_TRACE) HTTrace("Read Socket. Target PAUSED\n");
2.27      frystyk   311:                HTEvent_UnRegister(net->sockfd, FD_READ);
2.21      frystyk   312:                return HT_PAUSE;
2.5       frystyk   313:            } else if (status>0) {            /* Stream specific return code */
                    314:                if (PROT_TRACE)
2.24      eric      315:                    HTTrace("Read Socket. Target returns %d\n",status);
2.27      frystyk   316:                ch->write = ch->data + b_read;
2.5       frystyk   317:                return status;
                    318:            } else {                                 /* We have a real error */
2.4       frystyk   319:                if (PROT_TRACE)
2.24      eric      320:                    HTTrace("Read Socket. Target ERROR\n");
2.4       frystyk   321:                return status;
                    322:            }
2.1       frystyk   323:        }
2.27      frystyk   324:        ch->write = ch->data + b_read;
2.20      frystyk   325:     } while (net->preemptive);
2.27      frystyk   326:     HTEvent_Register(net->sockfd, request, (SockOps) FD_READ,
2.10      frystyk   327:                     net->cbf, net->priority);
2.1       frystyk   328:     return HT_WOULD_BLOCK;
                    329: }
2.2       frystyk   330: 
2.26      eric      331: /*     HTSocket_DLLHackFopen and close
                    332: **     -------------------------------
2.19      frystyk   333: **     Work around the problem that an app can't open a file and have a dll
                    334: **     read from it!
                    335: */
                    336: #ifdef WWW_WIN_DLL
                    337: PUBLIC FILE * HTSocket_DLLHackFopen (const char * filename, const char * mode)
                    338: {
                    339:     return (fopen(filename, mode));
2.26      eric      340: }
                    341: 
                    342: PUBLIC int HTSocket_DLLHackFclose (FILE * file)
                    343: {
                    344:     return (fclose(file));
2.19      frystyk   345: }
                    346: #endif /* WWW_WIN_DLL */
                    347: 
2.2       frystyk   348: /*     Push data from an ANSI file descriptor down a stream
                    349: **     ----------------------------------------------------
                    350: **
                    351: **   This routine is responsible for creating and PRESENTING any
                    352: **   graphic (or other) objects described by the file.
                    353: **
                    354: **   Bugs: When we can wait on a file then this should also check interrupts!
                    355: **
                    356: **   Returns    HT_LOADED      if finished reading
                    357: **             HT_ERROR        if error,
                    358: */
2.28    ! frystyk   359: PUBLIC int HTChannel_readFile (HTRequest * request, HTNet * net,
        !           360:                               HTFileBuffer * isoc, FILE * fp)
2.2       frystyk   361: {
                    362:     int b_read;
                    363:     int status;
                    364:     if (!fp) {
2.24      eric      365:        if (PROT_TRACE) HTTrace("Read File... Bad argument\n");
2.2       frystyk   366:        return HT_ERROR;
                    367:     }
                    368: 
                    369:     while(1) {
2.28    ! frystyk   370:        if ((b_read = fread(isoc->data, 1, CHANNEL_BUFFER_SIZE, fp))==0){
2.2       frystyk   371:            if (ferror(fp)) {
2.28    ! frystyk   372:                if (PROT_TRACE) HTTrace("Read File... READ ERROR\n");
2.2       frystyk   373:            } else
                    374:                return HT_LOADED;
                    375:        }
2.28    ! frystyk   376:        isoc->write = isoc->data;
        !           377:        isoc->read = isoc->data + b_read;
2.2       frystyk   378:        if (PROT_TRACE)
2.28    ! frystyk   379:            HTTrace("Read File... %d bytes read from file %p\n", b_read, fp);
2.2       frystyk   380: 
                    381:        /* Now push the data down the stream (we use blocking I/O) */
2.28    ! frystyk   382:        if ((status = (*net->target->isa->put_block)(net->target, isoc->data,
2.27      frystyk   383:                                                     b_read)) != HT_OK) {
2.28    ! frystyk   384:            if (PROT_TRACE) HTTrace("Read File... Target ERROR\n");
2.2       frystyk   385:            return status;
                    386:        }
2.28    ! frystyk   387:        isoc->write = isoc->data + b_read;
2.2       frystyk   388:     }
                    389: }
                    390: 
2.17      frystyk   391: /*     HTLoadSocket
                    392: **     ------------
                    393: **     Given an open socket, this routine loads what ever is on the socket
                    394: **
                    395: ** On entry,
                    396: **      request                This is the request structure
                    397: ** On Exit
                    398: **     returns         HT_ERROR        Error has occured in call back
                    399: **                     HT_OK           Call back was OK
                    400: */
                    401: PUBLIC int HTLoadSocket (SOCKET soc, HTRequest * request, SockOps ops)
                    402: {
2.18      frystyk   403:     HTNet * net = NULL;
                    404:     if (!request) return HT_ERROR;
2.17      frystyk   405:     if (ops == FD_NONE) {
2.18      frystyk   406:        HTNet * me;
2.17      frystyk   407:        if (soc==INVSOC) {
2.24      eric      408:            if (PROT_TRACE) HTTrace("Load Socket. invalid socket\n");
2.17      frystyk   409:            return HT_ERROR;
                    410:        }
2.24      eric      411:        if (PROT_TRACE) HTTrace("Load Socket. Loading socket %d\n",soc);
2.18      frystyk   412:        me = HTNet_new(request, soc);
                    413:        me->sockfd = soc;
                    414:        me->target = request->output_stream;
2.27      frystyk   415:        HTChannel_new(net->sockfd, HT_CH_PLAIN, NO);
2.18      frystyk   416:        net = me;
2.17      frystyk   417:     } else if (ops == FD_CLOSE) {                            /* Interrupted */
                    418:        HTNet_delete(request->net, HT_INTERRUPTED);
                    419:        return HT_OK;
2.18      frystyk   420:     } else
                    421:        net = request->net;
                    422:     if (!net) {
2.24      eric      423:        if (PROT_TRACE) HTTrace("Load Socket. invalid argument\n");
2.18      frystyk   424:        return HT_ERROR;
2.17      frystyk   425:     }
                    426: 
                    427:     /* In this load function we only have one state: READ */
                    428:     {
2.27      frystyk   429:        int status = HTChannel_readSocket(request, net);
2.17      frystyk   430:        if (status == HT_WOULD_BLOCK)
                    431:            return HT_OK;
2.22      frystyk   432:        else if (status == HT_CLOSED)
2.17      frystyk   433:            HTNet_delete(request->net, HT_LOADED);
                    434:        else
                    435:            HTNet_delete(request->net, HT_ERROR);
                    436:     }
                    437:     return HT_OK;
                    438: }
2.27      frystyk   439: 

Webmaster