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