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

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.16    ! frystyk     6: **     @(#) $Id: HTConLen.c,v 2.14.2.4 1996/11/11 20:52:00 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.10      frystyk    13: #include "sysdep.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) {
                    118:        if (STREAM_TRACE)
2.16    ! frystyk   119:            HTTrace("Buffer...... size %d reached, going transparent\n",
2.1       frystyk   120:                    me->max_size);
                    121:        return NO;
                    122:     } else if (size) {
                    123:        me->tmp_ind = 0;
                    124:        me->tmp_max = size;
2.8       frystyk   125:        if ((me->tmp_buf = (char  *) HT_MALLOC(size)) == NULL)
                    126:            HT_OUTOFMEM("buf_put_char");
2.1       frystyk   127:        if (STREAM_TRACE)
2.16    ! frystyk   128:            HTTrace("Buffer...... created with len %d\n", size);
2.1       frystyk   129:        return YES;
                    130:     }
                    131:     return NO;
                    132: }
                    133: 
2.14      frystyk   134: /*
                    135: **     After flushing the buffer we go into transparent mode so that we still
                    136: **     can handle incoming data. If we already are in transparent mode then
                    137: **     don't do anything.
                    138: */
2.1       frystyk   139: PRIVATE int buf_flush (HTStream * me)
                    140: {
2.16    ! frystyk   141:     if (me->state != HT_BS_TRANSPARENT) {
2.14      frystyk   142:        HTBufItem * cur;
                    143:        if (me->tmp_buf) append_buf(me);
                    144:        while ((cur = me->head)) {
                    145:            int status;
                    146:            if ((status = PUTBLOCK(cur->buf, cur->len)) != HT_OK) {
                    147:                return status;
                    148:            }
                    149:            me->head = cur->next;
                    150:            free_buf(cur);
                    151:        }
2.16    ! frystyk   152: 
        !           153:        /* If we are not a pipe then do no more buffering */
        !           154:        if (!(me->mode & HT_BM_PIPE)) me->state = HT_BS_TRANSPARENT;
2.1       frystyk   155:     }
2.16    ! frystyk   156:     return (*me->target->isa->flush)(me->target);
2.1       frystyk   157: }
                    158: 
2.10      frystyk   159: PRIVATE int buf_put_block (HTStream * me, const char * b, int l)
2.1       frystyk   160: {
2.14      frystyk   161:     /*
                    162:     **  If we are in pause mode then don't write anything but return PAUSE.
                    163:     **  The upper stream should then respect it and don't write any more data.
                    164:     */
2.16    ! frystyk   165:     if (me->state == HT_BS_PAUSE) return HT_PAUSE;
2.14      frystyk   166: 
                    167:     /*
                    168:     **  Start handling the incoming data. If we are still buffering then add
                    169:     **  it to the buffer. Otherwise just pump it through. Note that we still
                    170:     **  count the length - even if we have given up buffering!
                    171:     */
2.1       frystyk   172:     me->conlen += l;
2.16    ! frystyk   173:     if (me->state != HT_BS_TRANSPARENT) {
2.14      frystyk   174: 
                    175:        /*
                    176:        **  If there is still room in the existing chunk then fill it up.
                    177:        **  Otherwise create a new chunk and add it to the linked list of
                    178:        **  chunks. If the buffer fills up then either return HT_PAUSE or
                    179:        **  flush it and go transparent.
                    180:        */
2.1       frystyk   181:        if (me->tmp_buf && me->tmp_max-me->tmp_ind >= l) {     /* Still room */
                    182:            memcpy(me->tmp_buf + me->tmp_ind, b, l);
                    183:            me->tmp_ind += l;
                    184:            return HT_OK;
                    185:        } else {
2.14      frystyk   186: 
                    187:            /*
                    188:            **  Add the temporary buffer (if any) to the list of chunks
                    189:            */
2.1       frystyk   190:            if (me->tmp_buf) append_buf(me);
2.14      frystyk   191: 
                    192:            /*
                    193:            **  Find the right size of the next chunk. We increase the size
                    194:            **  exponentially until we reach HT_MAX_BLOCK in order to minimize
                    195:            **  the number of mallocs.
                    196:            */
2.1       frystyk   197:            if (me->cur_size < HT_MAX_BLOCK) {
                    198:                int newsize = me->cur_size ? me->cur_size : HT_MIN_BLOCK;
                    199:                while (l > newsize && newsize < HT_MAX_BLOCK) newsize *= 2;
                    200:                me->cur_size = newsize;
                    201:            }
2.14      frystyk   202: 
                    203:            if (alloc_new(me, me->cur_size)) {
                    204:                /* Buffer could accept the new data */
                    205:                memcpy(me->tmp_buf, b, l);
                    206:                me->tmp_ind = l;
2.16    ! frystyk   207:            } else if (me->mode & HT_BM_DELAY) {
2.14      frystyk   208:                /* Buffer ran full and we pause */
2.16    ! frystyk   209:                me->state = HT_BS_PAUSE;
        !           210:                if (STREAM_TRACE) HTTrace("Buffer....... Paused\n");
2.14      frystyk   211:                return HT_PAUSE;
                    212:            } else {
                    213:                /* Buffer ran full and we flush and go transparent */
2.1       frystyk   214:                int status = buf_flush(me);
                    215:                if (status != HT_OK) return status;
                    216:            }
                    217:        }
                    218:     }
2.14      frystyk   219: 
                    220:     /*
                    221:     **  If we couldn't buffer the data then check whether we should give up
                    222:     **  or pause the stream. If we are in transparent mode then put the rest
                    223:     **  of the data down the pipe.
                    224:     */
2.16    ! frystyk   225:     if (me->state == HT_BS_TRANSPARENT) return PUTBLOCK(b, l);
2.1       frystyk   226:     return HT_OK;
                    227: }
                    228: 
                    229: PRIVATE int buf_put_char (HTStream * me, char c)
                    230: {
                    231:     return buf_put_block(me, &c, 1);
                    232: }
                    233: 
2.10      frystyk   234: PRIVATE int buf_put_string (HTStream * me, const char * s)
2.1       frystyk   235: {
                    236:     return buf_put_block(me, s, (int) strlen(s));
                    237: }
                    238: 
                    239: PRIVATE int buf_free (HTStream * me)
                    240: {
                    241:     int status = HT_OK;
2.14      frystyk   242: 
                    243:     /*
2.15      frystyk   244:     **  If the buffer has not been flushed explicit and we are a pipe buffer
                    245:     **  then we don't free it.
                    246:     */ 
                    247:     if (me->pipe && me->state != HT_BUFFER_TRANSPARENT) {
                    248:        if (STREAM_TRACE) HTTrace("PipeBuffer Waiting to be flushed\n");
                    249:        return HT_OK;
                    250:     }
                    251: 
                    252:     /*
2.14      frystyk   253:     **  Should we count the content length and assign it to the
                    254:     **  anchor?
                    255:     */
2.16    ! frystyk   256:     if (me->mode & HT_BM_COUNT && me->request) {
2.14      frystyk   257:        HTParentAnchor * anchor = HTRequest_anchor(me->request);
2.1       frystyk   258:        if (STREAM_TRACE)
2.16    ! frystyk   259:            HTTrace("Buffer........ Calculated content-length: %d\n", me->conlen);
2.1       frystyk   260:        HTAnchor_setLength(anchor, me->conlen);
                    261:     }
2.14      frystyk   262: 
                    263:     /*
                    264:     **  Flush the buffered data - even if we are paused. That is, we always
                    265:     **  flush - except if we have already flushed the buffer, of course.
2.15      frystyk   266:     **  Also, we don't flush if we are a PIPE buffer. Then the flush MUST be
                    267:     **  called explicitly and the buffer can not be freed before this 
                    268:     **  happens.
2.14      frystyk   269:     */
                    270:     if ((status = buf_flush(me)) != HT_OK)
2.1       frystyk   271:        return status;
                    272:     if ((status = (*me->target->isa->_free)(me->target)) != HT_OK)
                    273:        return status;
2.8       frystyk   274:     HT_FREE(me);
2.1       frystyk   275:     return status;
                    276: }
                    277: 
2.3       frystyk   278: PRIVATE int buf_abort (HTStream * me, HTList * e)
2.1       frystyk   279: {
2.6       frystyk   280:     if (me->target) (*me->target->isa->abort)(me->target,e);
                    281:     free_buf_all(me);
2.8       frystyk   282:     HT_FREE(me);
2.16    ! frystyk   283:     if (PROT_TRACE) HTTrace("Buffer...... ABORTING...\n");
2.1       frystyk   284:     return HT_ERROR;
                    285: }
                    286: 
2.16    ! frystyk   287: HTStreamClass HTBufferClass = {
        !           288:     "Buffer",
2.1       frystyk   289:     buf_flush,
                    290:     buf_free,
                    291:     buf_abort,
                    292:     buf_put_char,
                    293:     buf_put_string,
                    294:     buf_put_block
                    295: };
                    296: 
2.16    ! frystyk   297: PUBLIC HTStream * HTBuffer_new (HTStream *     target,
        !           298:                                HTRequest *     request,
        !           299:                                int             max_size)
2.1       frystyk   300: {
2.8       frystyk   301:     HTStream * me;
                    302:     if ((me = (HTStream  *) HT_CALLOC(1, sizeof(HTStream))) == NULL)
2.16    ! frystyk   303:         HT_OUTOFMEM("HTBufferStream");
        !           304:     me->isa = &HTBufferClass;
2.1       frystyk   305:     me->target = target;
                    306:     me->request = request;
                    307:     me->max_size = (max_size > 0) ? max_size : HT_MAX_SIZE;
2.16    ! frystyk   308:     me->mode = HT_BM_PLAIN;
        !           309:     if (STREAM_TRACE) HTTrace("Buffer...... Created with size %d\n", me->max_size);
2.1       frystyk   310:     return me;
                    311: }
                    312: 
2.16    ! frystyk   313: PUBLIC HTStream * HTDelayBuffer (HTStream * target, int max_size)
2.1       frystyk   314: {
2.16    ! frystyk   315:     HTStream * me = HTBuffer_new(target, NULL, max_size);
2.14      frystyk   316:     if (me) {
2.16    ! frystyk   317:        me->mode = HT_BM_DELAY;
2.14      frystyk   318:        return me;
                    319:     }
                    320:     return HTErrorStream();
                    321: }
                    322: 
2.16    ! frystyk   323: PUBLIC HTStream * HTContentCounter (HTStream * target,
2.14      frystyk   324:                                    HTRequest * request,
                    325:                                    int         max_size)
                    326: {
2.16    ! frystyk   327:     HTStream * me = HTBuffer_new(target, NULL, max_size);
        !           328:     if (me) {
        !           329:        me->mode = HT_BM_COUNT;
        !           330:        return me;
        !           331:     }
        !           332:     return HTErrorStream();
        !           333: }
        !           334: 
        !           335: PUBLIC HTStream * HTPipeBuffer (HTStream * target, int max_size)
        !           336: {
        !           337:     HTStream * me = HTBuffer_new(target, NULL, max_size);
2.14      frystyk   338:     if (me) {
2.16    ! frystyk   339:        me->mode = HT_BM_PIPE;
2.14      frystyk   340:        return me;
                    341:     }
                    342:     return HTErrorStream();
2.1       frystyk   343: }

Webmaster