2 * $Id: fcgi_buf.c,v 1.3 1999/09/24 02:25:00 roberts Exp $
7 /*******************************************************************************
8 * Check buffer consistency with assertions.
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
)
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
)
41 buf
= (Buffer
*)ap_pcalloc(p
, sizeof(Buffer
) + size
);
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.
55 * <0 error, errno is set
57 * >0 successful read or no room in buffer (NOT # of bytes read)
59 int fcgi_buf_add_fd(Buffer
*buf
, int fd
)
65 if (buf
->length
== buf
->size
)
66 /* there's no room in the buffer, return "success" */
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
);
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() */
82 len
= read(fd
, buf
->end
, len
);
83 while (len
== -1 && errno
== EINTR
);
91 if (buf
->end
== (buf
->data
+ buf
->size
)) {
92 /* the buffer needs to be wrapped */
97 /* the buffer is wrapped, use readv() */
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
);
113 if (buf
->end
>= (buf
->data
+ buf
->size
))
114 buf
->end
-= buf
->size
;
119 if (buf
->length
< buf
->size
) {
120 /* There's still more buffer space to read into. */
125 struct timeval timeOut
;
128 FD_SET(fd
, &read_set
);
133 status
= ap_select(numFDs
, &read_set
, NULL
, NULL
, &timeOut
);
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
);
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.
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
)
174 if (buf
->length
== 0)
177 len
= min(buf
->length
, buf
->data
+ buf
->size
- buf
->begin
);
180 if (len
== buf
->length
) {
181 /* the buffer is not wrapped, we don't need to use writev() */
185 len
= write(fd
, buf
->begin
, len
);
186 while (len
== -1 && errno
== EINTR
);
194 if (buf
->begin
== buf
->data
+ buf
->size
) {
195 /* the buffer needs to be wrapped */
196 buf
->begin
= buf
->data
;
201 /* the buffer is wrapped, use writev() */
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
);
219 if (buf
->begin
>= buf
->data
+ buf
->size
)
220 buf
->begin
-= buf
->size
;
223 if (buf
->length
> 0) {
224 /* there's still more data to write */
229 struct timeval timeOut
;
232 FD_SET(fd
, &write_set
);
237 status
= ap_select(numFDs
, NULL
, &write_set
, NULL
, &timeOut
);
240 len
= status
; /* error, errno is set */
244 if (status
> 0 && FD_ISSET(fd
, &write_set
)) {
248 len2
= write(fd
, buf
->begin
, buf
->length
);
249 while (len
== -1 && errno
== EINTR
);
267 if (buf
->length
== 0)
268 buf
->begin
= buf
->end
= buf
->data
;
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
)
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
)
290 ap_assert(count
>= 0 && count
<= buf
->length
);
292 buf
->length
-= 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
)
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
)
316 ap_assert(count
>= 0 && count
<= BufferFree(buf
));
318 buf
->length
+= count
;
320 if(buf
->end
>= buf
->data
+ buf
->size
) {
321 buf
->end
-= buf
->size
;
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
)
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
);
341 ap_assert(datalen
> 0);
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
;
355 if (buf
->end
>= end
) {
356 buf
->end
= buf
->data
;
361 * If there's more to go, copy the second part starting from the
362 * beginning of the buffer.
366 memcpy(buf
->end
, data
, datalen
);
367 buf
->length
+= datalen
;
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
)
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);
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
;
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) {
416 canCopy
= min(buf
->length
, datalen
- copied
);
417 memcpy(data
, buf
->begin
, canCopy
);
418 buf
->length
-= canCopy
;
419 buf
->begin
+= canCopy
;
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
;
437 ap_assert(BufferLength(src
) >= len
);
438 ap_assert(BufferFree(dest
) >= len
);
441 fcgi_buf_check(dest
);
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
);
456 memcpy(dest_end
, src_begin
, move_len
);
457 fcgi_buf_toss(src
, move_len
);
458 fcgi_buf_add_update(dest
, move_len
);
463 static void array_grow(array_header
*arr
, size_t n
)
468 if (arr
->nelts
+ n
> arr
->nalloc
) {
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
)
483 memcpy(arr
->elts
+ arr
->nelts
* arr
->elt_size
, block
, n
* arr
->elt_size
);
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
);
497 ap_assert(len
<= BufferLength(buf
));
499 array_grow(arr
, len
);
501 len1
= min(len1
, len
);
502 array_cat_block(arr
, buf
->begin
, len1
);
505 array_cat_block(arr
, buf
->data
, len
- len1
);
507 fcgi_buf_toss(buf
, len
);