Fix stderr handling again
[mod_fastcgi.git] / fcgi_buf.c
blob29564868ff200e3449f024702a0e2903f3c0aee7
1 /*
2 * $Id: fcgi_buf.c,v 1.3 1999/09/24 02:25:00 roberts Exp $
3 */
5 #include "fcgi.h"
7 /*******************************************************************************
8 * Check buffer consistency with assertions.
9 */
10 void fcgi_buf_check(Buffer *buf)
12 ap_assert(buf->size > 0);
13 ap_assert(buf->length >= 0);
14 ap_assert(buf->length <= buf->size);
16 ap_assert(buf->begin >= buf->data);
17 ap_assert(buf->begin < buf->data + buf->size);
18 ap_assert(buf->end >= buf->data);
19 ap_assert(buf->end < buf->data + buf->size);
21 ap_assert(((buf->end - buf->begin + buf->size) % buf->size)
22 == (buf->length % buf->size));
25 /*******************************************************************************
26 * Reset buffer, losing any data that's in it.
28 void fcgi_buf_reset(Buffer *buf)
30 buf->length = 0;
31 buf->begin = buf->end = buf->data;
34 /*******************************************************************************
35 * Allocate and intialize a new buffer of the specified size.
37 Buffer *fcgi_buf_new(pool *p, int size)
39 Buffer *buf;
41 buf = (Buffer *)ap_pcalloc(p, sizeof(Buffer) + size);
42 buf->size = size;
43 fcgi_buf_reset(buf);
44 return buf;
47 /*******************************************************************************
48 * Read from an open file descriptor into buffer.
50 * The caller should disable the default Apache SIGPIPE handler,
51 * otherwise a bad script could cause the request to abort and appear
52 * as though the client's fd caused it.
54 * Results:
55 * <0 error, errno is set
56 * =0 EOF reached
57 * >0 successful read or no room in buffer (NOT # of bytes read)
59 int fcgi_buf_add_fd(Buffer *buf, int fd)
61 size_t len;
63 fcgi_buf_check(buf);
65 if (buf->length == buf->size)
66 /* there's no room in the buffer, return "success" */
67 return 1;
69 if (buf->length == 0)
70 /* the buffer is empty so defrag */
71 buf->begin = buf->end = buf->data;
73 len = min(buf->size - buf->length, buf->data + buf->size - buf->end);
75 #ifndef NO_WRITEV
76 /* assume there is a readv() if there is a writev() */
77 if (len == buf->size - buf->length) {
78 /* its not wrapped, use read() instead of readv() */
79 #endif
82 len = read(fd, buf->end, len);
83 while (len == -1 && errno == EINTR);
85 if (len <= 0)
86 return len;
88 buf->end += len;
89 buf->length += len;
91 if (buf->end == (buf->data + buf->size)) {
92 /* the buffer needs to be wrapped */
93 buf->end = buf->data;
94 #ifndef NO_WRITEV
96 } else {
97 /* the buffer is wrapped, use readv() */
98 struct iovec vec[2];
100 vec[0].iov_base = buf->end;
101 vec[0].iov_len = len;
102 vec[1].iov_base = buf->data;
103 vec[1].iov_len = buf->size - buf->length - len;
106 len = readv(fd, vec, 2);
107 while (len == -1 && errno == EINTR);
109 if (len <= 0)
110 return len;
112 buf->end += len;
113 if (buf->end >= (buf->data + buf->size))
114 buf->end -= buf->size;
116 buf->length += len;
118 #else
119 if (buf->length < buf->size) {
120 /* There's still more buffer space to read into. */
122 fd_set read_set;
123 int status;
124 int numFDs = fd + 1;
125 struct timeval timeOut;
127 FD_ZERO(&read_set);
128 FD_SET(fd, &read_set);
130 timeOut.tv_sec = 0;
131 timeOut.tv_usec = 0;
133 status = ap_select(numFDs, &read_set, NULL, NULL, &timeOut);
135 if (status < 0)
136 return status; /* error, errno is set */
138 if (status > 0 && FD_ISSET(fd, &read_set)) {
141 len = read(fd, buf->end, buf->size - buf->length);
142 while (len == -1 && errno == EINTR);
144 if (len <= 0)
145 return len;
147 buf->end += len;
148 buf->length += len;
152 #endif
153 return len; /* this may not contain the number of bytes read */
156 /*******************************************************************************
157 * Write from the buffer to an open file descriptor.
159 * The caller should disable the default Apache SIGPIPE handler,
160 * otherwise a bad script could cause the request to abort appearing
161 * as though the client's fd caused it.
163 * Results:
164 * <0 if an error occured (bytes may or may not have been written)
165 * =0 if no bytes were written
166 * >0 successful write
168 int fcgi_buf_get_to_fd(Buffer *buf, int fd)
170 size_t len;
172 fcgi_buf_check(buf);
174 if (buf->length == 0)
175 return 0;
177 len = min(buf->length, buf->data + buf->size - buf->begin);
179 #ifndef NO_WRITEV
180 if (len == buf->length) {
181 /* the buffer is not wrapped, we don't need to use writev() */
182 #endif
185 len = write(fd, buf->begin, len);
186 while (len == -1 && errno == EINTR);
188 if (len <= 0)
189 goto Return;
191 buf->begin += len;
192 buf->length -= len;
194 if (buf->begin == buf->data + buf->size) {
195 /* the buffer needs to be wrapped */
196 buf->begin = buf->data;
198 #ifndef NO_WRITEV
200 } else {
201 /* the buffer is wrapped, use writev() */
202 struct iovec vec[2];
204 vec[0].iov_base = buf->begin;
205 vec[0].iov_len = len;
206 vec[1].iov_base = buf->data;
207 vec[1].iov_len = buf->length - len;
210 len = writev(fd, vec, 2);
211 while (len == -1 && errno == EINTR);
213 if (len <= 0)
214 goto Return;
216 buf->begin += len;
217 buf->length -= len;
219 if (buf->begin >= buf->data + buf->size)
220 buf->begin -= buf->size;
222 #else
223 if (buf->length > 0) {
224 /* there's still more data to write */
226 fd_set write_set;
227 int status;
228 int numFDs = fd + 1;
229 struct timeval timeOut;
231 FD_ZERO(&write_set);
232 FD_SET(fd, &write_set);
234 timeOut.tv_sec = 0;
235 timeOut.tv_usec = 0;
237 status = ap_select(numFDs, NULL, &write_set, NULL, &timeOut);
239 if (status < 0) {
240 len = status; /* error, errno is set */
241 goto Return;
244 if (status > 0 && FD_ISSET(fd, &write_set)) {
245 int len2;
248 len2 = write(fd, buf->begin, buf->length);
249 while (len == -1 && errno == EINTR);
251 if (len2 < 0) {
252 len = len2;
253 goto Return;
256 if (len2 > 0) {
257 buf->begin += len2;
258 buf->length -= len2;
259 len += len2;
264 #endif
266 Return:
267 if (buf->length == 0)
268 buf->begin = buf->end = buf->data;
270 return len;
273 /*******************************************************************************
274 * Return the data block start address and the length of the block.
276 void fcgi_buf_get_block_info(Buffer *buf, char **beginPtr, int *countPtr)
278 fcgi_buf_check(buf);
279 *beginPtr = buf->begin;
280 *countPtr = min(buf->length,
281 buf->data + buf->size - buf->begin);
284 /*******************************************************************************
285 * Throw away bytes from buffer.
287 void fcgi_buf_toss(Buffer *buf, size_t count)
289 fcgi_buf_check(buf);
290 ap_assert(count >= 0 && count <= buf->length);
292 buf->length -= count;
293 buf->begin += count;
294 if(buf->begin >= buf->data + buf->size) {
295 buf->begin -= buf->size;
299 /*******************************************************************************
300 * Return the free data block start address and the length of the block.
302 void fcgi_buf_get_free_block_info(Buffer *buf, char **endPtr, int *countPtr)
304 fcgi_buf_check(buf);
305 *endPtr = buf->end;
306 *countPtr = min(buf->size - buf->length,
307 buf->data + buf->size - buf->end);
310 /*******************************************************************************
311 * Updates the buf to reflect recently added data.
313 void fcgi_buf_add_update(Buffer *buf, size_t count)
315 fcgi_buf_check(buf);
316 ap_assert(count >= 0 && count <= BufferFree(buf));
318 buf->length += count;
319 buf->end += count;
320 if(buf->end >= buf->data + buf->size) {
321 buf->end -= buf->size;
324 fcgi_buf_check(buf);
327 /*******************************************************************************
328 * Adds a block of data to a buffer, returning the number of bytes added.
330 int fcgi_buf_add_block(Buffer *buf, char *data, size_t datalen)
332 char *end;
333 int copied = 0; /* Number of bytes actually copied. */
334 size_t canCopy; /* Number of bytes to copy in a given op. */
336 ap_assert(data != NULL);
337 if(datalen == 0) {
338 return 0;
341 ap_assert(datalen > 0);
342 fcgi_buf_check(buf);
343 end = buf->data + buf->size;
346 * Copy the first part of the data: from here to the end of the
347 * buffer, or the end of the data, whichever comes first.
349 datalen = min(BufferFree(buf), datalen);
350 canCopy = min(datalen, end - buf->end);
351 memcpy(buf->end, data, canCopy);
352 buf->length += canCopy;
353 buf->end += canCopy;
354 copied += canCopy;
355 if (buf->end >= end) {
356 buf->end = buf->data;
358 datalen -= canCopy;
361 * If there's more to go, copy the second part starting from the
362 * beginning of the buffer.
364 if (datalen > 0) {
365 data += canCopy;
366 memcpy(buf->end, data, datalen);
367 buf->length += datalen;
368 buf->end += datalen;
369 copied += datalen;
371 return(copied);
374 /*******************************************************************************
375 * Add a string to a buffer, returning the number of bytes added.
377 int fcgi_buf_add_string(Buffer *buf, char *str)
379 return fcgi_buf_add_block(buf, str, strlen(str));
382 /*******************************************************************************
383 * Gets a data block from a buffer, returning the number of bytes copied.
385 int fcgi_buf_get_to_block(Buffer *buf, char *data, int datalen)
387 char *end;
388 int copied = 0; /* Number of bytes actually copied. */
389 size_t canCopy; /* Number of bytes to copy in a given op. */
391 ap_assert(data != NULL);
392 ap_assert(datalen > 0);
393 fcgi_buf_check(buf);
394 end = buf->data + buf->size;
397 * Copy the first part out of the buffer: from here to the end
398 * of the buffer, or all of the requested data.
400 canCopy = min(buf->length, datalen);
401 canCopy = min(canCopy, end - buf->begin);
402 memcpy(data, buf->begin, canCopy);
403 buf->length -= canCopy;
404 buf->begin += canCopy;
405 copied += canCopy;
406 if (buf->begin >= end) {
407 buf->begin = buf->data;
411 * If there's more to go, copy the second part starting from the
412 * beginning of the buffer.
414 if (copied < datalen && buf->length > 0) {
415 data += copied;
416 canCopy = min(buf->length, datalen - copied);
417 memcpy(data, buf->begin, canCopy);
418 buf->length -= canCopy;
419 buf->begin += canCopy;
420 copied += canCopy;
422 fcgi_buf_check(buf);
423 return(copied);
426 /*******************************************************************************
427 * Move 'len' bytes from 'src' buffer to 'dest' buffer. There must be at
428 * least 'len' bytes available in the source buffer and space for 'len'
429 * bytes in the destination buffer.
431 void fcgi_buf_get_to_buf(Buffer *dest, Buffer *src, int len)
433 char *dest_end, *src_begin;
434 size_t dest_len, src_len, move_len;
436 ap_assert(len > 0);
437 ap_assert(BufferLength(src) >= len);
438 ap_assert(BufferFree(dest) >= len);
440 fcgi_buf_check(src);
441 fcgi_buf_check(dest);
443 while (1) {
444 if (len == 0)
445 return;
447 fcgi_buf_get_free_block_info(dest, &dest_end, &dest_len);
448 fcgi_buf_get_block_info(src, &src_begin, &src_len);
450 move_len = min(dest_len, src_len);
451 move_len = min(move_len, len);
453 if (move_len == 0)
454 return;
456 memcpy(dest_end, src_begin, move_len);
457 fcgi_buf_toss(src, move_len);
458 fcgi_buf_add_update(dest, move_len);
459 len -= move_len;
463 static void array_grow(array_header *arr, size_t n)
465 if (n <= 0)
466 return;
468 if (arr->nelts + n > arr->nalloc) {
469 char *new_elts;
470 int new_nalloc = (arr->nalloc <= 0) ? n : arr->nelts + n;
472 new_elts = ap_pcalloc(arr->pool, arr->elt_size * new_nalloc);
473 memcpy(new_elts, arr->elts, arr->nelts * arr->elt_size);
475 arr->elts = new_elts;
476 arr->nalloc = new_nalloc;
480 static void array_cat_block(array_header *arr, void *block, size_t n)
482 array_grow(arr, n);
483 memcpy(arr->elts + arr->nelts * arr->elt_size, block, n * arr->elt_size);
484 arr->nelts += n;
487 /*----------------------------------------------------------------------
488 * Append "len" bytes from "buf" into "arr". Apache arrays are used
489 * whenever the data being handled is binary (may contain null chars).
491 void fcgi_buf_get_to_array(Buffer *buf, array_header *arr, size_t len)
493 int len1 = min(buf->length, buf->data + buf->size - buf->begin);
495 fcgi_buf_check(buf);
496 ap_assert(len > 0);
497 ap_assert(len <= BufferLength(buf));
499 array_grow(arr, len);
501 len1 = min(len1, len);
502 array_cat_block(arr, buf->begin, len1);
504 if (len1 < len)
505 array_cat_block(arr, buf->data, len - len1);
507 fcgi_buf_toss(buf, len);