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