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