Annotation of libwww/Library/src/HTReader.c, revision 2.1
2.1 ! frystyk 1: /* HTReader.c
! 2: ** READ STREAM FROM THE NETWORK USING TCP
! 3: **
! 4: ** (c) COPYRIGHT MIT 1995.
! 5: ** Please first read the full copyright statement in the file COPYRIGH.
! 6: ** @(#) $Id: Date Author State $
! 7: **
! 8: ** HISTORY:
! 9: ** 6 June 95 HFN Written
! 10: */
! 11:
! 12: /* Library Include files */
! 13: #include "sysdep.h"
! 14: #include "WWWUtil.h"
! 15: #include "HTReqMan.h"
! 16: #include "HTProt.h"
! 17: #include "HTIOStream.h"
! 18: #include "HTChannl.h"
! 19: #include "HTAlert.h"
! 20: #include "HTFormat.h"
! 21: #include "HTNetMan.h"
! 22: #include "HTError.h"
! 23: #include "HTReader.h" /* Implemented here */
! 24:
! 25: struct _HTStream {
! 26: const HTStreamClass * isa;
! 27: /* ... */
! 28: };
! 29:
! 30: struct _HTInputStream {
! 31: const HTInputStreamClass * isa;
! 32: HTChannel * ch;
! 33: HTNet * net;
! 34: HTStream * target; /* Target for incoming data */
! 35: char * write; /* Last byte written */
! 36: char * read; /* Last byte read */
! 37: char data [INPUT_BUFFER_SIZE]; /* buffer */
! 38: };
! 39:
! 40: /* ------------------------------------------------------------------------- */
! 41:
! 42: PRIVATE int HTReader_flush (HTInputStream * me)
! 43: {
! 44: return me->target ? (*me->target->isa->flush)(me->target) : HT_OK;
! 45: }
! 46:
! 47: PRIVATE int HTReader_free (HTInputStream * me)
! 48: {
! 49: if (me->target) {
! 50: int status = (*me->target->isa->_free)(me->target);
! 51: if (status != HT_WOULD_BLOCK) me->target = NULL;
! 52: return status;
! 53: }
! 54: return HT_OK;
! 55: }
! 56:
! 57: PRIVATE int HTReader_abort (HTInputStream * me, HTList * e)
! 58: {
! 59: if (me->target) {
! 60: (*me->target->isa->abort)(me->target, NULL);
! 61: me->target = NULL;
! 62: }
! 63: return HT_ERROR;
! 64: }
! 65:
! 66: /* Push data from a socket down a stream
! 67: ** -------------------------------------
! 68: **
! 69: ** This routine is responsible for creating and PRESENTING any
! 70: ** graphic (or other) objects described by the file. As this function
! 71: ** max reads a chunk of data on size INPUT_BUFFER_SIZE, it can be used
! 72: ** with both blocking or non-blocking sockets. It will always return to
! 73: ** the event loop, however if we are using blocking I/O then we get a full
! 74: ** buffer read, otherwise we get what's available.
! 75: **
! 76: ** Returns HT_LOADED if finished reading
! 77: ** HT_OK if OK, but more to read
! 78: ** HT_ERROR if error,
! 79: ** HT_WOULD_BLOCK if read or write would block
! 80: ** HT_PAUSE if stream is paused
! 81: */
! 82: PRIVATE int HTReader_read (HTInputStream * me)
! 83: {
! 84: HTNet * net = me->net;
! 85: SOCKET soc = net->sockfd;
! 86: int b_read = me->read - me->data;
! 87: int status;
! 88:
! 89: /* Read from socket if we got rid of all the data previously read */
! 90: do {
! 91: if (me->write >= me->read) {
! 92: if ((b_read = NETREAD(soc, me->data, INPUT_BUFFER_SIZE)) < 0) {
! 93: #ifdef EAGAIN
! 94: if (socerrno==EAGAIN || socerrno==EWOULDBLOCK) /* POSIX */
! 95: #else
! 96: if (socerrno==EWOULDBLOCK) /* BSD */
! 97: #endif
! 98: {
! 99: if (PROT_TRACE)
! 100: HTTrace("Read Socket. WOULD BLOCK fd %d\n",soc);
! 101: HTEvent_Register(soc, net->request, (SockOps) FD_READ,
! 102: net->cbf, net->priority);
! 103: return HT_WOULD_BLOCK;
! 104: #ifdef __svr4__
! 105: /*
! 106: ** In Solaris envirnoment, SIGPOLL is used to signal end of buffer for
! 107: ** /dev/audio. If your process is also doing a socket read, it will cause
! 108: ** an EINTR error. This error will cause the www library request to
! 109: ** terminate prematurly.
! 110: */
! 111: } else if (socerrno == EINTR) {
! 112: continue;
! 113: #endif /* __svr4__ */
! 114: } else { /* We have a real error */
! 115:
! 116: /* HERE WE SHOULD RETURN target abort */
! 117:
! 118: HTRequest_addSystemError(net->request, ERR_FATAL, socerrno,
! 119: NO, "NETREAD");
! 120: return HT_ERROR;
! 121: }
! 122: } else if (!b_read) {
! 123: HTAlertCallback *cbf = HTAlert_find(HT_PROG_DONE);
! 124: if (PROT_TRACE)
! 125: HTTrace("Read Socket. Finished loading socket %d\n",
! 126: soc);
! 127: if (cbf) (*cbf)(net->request, HT_PROG_DONE,
! 128: HT_MSG_NULL, NULL, NULL, NULL);
! 129: HTEvent_UnRegister(soc, FD_READ);
! 130: return HT_CLOSED;
! 131: }
! 132:
! 133: /* Remember how much we have read from the input socket */
! 134: me->write = me->data;
! 135: me->read = me->data + b_read;
! 136:
! 137: #ifdef NOT_ASCII
! 138: {
! 139: char *p = me->data;
! 140: while (p < me->read) {
! 141: *p = FROMASCII(*p);
! 142: p++;
! 143: }
! 144: }
! 145: #endif /* NOT_ASCII */
! 146:
! 147: if (PROT_TRACE)
! 148: HTTrace("Read Socket. %d bytes read from socket %d\n",
! 149: b_read, soc);
! 150: {
! 151: HTAlertCallback * cbf = HTAlert_find(HT_PROG_READ);
! 152: net->bytes_read += b_read;
! 153: if (cbf) (*cbf)(net->request, HT_PROG_READ,
! 154: HT_MSG_NULL, NULL, NULL, NULL);
! 155: }
! 156: }
! 157:
! 158: /* Now push the data down the stream */
! 159: if ((status = (*me->target->isa->put_block)
! 160: (me->target, me->data, b_read)) != HT_OK) {
! 161: if (status == HT_WOULD_BLOCK) {
! 162: if (PROT_TRACE) HTTrace("Read Socket. Target WOULD BLOCK\n");
! 163: HTEvent_UnRegister(soc, FD_READ);
! 164: return HT_WOULD_BLOCK;
! 165: } else if (status == HT_PAUSE) {
! 166: if (PROT_TRACE) HTTrace("Read Socket. Target PAUSED\n");
! 167: HTEvent_UnRegister(soc, FD_READ);
! 168: return HT_PAUSE;
! 169: } else if (status>0) { /* Stream specific return code */
! 170: if (PROT_TRACE)
! 171: HTTrace("Read Socket. Target returns %d\n", status);
! 172: me->write = me->data + b_read;
! 173: return status;
! 174: } else { /* We have a real error */
! 175: if (PROT_TRACE) HTTrace("Read Socket. Target ERROR\n");
! 176: return status;
! 177: }
! 178: }
! 179: me->write = me->data + b_read;
! 180: } while (net->preemptive);
! 181: HTEvent_Register(soc, net->request, (SockOps) FD_READ,
! 182: net->cbf, net->priority);
! 183: return HT_WOULD_BLOCK;
! 184: }
! 185:
! 186: /*
! 187: ** The difference between the close and the free method is that we don't
! 188: ** close the connection in the free method - we only call the free method
! 189: ** of the target stream. That way, we can keep the input stream as long
! 190: ** as the channel itself.
! 191: */
! 192: PRIVATE int HTReader_close (HTInputStream * me)
! 193: {
! 194: int status = HT_OK;
! 195: if (me->target) {
! 196: if ((status = (*me->target->isa->_free)(me->target))==HT_WOULD_BLOCK)
! 197: return HT_WOULD_BLOCK;
! 198: }
! 199: if (PROT_TRACE) HTTrace("Socket read. FREEING....\n");
! 200: HT_FREE(me);
! 201: return status;
! 202: }
! 203:
! 204: PRIVATE const HTInputStreamClass HTReader =
! 205: {
! 206: "SocketReader",
! 207: HTReader_flush,
! 208: HTReader_free,
! 209: HTReader_abort,
! 210: HTReader_read,
! 211: HTReader_close
! 212: };
! 213:
! 214: /*
! 215: ** Create a new input read stream. Before we actually create it we check
! 216: ** to see whether we already have an input stream for this channel and if
! 217: ** so we just return that. This means that we can reuse input streams
! 218: ** in persistent connections, for example.
! 219: */
! 220: PUBLIC HTInputStream * HTReader_new (HTNet * net, HTChannel * ch,
! 221: HTStream * target, void * param, int mode)
! 222: {
! 223: if (net && ch) {
! 224: HTInputStream * me = HTChannel_input(ch);
! 225: if (me == NULL) {
! 226: if ((me=(HTInputStream *) HT_CALLOC(1, sizeof(HTInputStream))) == NULL)
! 227: HT_OUTOFMEM("HTReader_new");
! 228: me->isa = &HTReader;
! 229: me->ch = ch;
! 230: }
! 231: me->target = target;
! 232: me->net = net;
! 233: return me;
! 234: }
! 235: return NULL;
! 236: }
Webmaster