Annotation of libwww/Library/src/HTBufWrt.c, revision 2.26

2.1       frystyk     1: /*
                      2: **     BUFFERED TRANSPORT WRITER STREAM
                      3: **
                      4: **     (c) COPYRIGHT MIT 1995.
                      5: **     Please first read the full copyright statement in the file COPYRIGH.
2.26    ! frystyk     6: **     @(#) $Id: HTBufWrt.c,v 2.25 1999/02/22 22:10:11 frystyk Exp $
2.1       frystyk     7: **
                      8: **     A buffered output stream. This stream lets you write characters to a
                      9: **     stream without causing a write every time.  The data is first written
                     10: **     into a buffer. Data is written to the actual stream only when the
                     11: **     buffer is full, or when the stream is flushed.
                     12: */
                     13: 
                     14: /* Library include files */
2.20      frystyk    15: #include "wwwsys.h"
2.1       frystyk    16: #include "WWWUtil.h"
                     17: #include "WWWCore.h"
                     18: #include "HTNetMan.h"
                     19: #include "HTWriter.h"
2.6       frystyk    20: #include "HTTimer.h"
2.1       frystyk    21: #include "HTBufWrt.h"                                   /* Implemented here */
                     22: 
                     23: struct _HTOutputStream {
                     24:     const HTOutputStreamClass *        isa;
                     25:     HTOutputStream *           target;          /* Target for outgoing data */
2.6       frystyk    26:     HTHost *                   host;
2.15      frystyk    27: 
                     28:     int                                allocated;          /* Allocated Buffer size */
                     29:     int                         growby;
2.16      frystyk    30:     int                                expo;
2.15      frystyk    31: 
2.1       frystyk    32:     char *                     read;                  /* Position in 'data' */
                     33:     char *                     data;                              /* buffer */
2.6       frystyk    34: 
2.7       frystyk    35:     ms_t                       lastFlushTime;  /* polar coordinates of the moon */
2.6       frystyk    36:     HTTimer *                  timer;
2.1       frystyk    37: };
                     38: 
                     39: #define PUTBLOCK(b,l) (*me->target->isa->put_block)(me->target,(b),(l))
                     40: 
                     41: /* ------------------------------------------------------------------------- */
                     42: 
2.11      frystyk    43: /*
                     44: **  This function is only called from either FlushEvent or HTBufferWriter_lazyFlush
                     45: **  which means that only the host object or timeout can cause a flush
                     46: */
2.1       frystyk    47: PRIVATE int HTBufferWriter_flush (HTOutputStream * me)
                     48: {
2.17      frystyk    49:     int status = HT_OK;
2.11      frystyk    50:     if (me && me->read > me->data) {
2.16      frystyk    51:        me->lastFlushTime = HTGetTimeInMillis();
                     52:         if ((status = PUTBLOCK(me->data, me->read - me->data))==HT_WOULD_BLOCK)
2.1       frystyk    53:            return HT_WOULD_BLOCK;
                     54:        me->read = me->data;
                     55:     }
                     56:     return status;
                     57: }
                     58: 
2.9       eric       59: PRIVATE int FlushEvent (HTTimer * timer, void * param, HTEventType type)
2.6       frystyk    60: {
                     61:     HTOutputStream * me = (HTOutputStream *) param;
2.19      frystyk    62:     if (timer != me->timer)
2.25      frystyk    63:        HTDEBUGBREAK("Buffer Writer timer %p not in sync\n" _ timer);
                     64:     HTTRACE(PROT_TRACE, "Buffer...... Timeout flushing %p with timer %p\n" _ me _ timer);
2.11      frystyk    65: 
                     66:     /*
                     67:     **  We ignore the return code here which we shouldn't!!!
                     68:     */
2.6       frystyk    69:     HTBufferWriter_flush(me);
2.11      frystyk    70: 
                     71:     /*
                     72:     **  Delete the timer
                     73:     */
2.24      frystyk    74:     HTTimer_delete(me->timer);
2.11      frystyk    75:     me->timer = NULL;
2.6       frystyk    76:     return HT_OK;
                     77: }
                     78: 
                     79: PRIVATE int HTBufferWriter_lazyFlush (HTOutputStream * me)
                     80: {
                     81:     HTNet * net;
                     82:     int delay;
                     83: 
2.18      frystyk    84:     if (me->read <= me->data) {
2.6       frystyk    85:        return HT_OK;                   /* nothing to flush */
2.18      frystyk    86:     }
2.6       frystyk    87:     /*
                     88:     **  If we are allowed to delay the flush then set a timer with the
                     89:     **  delay descibed by our delay variable. If we can't delay then flush 
                     90:     **  right away.
                     91:     */
2.12      frystyk    92:     delay = HTHost_findWriteDelay(me->host, me->lastFlushTime, me->read - me->data);
2.6       frystyk    93: 
                     94:     /*
                     95:     ** Flush immediately
                     96:     */
2.11      frystyk    97:     if (!delay) {
                     98:        int status;
2.25      frystyk    99:        HTTRACE(STREAM_TRACE, "Buffer...... Flushing %p\n" _ me);
2.11      frystyk   100:        if ((status = HTBufferWriter_flush(me)) && me->timer) {
                    101:            HTTimer_delete(me->timer);
                    102:            me->timer = NULL;
                    103:        }
                    104:        return status;
                    105:     }
2.6       frystyk   106: 
                    107:     /*
2.11      frystyk   108:     ** Set a timer and tell the host we've done the write if
2.21      frystyk   109:     **  we have not already started a timer earlier. If a timer
                    110:     **  does already exist then make sure that it hasn't expired.
                    111:     **  This can be the case if we have a really slow client that
                    112:     **  can't parse the data fast enough.
2.6       frystyk   113:     */
2.11      frystyk   114:     if (!me->timer) {
                    115:        net = HTHost_getWriteNet(me->host);
2.21      frystyk   116:        me->timer = HTTimer_new(NULL, FlushEvent, me, delay, YES, NO);
2.11      frystyk   117:        HTHost_unregister(me->host, net, HTEvent_WRITE);
2.25      frystyk   118:        HTTRACE(STREAM_TRACE, "Buffer...... Waiting %dms on %p\n" _ delay _ me);
2.21      frystyk   119:     } else {
                    120:        if (HTTimer_hasTimerExpired(me->timer)) {
2.25      frystyk   121:            HTTRACE(STREAM_TRACE, "Buffer...... Dispatching old timer %p\n" _ me->timer);
2.21      frystyk   122:            HTTimer_dispatch(me->timer);
                    123:            me->timer = NULL;
                    124:        } else {
2.25      frystyk   125:            HTTRACE(STREAM_TRACE, "Buffer...... Waiting on unexpired timer %p\n" _ me->timer);
2.21      frystyk   126:        }
2.11      frystyk   127:     }
2.6       frystyk   128:     return HT_OK;
                    129: }
                    130: 
2.1       frystyk   131: PRIVATE int HTBufferWriter_free (HTOutputStream * me)
                    132: {
2.11      frystyk   133:     return HTBufferWriter_lazyFlush(me);
2.1       frystyk   134: }
                    135: 
2.15      frystyk   136: PRIVATE BOOL HTBufferWriter_addBuffer(HTOutputStream * me, int addthis)
                    137: {
                    138:     if (me) {
2.16      frystyk   139:         me->allocated += (addthis - addthis%me->growby + (me->growby*me->expo));
2.17      frystyk   140:        me->expo *= 2;
2.25      frystyk   141:        HTTRACE(STREAM_TRACE, "Buffer...... Increasing buffer to %d bytes\n" _ me->allocated);
2.15      frystyk   142:         if (me->data) {
                    143:             int size = me->read-me->data;
                    144:             if ((me->data = (char *) HT_REALLOC(me->data, me->allocated)) == NULL)
                    145:                 HT_OUTOFMEM("HTBufferWriter_addBuffer");
                    146:             me->read = me->data + size;
                    147:         } else {
                    148:             if ((me->data = (char *) HT_CALLOC(1, me->allocated)) == NULL)
                    149:                 HT_OUTOFMEM("HTBufferWriter_addBuffer");
                    150:             me->read = me->data;
                    151:         }
                    152:        return YES;
                    153:     }
                    154:     return NO;
                    155: }
                    156: 
2.1       frystyk   157: PRIVATE int HTBufferWriter_abort (HTOutputStream * me, HTList * e)
                    158: {
2.25      frystyk   159:     HTTRACE(STREAM_TRACE, "Buffer...... ABORTING...\n");
2.1       frystyk   160:     if (me->target) (*me->target->isa->abort)(me->target, e);
2.8       frystyk   161:     if (me->timer) {
                    162:        HTTimer_delete(me->timer);
                    163:        me->timer = NULL;
                    164:     }
2.1       frystyk   165:     return HT_ERROR;
                    166: }
                    167: 
2.6       frystyk   168: PRIVATE int HTBufferWriter_write (HTOutputStream * me, const char * buf, int len)
2.1       frystyk   169: {
                    170:     int status;
2.16      frystyk   171:     while (1) {
                    172:        int available = me->data + me->allocated - me->read;
                    173: 
                    174:        /* If we have enough buffer space */
                    175:        if (len <= available) {
                    176:            int size = 0;
                    177:            memcpy(me->read, buf, len);
                    178:            me->read += len;
2.15      frystyk   179:         
2.16      frystyk   180:            /* If we have accumulated enough data then flush */
                    181:            if ((size = me->read - me->data) > me->growby) {
                    182:                me->lastFlushTime = HTGetTimeInMillis();
                    183:                status = PUTBLOCK(me->data, size);
                    184:                if (status == HT_OK) {
                    185:                    me->read = me->data;
                    186:                } else {
                    187:                    return (status == HT_WOULD_BLOCK) ? HT_OK : HT_ERROR;
                    188:                }
                    189:            }
                    190:            return HT_OK;
                    191:        } else {
                    192: 
                    193:            /* Fill the existing buffer (if not already) and flush */
                    194:            if (available) {
                    195:                memcpy(me->read, buf, available);
                    196:                buf += available;
                    197:                len -= available;
                    198:                me->read += available;
                    199:            }
                    200:            me->lastFlushTime = HTGetTimeInMillis();
                    201:            status = PUTBLOCK(me->data, me->allocated);
                    202:            if (status == HT_OK) {
                    203:                me->read = me->data;
                    204:            } else if (status == HT_WOULD_BLOCK) {
                    205:                HTBufferWriter_addBuffer(me, len);
                    206:                memcpy(me->read, buf, len);
                    207:                me->read += len;
                    208:                return HT_OK;
                    209:            }
                    210:        }
2.15      frystyk   211:     }
2.1       frystyk   212: }
                    213: 
                    214: /*     Character handling
                    215: **     ------------------
                    216: */
                    217: PRIVATE int HTBufferWriter_put_character (HTOutputStream * me, char c)
                    218: {
                    219:     return HTBufferWriter_write(me, &c, 1);
                    220: }
                    221: 
                    222: /*     String handling
                    223: **     ---------------
                    224: **
                    225: **     Strings must be smaller than this buffer size.
                    226: */
                    227: PRIVATE int HTBufferWriter_put_string (HTOutputStream * me, const char * s)
                    228: {
                    229:     return HTBufferWriter_write(me, s, (int) strlen(s));
                    230: }
                    231: /*
                    232: **     The difference between the close and the free method is that we don't
                    233: **     close the connection in the free method - we only call the free method
                    234: **     of the target stream. That way, we can keep the output stream as long 
                    235: **     as the channel itself.
                    236: */
                    237: PRIVATE int HTBufferWriter_close (HTOutputStream * me)
                    238: {
                    239:     if (me) {
                    240:        if (me->target) (*me->target->isa->close)(me->target);
                    241:        HT_FREE(me->data);
                    242:        HT_FREE(me);
                    243:     }
                    244:     return HT_OK;
                    245: }
                    246: 
                    247: PRIVATE const HTOutputStreamClass HTBufferWriter =
                    248: {              
2.6       frystyk   249:     "BufferedSocketWriter",
                    250:     HTBufferWriter_lazyFlush,
2.1       frystyk   251:     HTBufferWriter_free,
                    252:     HTBufferWriter_abort,
                    253:     HTBufferWriter_put_character,
                    254:     HTBufferWriter_put_string,
                    255:     HTBufferWriter_write,
                    256:     HTBufferWriter_close
                    257: }; 
                    258: 
2.26    ! frystyk   259: PRIVATE HTOutputStream * buffer_new (HTHost * host, HTChannel * ch,
        !           260:                                     void * param, int bufsize)
2.1       frystyk   261: {
2.6       frystyk   262:     if (host && ch) {
2.1       frystyk   263:        HTOutputStream * me = HTChannel_output(ch);
2.6       frystyk   264:        if (!me) {
                    265:            HTOutputStream * me;
2.16      frystyk   266:            int tcpbufsize = 0;
                    267: 
                    268: #if defined(HAVE_GETSOCKOPT) && defined(SO_SNDBUF)
                    269:            /*
                    270:            ** Get the TCP socket buffer size
                    271:            */
                    272:            {
                    273:                SOCKET sockfd = HTChannel_socket(ch);
                    274:                int size = sizeof(int);
                    275:                int status = getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF,
                    276:                                        (void *) &tcpbufsize, &size);
                    277:                if (status == -1) {
2.25      frystyk   278:                    HTTRACE(STREAM_TRACE, "Socket...... Could not get TCP send buffer size for socket %d\n" _ sockfd);
2.16      frystyk   279:                } else {
2.25      frystyk   280:                    HTTRACE(STREAM_TRACE, "Socket...... TCP send buffer size is %d for socket %d\n" _ tcpbufsize _ sockfd);
2.16      frystyk   281:                }               
                    282:            }
                    283: #endif
2.17      frystyk   284:            if (bufsize <= 0) bufsize = tcpbufsize ? tcpbufsize : OUTPUT_BUFFER_SIZE;
2.6       frystyk   285:            if ((me = (HTOutputStream *) HT_CALLOC(1, sizeof(HTOutputStream)))==NULL ||
2.1       frystyk   286:                (me->data = (char *) HT_MALLOC(bufsize)) == NULL)
                    287:                HT_OUTOFMEM("HTBufferWriter_new");
                    288:            me->isa = &HTBufferWriter;
                    289:            me->read = me->data;
2.15      frystyk   290:            me->allocated = bufsize;
                    291:             me->growby = bufsize;
2.16      frystyk   292:            me->expo = 1;
2.6       frystyk   293:            me->host = host;
2.15      frystyk   294:            return me;
2.26    ! frystyk   295:        }
        !           296:     }
        !           297:     return NULL;
        !           298: }
        !           299: 
        !           300: PUBLIC HTOutputStream * HTBufferWriter_new (HTHost *           host,
        !           301:                                            HTChannel *         ch,
        !           302:                                            void *              param,
        !           303:                                            int                 bufsize)
        !           304: {
        !           305:     HTOutputStream * me = buffer_new(host, ch, param, bufsize);
        !           306:     if (me) {
        !           307:        me->target = HTWriter_new(host, ch, param, 0);
        !           308:        return me;
        !           309:     }
        !           310:     return NULL;
        !           311: }
        !           312: 
        !           313: PUBLIC HTOutputStream * HTBufferConverter_new (HTHost *        host,
        !           314:                                               HTChannel *      ch,
        !           315:                                               void *           param,
        !           316:                                               int              bufsize,
        !           317:                                               HTOutputStream * target)
        !           318: {
        !           319:     if (target) {
        !           320:        HTOutputStream * me = buffer_new(host, ch, param, bufsize);
        !           321:        if (me) {
        !           322:            me->target = target;
        !           323:            return me;
2.6       frystyk   324:        }
2.1       frystyk   325:     }
                    326:     return NULL;
                    327: }

Webmaster