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