Beta WinNT support. David Allen [djallen@raleigh.ibm.com]
[mod_fastcgi.git] / fcgi_buf.c
blobe407c64003e91429f773fc386b15e3fb4f5002ac
1 /*
2 * $Id: fcgi_buf.c,v 1.4 2000/04/27 02:27:35 robs 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 #ifdef WIN32
60 int fcgi_buf_add_fd(Buffer *buf, SOCKET fd)
61 #else
62 int fcgi_buf_add_fd(Buffer *buf, int fd)
63 #endif
65 size_t len;
66 #ifdef WIN32
67 DWORD bytesRead;
68 #endif
70 fcgi_buf_check(buf);
72 if (buf->length == buf->size)
73 /* there's no room in the buffer, return "success" */
74 return 1;
76 if (buf->length == 0)
77 /* the buffer is empty so defrag */
78 buf->begin = buf->end = buf->data;
80 len = min(buf->size - buf->length, buf->data + buf->size - buf->end);
82 #ifndef NO_WRITEV
83 /* assume there is a readv() if there is a writev() */
84 if (len == buf->size - buf->length) {
85 /* its not wrapped, use read() instead of readv() */
86 #endif
89 #ifdef WIN32
91 if (!ReadFile((HANDLE) fd, buf->end, len, &bytesRead, NULL)) {
92 int err = GetLastError();
93 len = recv(fd, buf->end, len, 0);
95 else {
96 len = bytesRead;
99 #else
100 len = read(fd, buf->end, len);
101 #endif
102 while (len == -1 && errno == EINTR);
104 if (len <= 0)
105 return len;
107 buf->end += len;
108 buf->length += len;
110 if (buf->end == (buf->data + buf->size)) {
111 /* the buffer needs to be wrapped */
112 buf->end = buf->data;
113 #ifndef NO_WRITEV
115 } else {
116 /* the buffer is wrapped, use readv() */
117 struct iovec vec[2];
119 vec[0].iov_base = buf->end;
120 vec[0].iov_len = len;
121 vec[1].iov_base = buf->data;
122 vec[1].iov_len = buf->size - buf->length - len;
125 len = readv(fd, vec, 2);
126 while (len == -1 && errno == EINTR);
128 if (len <= 0)
129 return len;
131 buf->end += len;
132 if (buf->end >= (buf->data + buf->size))
133 buf->end -= buf->size;
135 buf->length += len;
137 #else
138 if (buf->length < buf->size) {
139 /* There's still more buffer space to read into. */
141 fd_set read_set;
142 int status;
143 int numFDs = fd + 1;
144 struct timeval timeOut;
146 FD_ZERO(&read_set);
147 FD_SET(fd, &read_set);
149 timeOut.tv_sec = 0;
150 timeOut.tv_usec = 0;
152 status = ap_select(numFDs, &read_set, NULL, NULL, &timeOut);
154 if (status < 0)
155 return status; /* error, errno is set */
157 if (status > 0 && FD_ISSET(fd, &read_set)) {
160 len = read(fd, buf->end, buf->size - buf->length);
161 while (len == -1 && errno == EINTR);
163 if (len <= 0)
164 return len;
166 buf->end += len;
167 buf->length += len;
171 #endif
172 return len; /* this may not contain the number of bytes read */
175 /*******************************************************************************
176 * Write from the buffer to an open file descriptor.
178 * The caller should disable the default Apache SIGPIPE handler,
179 * otherwise a bad script could cause the request to abort appearing
180 * as though the client's fd caused it.
182 * Results:
183 * <0 if an error occured (bytes may or may not have been written)
184 * =0 if no bytes were written
185 * >0 successful write
187 #ifdef WIN32
188 int fcgi_buf_get_to_fd(Buffer *buf, SOCKET fd)
189 #else
190 int fcgi_buf_get_to_fd(Buffer *buf, int fd)
191 #endif
193 size_t len;
194 #ifdef WIN32
195 DWORD bytesWritten;
196 #endif
198 fcgi_buf_check(buf);
200 if (buf->length == 0)
201 return 0;
203 len = min(buf->length, buf->data + buf->size - buf->begin);
205 #ifndef NO_WRITEV
206 if (len == buf->length) {
207 /* the buffer is not wrapped, we don't need to use writev() */
208 #endif
211 #ifdef WIN32
213 int err;
215 if (!WriteFile((HANDLE) fd, (LPVOID) buf->begin, len, &bytesWritten, NULL)) {
216 err =GetLastError();
218 len = send(fd, buf->begin, len, 0);
220 else
221 len = bytesWritten;
223 #else
224 len = write(fd, buf->begin, len);
225 #endif
226 while (len == -1 && errno == EINTR);
228 if (len <= 0)
229 goto Return;
231 buf->begin += len;
232 buf->length -= len;
234 if (buf->begin == buf->data + buf->size) {
235 /* the buffer needs to be wrapped */
236 buf->begin = buf->data;
238 #ifndef NO_WRITEV
240 } else {
241 /* the buffer is wrapped, use writev() */
242 struct iovec vec[2];
244 vec[0].iov_base = buf->begin;
245 vec[0].iov_len = len;
246 vec[1].iov_base = buf->data;
247 vec[1].iov_len = buf->length - len;
250 len = writev(fd, vec, 2);
251 while (len == -1 && errno == EINTR);
253 if (len <= 0)
254 goto Return;
256 buf->begin += len;
257 buf->length -= len;
259 if (buf->begin >= buf->data + buf->size)
260 buf->begin -= buf->size;
262 #else
263 if (buf->length > 0) {
264 /* there's still more data to write */
266 fd_set write_set;
267 int status;
268 int numFDs = fd + 1;
269 struct timeval timeOut;
271 FD_ZERO(&write_set);
272 FD_SET(fd, &write_set);
274 timeOut.tv_sec = 0;
275 timeOut.tv_usec = 0;
277 status = ap_select(numFDs, NULL, &write_set, NULL, &timeOut);
279 if (status < 0) {
280 len = status; /* error, errno is set */
281 goto Return;
284 if (status > 0 && FD_ISSET(fd, &write_set)) {
285 int len2;
288 len2 = write(fd, buf->begin, buf->length);
289 while (len == -1 && errno == EINTR);
291 if (len2 < 0) {
292 len = len2;
293 goto Return;
296 if (len2 > 0) {
297 buf->begin += len2;
298 buf->length -= len2;
299 len += len2;
304 #endif
306 Return:
307 if (buf->length == 0)
308 buf->begin = buf->end = buf->data;
310 return len;
313 /*******************************************************************************
314 * Return the data block start address and the length of the block.
316 void fcgi_buf_get_block_info(Buffer *buf, char **beginPtr, int *countPtr)
318 fcgi_buf_check(buf);
319 *beginPtr = buf->begin;
320 *countPtr = min(buf->length,
321 buf->data + buf->size - buf->begin);
324 /*******************************************************************************
325 * Throw away bytes from buffer.
327 void fcgi_buf_toss(Buffer *buf, size_t count)
329 fcgi_buf_check(buf);
330 ap_assert(count >= 0 && (int) count <= buf->length);
332 buf->length -= count;
333 buf->begin += count;
334 if(buf->begin >= buf->data + buf->size) {
335 buf->begin -= buf->size;
339 /*******************************************************************************
340 * Return the free data block start address and the length of the block.
342 void fcgi_buf_get_free_block_info(Buffer *buf, char **endPtr, int *countPtr)
344 fcgi_buf_check(buf);
345 *endPtr = buf->end;
346 *countPtr = min(buf->size - buf->length,
347 buf->data + buf->size - buf->end);
350 /*******************************************************************************
351 * Updates the buf to reflect recently added data.
353 void fcgi_buf_add_update(Buffer *buf, size_t count)
355 fcgi_buf_check(buf);
356 ap_assert(count >= 0 && (int) count <= BufferFree(buf));
358 buf->length += count;
359 buf->end += count;
360 if(buf->end >= buf->data + buf->size) {
361 buf->end -= buf->size;
364 fcgi_buf_check(buf);
367 /*******************************************************************************
368 * Adds a block of data to a buffer, returning the number of bytes added.
370 int fcgi_buf_add_block(Buffer *buf, char *data, size_t datalen)
372 char *end;
373 int copied = 0; /* Number of bytes actually copied. */
374 size_t canCopy; /* Number of bytes to copy in a given op. */
376 ap_assert(data != NULL);
377 if(datalen == 0) {
378 return 0;
381 ap_assert(datalen > 0);
382 fcgi_buf_check(buf);
383 end = buf->data + buf->size;
386 * Copy the first part of the data: from here to the end of the
387 * buffer, or the end of the data, whichever comes first.
389 datalen = min(BufferFree(buf), (int) datalen);
390 canCopy = min((int) datalen, end - buf->end);
391 memcpy(buf->end, data, canCopy);
392 buf->length += canCopy;
393 buf->end += canCopy;
394 copied += canCopy;
395 if (buf->end >= end) {
396 buf->end = buf->data;
398 datalen -= canCopy;
401 * If there's more to go, copy the second part starting from the
402 * beginning of the buffer.
404 if (datalen > 0) {
405 data += canCopy;
406 memcpy(buf->end, data, datalen);
407 buf->length += datalen;
408 buf->end += datalen;
409 copied += datalen;
411 return(copied);
414 /*******************************************************************************
415 * Add a string to a buffer, returning the number of bytes added.
417 int fcgi_buf_add_string(Buffer *buf, char *str)
419 return fcgi_buf_add_block(buf, str, strlen(str));
422 /*******************************************************************************
423 * Gets a data block from a buffer, returning the number of bytes copied.
425 int fcgi_buf_get_to_block(Buffer *buf, char *data, int datalen)
427 char *end;
428 int copied = 0; /* Number of bytes actually copied. */
429 size_t canCopy; /* Number of bytes to copy in a given op. */
431 ap_assert(data != NULL);
432 ap_assert(datalen > 0);
433 fcgi_buf_check(buf);
434 end = buf->data + buf->size;
437 * Copy the first part out of the buffer: from here to the end
438 * of the buffer, or all of the requested data.
440 canCopy = min(buf->length, datalen);
441 canCopy = min((int) canCopy, end - buf->begin);
442 memcpy(data, buf->begin, canCopy);
443 buf->length -= canCopy;
444 buf->begin += canCopy;
445 copied += canCopy;
446 if (buf->begin >= end) {
447 buf->begin = buf->data;
451 * If there's more to go, copy the second part starting from the
452 * beginning of the buffer.
454 if (copied < datalen && buf->length > 0) {
455 data += copied;
456 canCopy = min(buf->length, datalen - copied);
457 memcpy(data, buf->begin, canCopy);
458 buf->length -= canCopy;
459 buf->begin += canCopy;
460 copied += canCopy;
462 fcgi_buf_check(buf);
463 return(copied);
466 /*******************************************************************************
467 * Move 'len' bytes from 'src' buffer to 'dest' buffer. There must be at
468 * least 'len' bytes available in the source buffer and space for 'len'
469 * bytes in the destination buffer.
471 void fcgi_buf_get_to_buf(Buffer *dest, Buffer *src, int len)
473 char *dest_end, *src_begin;
474 size_t dest_len, src_len, move_len;
476 ap_assert(len > 0);
477 ap_assert(BufferLength(src) >= len);
478 ap_assert(BufferFree(dest) >= len);
480 fcgi_buf_check(src);
481 fcgi_buf_check(dest);
483 while (1) {
484 if (len == 0)
485 return;
487 fcgi_buf_get_free_block_info(dest, &dest_end, &dest_len);
488 fcgi_buf_get_block_info(src, &src_begin, &src_len);
490 move_len = min(dest_len, src_len);
491 move_len = min((int) move_len, len);
493 if (move_len == 0)
494 return;
496 memcpy(dest_end, src_begin, move_len);
497 fcgi_buf_toss(src, move_len);
498 fcgi_buf_add_update(dest, move_len);
499 len -= move_len;
503 static void array_grow(array_header *arr, size_t n)
505 if (n <= 0)
506 return;
508 if ((int) (arr->nelts + n) > arr->nalloc) {
509 char *new_elts;
510 int new_nalloc = (arr->nalloc <= 0) ? n : arr->nelts + n;
512 new_elts = ap_pcalloc(arr->pool, arr->elt_size * new_nalloc);
513 memcpy(new_elts, arr->elts, arr->nelts * arr->elt_size);
515 arr->elts = new_elts;
516 arr->nalloc = new_nalloc;
520 static void array_cat_block(array_header *arr, void *block, size_t n)
522 array_grow(arr, n);
523 memcpy(arr->elts + arr->nelts * arr->elt_size, block, n * arr->elt_size);
524 arr->nelts += n;
527 /*----------------------------------------------------------------------
528 * Append "len" bytes from "buf" into "arr". Apache arrays are used
529 * whenever the data being handled is binary (may contain null chars).
531 void fcgi_buf_get_to_array(Buffer *buf, array_header *arr, size_t len)
533 int len1 = min(buf->length, buf->data + buf->size - buf->begin);
535 fcgi_buf_check(buf);
536 ap_assert(len > 0);
537 ap_assert((int) len <= BufferLength(buf));
539 array_grow(arr, len);
541 len1 = min(len1, (int) len);
542 array_cat_block(arr, buf->begin, len1);
544 if (len1 < (int) len)
545 array_cat_block(arr, buf->data, len - len1);
547 fcgi_buf_toss(buf, len);