/* HTWrite.c ** FILE WRITER BASED ON A SOCKET ** ** (c) COPYRIGHT MIT 1995. ** Please first read the full copyright statement in the file COPYRIGH. ** @(#) $Id: HTWriter.c,v 2.50 1998/05/19 16:49:46 frystyk Exp $ ** ** This is a try with a non-buffered output stream which remembers ** state using the write_pointer. As normally we have a big buffer ** somewhere else in the stream chain an extra output buffer will often ** not be needed. */ /* Library include files */ #include "wwwsys.h" #include "WWWUtil.h" #include "WWWCore.h" #include "HTNet.h" #include "HTNetMan.h" #include "HTWriter.h" /* Implemented here */ #include "HTHstMan.h" struct _HTStream { const HTStreamClass * isa; /* ... */ }; struct _HTOutputStream { const HTOutputStreamClass * isa; HTChannel * ch; HTHost * host; int offset; #ifdef NOT_ASCII char * ascbuf; /* Buffer for TOASCII conversion */ #endif }; /* ------------------------------------------------------------------------- */ PRIVATE int HTWriter_flush (HTOutputStream * me) { return HT_OK; /* As we don't have any output buffer */ } PRIVATE int HTWriter_free (HTOutputStream * me) { return HT_OK; } PRIVATE int HTWriter_abort (HTOutputStream * me, HTList * e) { return HT_ERROR; } /* Write to the socket ** ** According to Solaris 2.3 man on write: ** ** o If O_NONBLOCK and O_NDELAY are clear, write() blocks ** until the data can be accepted. ** ** o If O_NONBLOCK or O_NDELAY is set, write() does not ** block the process. If some data can be written ** without blocking the process, write() writes what it ** can and returns the number of bytes written. Other- ** wise, if O_NONBLOCK is set, it returns - 1 and sets ** errno to EAGAIN or if O_NDELAY is set, it returns 0. ** ** According to SunOS 4.1.1 man on write: ** ** + If the descriptor is marked for non-blocking I/O ** using fcntl() to set the FNONBLOCK or O_NONBLOCK ** flag (defined in ), write() requests ** for {PIPE_BUF} (see pathconf(2V)) or fewer bytes ** either succeed completely and return nbyte, or ** return -1 and set errno to EAGAIN. A write() request ** for greater than {PIPE_BUF} bytes either transfers ** what it can and returns the number of bytes written, ** or transfers no data and returns -1 and sets errno ** to EAGAIN. If a write() request is greater than ** {PIPE_BUF} bytes and all data previously written to ** the pipe has been read, write() transfers at least ** {PIPE_BUF} bytes. */ PRIVATE int HTWriter_write (HTOutputStream * me, const char * buf, int len) { HTHost * host = me->host; SOCKET soc = HTChannel_socket(HTHost_channel(host)); HTNet * net = HTHost_getWriteNet(host); int b_write; char * wrtp; const char *limit = buf+len; /* If we don't have a Net object then return right away */ if (!net) { if (STREAM_TRACE) HTTrace("Write Socket WOULD BLOCK %d (offset %d)\n",soc, me->offset); return HT_ERROR; } #ifdef NOT_ASCII if (len && !me->ascbuf) { /* Generate new buffer */ const char *orig = buf; char *dest; int cnt; if ((me->ascbuf = (char *) HT_MALLOC(len)) == NULL) HT_OUTOFMEM("HTWriter_write"); dest = me->ascbuf; for (cnt=0; cntascbuf; limit = me->ascbuf+len; } #else if (!me->offset) wrtp = (char *) buf; else { wrtp = (char *) buf + me->offset; len -= me->offset; me->offset = 0; } #endif /* Write data to the network */ while (wrtp < limit) { if ((b_write = NETWRITE(soc, wrtp, len)) < 0) { #ifdef EAGAIN if (socerrno == EAGAIN || socerrno == EWOULDBLOCK)/* POSIX, SVR4 */ #else if (socerrno == EWOULDBLOCK) /* BSD */ #endif { HTHost_register(host, net, HTEvent_WRITE); me->offset = wrtp - buf; if (STREAM_TRACE) HTTrace("Write Socket WOULD BLOCK %d (offset %d)\n",soc, me->offset); return HT_WOULD_BLOCK; #ifdef EINTR } else if (socerrno == EINTR) { /* ** EINTR A signal was caught during the write opera- ** tion and no data was transferred. */ if (STREAM_TRACE) HTTrace("Write Socket call interruted - try again\n"); continue; #endif } else { #ifdef EPIPE if (socerrno == EPIPE) if (STREAM_TRACE) HTTrace("Write Socket got EPIPE\n"); #endif /* EPIPE */ host->broken_pipe = YES; HTRequest_addSystemError(net->request, ERR_FATAL, socerrno, NO, "NETWRITE"); return HT_ERROR; } } /* We do this unconditionally, should we check to see if we ever blocked? */ HTTraceData(wrtp, b_write, "Writing to socket %d", soc); wrtp += b_write; len -= b_write; if (STREAM_TRACE) HTTrace("Write Socket %d bytes written to %d\n", b_write, soc); { HTAlertCallback *cbf = HTAlert_find(HT_PROG_READ); if (cbf) (*cbf)(net->request, HT_PROG_WRITE, HT_MSG_NULL, NULL, NULL, NULL); } } #ifdef NOT_ASCII HT_FREE(me->ascbuf); #endif return HT_OK; } /* Character handling ** ------------------ */ PRIVATE int HTWriter_put_character (HTOutputStream * me, char c) { return HTWriter_write(me, &c, 1); } /* String handling ** --------------- ** ** Strings must be smaller than this buffer size. */ PRIVATE int HTWriter_put_string (HTOutputStream * me, const char * s) { return HTWriter_write(me, s, (int) strlen(s)); } /* ** The difference between the close and the free method is that we don't ** close the connection in the free method - we only call the free method ** of the target stream. That way, we can keep the output stream as long ** as the channel itself. */ PRIVATE int HTWriter_close (HTOutputStream * me) { if (STREAM_TRACE) HTTrace("Socket write FREEING....\n"); HT_FREE(me); return HT_OK; } PRIVATE const HTOutputStreamClass HTWriter = { "SocketWriter", HTWriter_flush, HTWriter_free, HTWriter_abort, HTWriter_put_character, HTWriter_put_string, HTWriter_write, HTWriter_close }; PUBLIC HTOutputStream * HTWriter_new (HTHost * host, HTChannel * ch, void * param, int mode) { if (host && ch) { HTOutputStream * me = HTChannel_output(ch); if (!me) { if ((me=(HTOutputStream *) HT_CALLOC(1, sizeof(HTOutputStream)))==NULL) HT_OUTOFMEM("HTWriter_new"); me->isa = &HTWriter; me->ch = ch; me->host = host; } return me; } return NULL; }