Annotation of libwww/Library/src/HTConLen.c, revision 2.19

2.1       frystyk     1: /*                                                                  HTConlen.c
                      2: **     CONTENT LENGTH COUNTER STREAM
                      3: **
                      4: **     (c) COPYRIGHT MIT 1995.
                      5: **     Please first read the full copyright statement in the file COPYRIGH.
2.19    ! frystyk     6: **     @(#) $Id: HTConLen.c,v 2.18 1998/05/04 19:36:23 frystyk Exp $
2.1       frystyk     7: **
                      8: **     This stream counts the number of bytes in a stream until it reaches
                      9: **     max number of bytes it can occupy. If this happens then it gives up.
                     10: */
                     11: 
                     12: /* Library include files */
2.18      frystyk    13: #include "wwwsys.h"
2.13      frystyk    14: #include "WWWUtil.h"
                     15: #include "WWWCore.h"
2.1       frystyk    16: #include "HTConLen.h"                                   /* Implemented here */
                     17: 
2.7       frystyk    18: #define HT_MIN_BLOCK   0x100
2.1       frystyk    19: #define HT_MAX_BLOCK   0x2000
                     20: #define HT_MAX_SIZE    0x10000
2.12      frystyk    21: #define PUTBLOCK(b, l) (*me->target->isa->put_block)(me->target,(b),(l))
2.1       frystyk    22: 
                     23: typedef struct _HTBufItem {
                     24:     int                        len;
                     25:     char *             buf;
                     26:     struct _HTBufItem *        next;
                     27: } HTBufItem;
                     28: 
2.16      frystyk    29: typedef enum _BufferMode {
                     30:     HT_BM_PLAIN                = 0x0,
                     31:     HT_BM_DELAY                = 0x1,          /* Buffer full and we pause */
                     32:     HT_BM_COUNT                = 0x2,          /* Content length counter */
                     33:     HT_BM_PIPE         = 0x4           /* Pipe buffer */
                     34: } BufferMode;
                     35: 
2.14      frystyk    36: typedef enum _BufferState {
2.16      frystyk    37:     HT_BS_OK           = 0,
                     38:     HT_BS_PAUSE                = 1,
                     39:     HT_BS_TRANSPARENT  = 2
2.14      frystyk    40: } BufferState;
                     41: 
2.1       frystyk    42: struct _HTStream {
                     43:     HTStreamClass *    isa;
                     44:     HTRequest *                request;
                     45:     HTStream *         target;
                     46: 
                     47:     char *             tmp_buf;
                     48:     int                        tmp_ind;
                     49:     int                        tmp_max;
                     50:     HTBufItem *                head;
                     51:     HTBufItem *                tail;
                     52: 
                     53:     int                        max_size;
                     54:     int                        cur_size;
                     55:     int                        conlen;
2.16      frystyk    56:     BufferMode         mode;                      /* State of the buffering */
                     57:     BufferState                state;
2.1       frystyk    58: };
                     59: 
                     60: /* ------------------------------------------------------------------------- */
                     61: 
                     62: /*
                     63: **     MIME output with content-length calculation
                     64: **     -------------------------------------------
                     65: **     This stream also buffers the result to find out the content length.
                     66: **     If a maximum buffer limit is reached Content-Length is calculated
                     67: **     for logs but it is not sent to the client -- rather the buffer is
                     68: **     flushed right away.
                     69: **     Code taken from HTRequest.c written by Ari Luotonen and modified to
                     70: **     fit new stream model
                     71: */
                     72: 
                     73: PRIVATE BOOL free_buf (HTBufItem * me)
                     74: {
                     75:     if (me) {
2.8       frystyk    76:        HT_FREE(me->buf);
                     77:        HT_FREE(me);
2.1       frystyk    78:        return YES;
                     79:     }
                     80:     return NO;
                     81: }
                     82: 
                     83: PRIVATE void free_buf_all (HTStream * me)
                     84: {
                     85:     HTBufItem * cur = me->head;
                     86:     HTBufItem * killme;
2.6       frystyk    87:     me->tmp_ind = 0;
                     88:     me->tmp_max = 0;
2.8       frystyk    89:     HT_FREE(me->tmp_buf);
2.1       frystyk    90:     while (cur) {
                     91:        killme = cur;
                     92:        cur = cur->next;
                     93:        free_buf(cur);
                     94:     }
                     95:     me->head = me->tail = NULL;
                     96: }
                     97: 
                     98: PRIVATE void append_buf (HTStream * me)
                     99: {
2.8       frystyk   100:     HTBufItem * b;
                    101:     if ((b = (HTBufItem  *) HT_CALLOC(1, sizeof(HTBufItem))) == NULL)
                    102:         HT_OUTOFMEM("append_buf");
2.1       frystyk   103:     b->len = me->tmp_ind;
                    104:     b->buf = me->tmp_buf;
                    105:     me->tmp_ind = 0;
                    106:     me->tmp_max = 0;
                    107:     me->tmp_buf = 0;
                    108:     if (me->tail)
                    109:        me->tail->next = b;
                    110:     else
                    111:        me->head = b;
                    112:     me->tail = b;
                    113: }
                    114: 
                    115: PRIVATE BOOL alloc_new (HTStream * me, int size)
                    116: {
                    117:     if (me->conlen >= me->max_size) {
2.19    ! frystyk   118:        HTTRACE(STREAM_TRACE, "Buffer...... size %d reached, going transparent\n" _ 
2.1       frystyk   119:                    me->max_size);
                    120:        return NO;
                    121:     } else if (size) {
                    122:        me->tmp_ind = 0;
                    123:        me->tmp_max = size;
2.8       frystyk   124:        if ((me->tmp_buf = (char  *) HT_MALLOC(size)) == NULL)
                    125:            HT_OUTOFMEM("buf_put_char");
2.19    ! frystyk   126:        HTTRACE(STREAM_TRACE, "Buffer...... created with len %d\n" _ size);
2.1       frystyk   127:        return YES;
                    128:     }
                    129:     return NO;
                    130: }
                    131: 
2.14      frystyk   132: /*
                    133: **     After flushing the buffer we go into transparent mode so that we still
                    134: **     can handle incoming data. If we already are in transparent mode then
                    135: **     don't do anything.
                    136: */
2.1       frystyk   137: PRIVATE int buf_flush (HTStream * me)
                    138: {
2.16      frystyk   139:     if (me->state != HT_BS_TRANSPARENT) {
2.14      frystyk   140:        HTBufItem * cur;
                    141:        if (me->tmp_buf) append_buf(me);
                    142:        while ((cur = me->head)) {
                    143:            int status;
                    144:            if ((status = PUTBLOCK(cur->buf, cur->len)) != HT_OK) {
                    145:                return status;
                    146:            }
                    147:            me->head = cur->next;
                    148:            free_buf(cur);
                    149:        }
2.16      frystyk   150: 
                    151:        /* If we are not a pipe then do no more buffering */
                    152:        if (!(me->mode & HT_BM_PIPE)) me->state = HT_BS_TRANSPARENT;
2.1       frystyk   153:     }
2.16      frystyk   154:     return (*me->target->isa->flush)(me->target);
2.1       frystyk   155: }
                    156: 
2.10      frystyk   157: PRIVATE int buf_put_block (HTStream * me, const char * b, int l)
2.1       frystyk   158: {
2.14      frystyk   159:     /*
                    160:     **  If we are in pause mode then don't write anything but return PAUSE.
                    161:     **  The upper stream should then respect it and don't write any more data.
                    162:     */
2.16      frystyk   163:     if (me->state == HT_BS_PAUSE) return HT_PAUSE;
2.14      frystyk   164: 
                    165:     /*
                    166:     **  Start handling the incoming data. If we are still buffering then add
                    167:     **  it to the buffer. Otherwise just pump it through. Note that we still
                    168:     **  count the length - even if we have given up buffering!
                    169:     */
2.1       frystyk   170:     me->conlen += l;
2.16      frystyk   171:     if (me->state != HT_BS_TRANSPARENT) {
2.14      frystyk   172: 
                    173:        /*
                    174:        **  If there is still room in the existing chunk then fill it up.
                    175:        **  Otherwise create a new chunk and add it to the linked list of
                    176:        **  chunks. If the buffer fills up then either return HT_PAUSE or
                    177:        **  flush it and go transparent.
                    178:        */
2.1       frystyk   179:        if (me->tmp_buf && me->tmp_max-me->tmp_ind >= l) {     /* Still room */
                    180:            memcpy(me->tmp_buf + me->tmp_ind, b, l);
                    181:            me->tmp_ind += l;
                    182:            return HT_OK;
                    183:        } else {
2.14      frystyk   184: 
                    185:            /*
                    186:            **  Add the temporary buffer (if any) to the list of chunks
                    187:            */
2.1       frystyk   188:            if (me->tmp_buf) append_buf(me);
2.14      frystyk   189: 
                    190:            /*
                    191:            **  Find the right size of the next chunk. We increase the size
                    192:            **  exponentially until we reach HT_MAX_BLOCK in order to minimize
                    193:            **  the number of mallocs.
                    194:            */
2.1       frystyk   195:            if (me->cur_size < HT_MAX_BLOCK) {
                    196:                int newsize = me->cur_size ? me->cur_size : HT_MIN_BLOCK;
                    197:                while (l > newsize && newsize < HT_MAX_BLOCK) newsize *= 2;
                    198:                me->cur_size = newsize;
                    199:            }
2.14      frystyk   200: 
                    201:            if (alloc_new(me, me->cur_size)) {
                    202:                /* Buffer could accept the new data */
                    203:                memcpy(me->tmp_buf, b, l);
                    204:                me->tmp_ind = l;
2.16      frystyk   205:            } else if (me->mode & HT_BM_DELAY) {
2.14      frystyk   206:                /* Buffer ran full and we pause */
2.16      frystyk   207:                me->state = HT_BS_PAUSE;
2.19    ! frystyk   208:                HTTRACE(STREAM_TRACE, "Buffer....... Paused\n");
2.14      frystyk   209:                return HT_PAUSE;
                    210:            } else {
                    211:                /* Buffer ran full and we flush and go transparent */
2.1       frystyk   212:                int status = buf_flush(me);
                    213:                if (status != HT_OK) return status;
                    214:            }
                    215:        }
                    216:     }
2.14      frystyk   217: 
                    218:     /*
                    219:     **  If we couldn't buffer the data then check whether we should give up
                    220:     **  or pause the stream. If we are in transparent mode then put the rest
                    221:     **  of the data down the pipe.
                    222:     */
2.16      frystyk   223:     if (me->state == HT_BS_TRANSPARENT) return PUTBLOCK(b, l);
2.1       frystyk   224:     return HT_OK;
                    225: }
                    226: 
                    227: PRIVATE int buf_put_char (HTStream * me, char c)
                    228: {
                    229:     return buf_put_block(me, &c, 1);
                    230: }
                    231: 
2.10      frystyk   232: PRIVATE int buf_put_string (HTStream * me, const char * s)
2.1       frystyk   233: {
                    234:     return buf_put_block(me, s, (int) strlen(s));
                    235: }
                    236: 
                    237: PRIVATE int buf_free (HTStream * me)
                    238: {
                    239:     int status = HT_OK;
2.14      frystyk   240: 
                    241:     /*
2.15      frystyk   242:     **  If the buffer has not been flushed explicit and we are a pipe buffer
                    243:     **  then we don't free it.
                    244:     */ 
2.17      frystyk   245:     if (me->mode & HT_BM_PIPE && me->state != HT_BS_TRANSPARENT) {
2.19    ! frystyk   246:        HTTRACE(STREAM_TRACE, "PipeBuffer Waiting to be flushed\n");
2.15      frystyk   247:        return HT_OK;
                    248:     }
                    249: 
                    250:     /*
2.14      frystyk   251:     **  Should we count the content length and assign it to the
                    252:     **  anchor?
                    253:     */
2.16      frystyk   254:     if (me->mode & HT_BM_COUNT && me->request) {
2.14      frystyk   255:        HTParentAnchor * anchor = HTRequest_anchor(me->request);
2.19    ! frystyk   256:        HTTRACE(STREAM_TRACE, "Buffer........ Calculated content-length: %d\n" _ me->conlen);
2.1       frystyk   257:        HTAnchor_setLength(anchor, me->conlen);
                    258:     }
2.14      frystyk   259: 
                    260:     /*
                    261:     **  Flush the buffered data - even if we are paused. That is, we always
                    262:     **  flush - except if we have already flushed the buffer, of course.
2.15      frystyk   263:     **  Also, we don't flush if we are a PIPE buffer. Then the flush MUST be
                    264:     **  called explicitly and the buffer can not be freed before this 
                    265:     **  happens.
2.14      frystyk   266:     */
                    267:     if ((status = buf_flush(me)) != HT_OK)
2.1       frystyk   268:        return status;
                    269:     if ((status = (*me->target->isa->_free)(me->target)) != HT_OK)
                    270:        return status;
2.8       frystyk   271:     HT_FREE(me);
2.1       frystyk   272:     return status;
                    273: }
                    274: 
2.3       frystyk   275: PRIVATE int buf_abort (HTStream * me, HTList * e)
2.1       frystyk   276: {
2.6       frystyk   277:     if (me->target) (*me->target->isa->abort)(me->target,e);
                    278:     free_buf_all(me);
2.8       frystyk   279:     HT_FREE(me);
2.19    ! frystyk   280:     HTTRACE(PROT_TRACE, "Buffer...... ABORTING...\n");
2.1       frystyk   281:     return HT_ERROR;
                    282: }
                    283: 
2.16      frystyk   284: HTStreamClass HTBufferClass = {
                    285:     "Buffer",
2.1       frystyk   286:     buf_flush,
                    287:     buf_free,
                    288:     buf_abort,
                    289:     buf_put_char,
                    290:     buf_put_string,
                    291:     buf_put_block
                    292: };
                    293: 
2.16      frystyk   294: PUBLIC HTStream * HTBuffer_new (HTStream *     target,
                    295:                                HTRequest *     request,
                    296:                                int             max_size)
2.1       frystyk   297: {
2.8       frystyk   298:     HTStream * me;
                    299:     if ((me = (HTStream  *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
2.16      frystyk   300:         HT_OUTOFMEM("HTBufferStream");
                    301:     me->isa = &HTBufferClass;
2.1       frystyk   302:     me->target = target;
                    303:     me->request = request;
                    304:     me->max_size = (max_size > 0) ? max_size : HT_MAX_SIZE;
2.16      frystyk   305:     me->mode = HT_BM_PLAIN;
2.19    ! frystyk   306:     HTTRACE(STREAM_TRACE, "Buffer...... Created with size %d\n" _ me->max_size);
2.1       frystyk   307:     return me;
                    308: }
                    309: 
2.16      frystyk   310: PUBLIC HTStream * HTDelayBuffer (HTStream * target, int max_size)
2.1       frystyk   311: {
2.16      frystyk   312:     HTStream * me = HTBuffer_new(target, NULL, max_size);
2.14      frystyk   313:     if (me) {
2.16      frystyk   314:        me->mode = HT_BM_DELAY;
2.14      frystyk   315:        return me;
                    316:     }
                    317:     return HTErrorStream();
                    318: }
                    319: 
2.16      frystyk   320: PUBLIC HTStream * HTContentCounter (HTStream * target,
2.14      frystyk   321:                                    HTRequest * request,
                    322:                                    int         max_size)
                    323: {
2.16      frystyk   324:     HTStream * me = HTBuffer_new(target, NULL, max_size);
                    325:     if (me) {
                    326:        me->mode = HT_BM_COUNT;
                    327:        return me;
                    328:     }
                    329:     return HTErrorStream();
                    330: }
                    331: 
                    332: PUBLIC HTStream * HTPipeBuffer (HTStream * target, int max_size)
                    333: {
                    334:     HTStream * me = HTBuffer_new(target, NULL, max_size);
2.14      frystyk   335:     if (me) {
2.16      frystyk   336:        me->mode = HT_BM_PIPE;
2.14      frystyk   337:        return me;
                    338:     }
                    339:     return HTErrorStream();
2.1       frystyk   340: }

Webmaster