typedef struct CORE_OUTPUT_FILTER_CTX {
apr_bucket_brigade *b;
} core_output_filter_ctx_t;
+
#define MAX_IOVEC_TO_WRITE 16
+
static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
{
apr_status_t rv;
- apr_bucket_brigade *more = NULL;
- apr_size_t bytes_sent = 0, nbytes;
- apr_bucket *e;
conn_rec *c = f->c;
core_output_filter_ctx_t *ctx = f->ctx;
- apr_size_t nvec = 0;
- apr_size_t nvec_trailers= 0;
- struct iovec vec[MAX_IOVEC_TO_WRITE];
- struct iovec vec_trailers[MAX_IOVEC_TO_WRITE];
-
- apr_file_t *fd = NULL;
- apr_size_t flen = 0;
- apr_off_t foffset = 0;
-
if (ctx == NULL) {
f->ctx = ctx = apr_pcalloc(c->pool, sizeof(core_output_filter_ctx_t));
}
+
/* If we have a saved brigade, concatenate the new brigade to it */
if (ctx->b) {
APR_BRIGADE_CONCAT(ctx->b, b);
ctx->b = NULL;
}
- /* Iterate over the brigade collecting iovecs */
- do {
- while (b) {
- nbytes = 0; /* in case more points to another brigade */
- APR_BRIGADE_FOREACH(e, b) {
- if (APR_BUCKET_IS_EOS(e) || APR_BUCKET_IS_FLUSH(e)) {
+ /* Perform multiple passes over the brigade, sending batches of output
+ to the connection. */
+ while (b) {
+ apr_size_t nbytes = 0;
+ apr_bucket *e;
+
+ /* tail of brigade if we need another pass */
+ apr_bucket_brigade *more = NULL;
+
+ /* one group of iovecs per pass over the brigade */
+ apr_size_t nvec = 0;
+ apr_size_t nvec_trailers = 0;
+ struct iovec vec[MAX_IOVEC_TO_WRITE];
+ struct iovec vec_trailers[MAX_IOVEC_TO_WRITE];
+
+ /* one file per pass over the brigade */
+ apr_file_t *fd = NULL;
+ apr_size_t flen = 0;
+ apr_off_t foffset = 0;
+
+ /* Iterate over the brigade: collect iovecs and/or a file */
+ APR_BRIGADE_FOREACH(e, b) {
+ if (APR_BUCKET_IS_EOS(e) || APR_BUCKET_IS_FLUSH(e)) {
+ break;
+ }
+ /* It doesn't make any sense to use sendfile for a file bucket
+ * that represents 10 bytes.
+ */
+ else if (APR_BUCKET_IS_FILE(e)
+ && (e->length >= AP_MIN_SENDFILE_BYTES)) {
+ apr_bucket_shared *s = e->data;
+ apr_bucket_file *a = s->data;
+
+ /* We can't handle more than one file bucket at a time
+ * so we split here and send the file we have already
+ * found.
+ */
+ if (fd) {
+ more = apr_brigade_split(b, e);
break;
}
- /* It doesn't make any sense to use sendfile for a file bucket
- * that represents 10 bytes.
- */
- else if (APR_BUCKET_IS_FILE(e) && (e->length >= AP_MIN_SENDFILE_BYTES)) {
- apr_bucket_shared *s = e->data;
- apr_bucket_file *a = s->data;
- /* We can't handle more than one file bucket at a time
- * so we split here and send the file we have already found.
- */
- if (fd) {
- more = apr_brigade_split(b, e);
- break;
+ fd = a->fd;
+ flen = e->length;
+ foffset = s->start;
+ }
+ else {
+ const char *str;
+ apr_size_t n;
+
+ rv = apr_bucket_read(e, &str, &n, APR_BLOCK_READ);
+ if (n) {
+ nbytes += n;
+ if (!fd) {
+ vec[nvec].iov_base = (char*) str;
+ vec[nvec].iov_len = n;
+ nvec++;
}
-
- fd = a->fd;
- flen = e->length;
- foffset = s->start;
- }
- else {
- const char *str;
- apr_size_t n;
- rv = apr_bucket_read(e, &str, &n, APR_BLOCK_READ);
- if (n) {
- nbytes += n;
- if (!fd) {
- vec[nvec].iov_base = (char*) str;
- vec[nvec].iov_len = n;
- nvec++;
- }
- else {
- /* The bucket is a trailer to a file bucket */
- vec_trailers[nvec_trailers].iov_base = (char*) str;
- vec_trailers[nvec_trailers].iov_len = n;
- nvec_trailers++;
- }
+ else {
+ /* The bucket is a trailer to a file bucket */
+ vec_trailers[nvec_trailers].iov_base = (char*) str;
+ vec_trailers[nvec_trailers].iov_len = n;
+ nvec_trailers++;
}
}
+ }
- if ((nvec == MAX_IOVEC_TO_WRITE) ||
- (nvec_trailers == MAX_IOVEC_TO_WRITE)) {
- /* Split the brigade and break */
- if (APR_BUCKET_NEXT(e) != APR_BRIGADE_SENTINEL(b)) {
- more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
- }
- break;
+ if ((nvec == MAX_IOVEC_TO_WRITE) ||
+ (nvec_trailers == MAX_IOVEC_TO_WRITE)) {
+ /* Split the brigade and break */
+ if (APR_BUCKET_NEXT(e) != APR_BRIGADE_SENTINEL(b)) {
+ more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
}
+ break;
}
+ }
- /* Completed iterating over the brigades, now determine if we want
- * to buffer the brigade or send the brigade out on the network
- */
- if ((!fd && (!more) &&
- (nbytes < AP_MIN_BYTES_TO_WRITE) && !APR_BUCKET_IS_FLUSH(e))
- || (APR_BUCKET_IS_EOS(e) && c->keepalive)) {
+ /* Completed iterating over the brigades, now determine if we want
+ * to buffer the brigade or send the brigade out on the network.
+ *
+ * Save if:
+ *
+ * 1) we didn't see a file, we don't have more passes over the
+ * brigade to perform, we haven't accumulated enough bytes to
+ * send, AND we didn't stop at a FLUSH bucket.
+ * (IOW, we will save away plain old bytes)
+ * or
+ * 2) we hit the EOS and have a keep-alive connection
+ * (IOW, this response is a bit more complex, but we save it
+ * with the hope of concatenating with another response)
+ */
+ if ((!fd && !more &&
+ (nbytes < AP_MIN_BYTES_TO_WRITE) && !APR_BUCKET_IS_FLUSH(e))
+ || (APR_BUCKET_IS_EOS(e) && c->keepalive)) {
- /* NEVER save an EOS in here. If we are saving a brigade with
- * an EOS bucket, then we are doing keepalive connections, and
- * we want to process to second request fully.
- */
- if (APR_BUCKET_IS_EOS(e)) {
- APR_BUCKET_REMOVE(e);
- apr_bucket_destroy(e);
- }
- ap_save_brigade(f, &ctx->b, &b);
- return APR_SUCCESS;
+ /* NEVER save an EOS in here. If we are saving a brigade with
+ * an EOS bucket, then we are doing keepalive connections, and
+ * we want to process to second request fully.
+ */
+ if (APR_BUCKET_IS_EOS(e)) {
+ APR_BUCKET_REMOVE(e);
+ apr_bucket_destroy(e);
}
- if (fd) {
- apr_hdtr_t hdtr;
+ ap_save_brigade(f, &ctx->b, &b);
+ return APR_SUCCESS;
+ }
+
+ if (fd) {
+ apr_hdtr_t hdtr;
#if APR_HAS_SENDFILE
- apr_int32_t flags = 0;
+ apr_int32_t flags = 0;
#endif
- memset(&hdtr, '\0', sizeof(hdtr));
- if (nvec) {
- hdtr.numheaders = nvec;
- hdtr.headers = vec;
- }
- if (nvec_trailers) {
- hdtr.numtrailers = nvec_trailers;
- hdtr.trailers = vec_trailers;
- }
+ memset(&hdtr, '\0', sizeof(hdtr));
+ if (nvec) {
+ hdtr.numheaders = nvec;
+ hdtr.headers = vec;
+ }
+ if (nvec_trailers) {
+ hdtr.numtrailers = nvec_trailers;
+ hdtr.trailers = vec_trailers;
+ }
#if APR_HAS_SENDFILE
- if (!c->keepalive) {
- /* Prepare the socket to be reused */
- flags |= APR_SENDFILE_DISCONNECT_SOCKET;
- }
- rv = sendfile_it_all(c, /* the connection */
- fd, /* the file to send */
- &hdtr, /* header and trailer iovecs */
- foffset, /* offset in the file to begin
- sending from */
- flen, /* length of file */
- nbytes + flen, /* total length including
- headers */
- flags); /* apr_sendfile flags */
+ if (!c->keepalive) {
+ /* Prepare the socket to be reused */
+ flags |= APR_SENDFILE_DISCONNECT_SOCKET;
+ }
+ rv = sendfile_it_all(c, /* the connection */
+ fd, /* the file to send */
+ &hdtr, /* header and trailer iovecs */
+ foffset, /* offset in the file to begin
+ sending from */
+ flen, /* length of file */
+ nbytes + flen, /* total length including
+ headers */
+ flags); /* apr_sendfile flags */
- /* If apr_sendfile() returns APR_ENOTIMPL, call send_the_file()
- * to loop on apr_read/apr_send to send the file. Our Windows
- * binary distributions (which work on Windows 9x/NT) are
- * compiled on Windows NT. TransmitFile is not available on
- * Windows 95/98 and we discover this at runtime when
- * apr_sendfile() returns APR_ENOTIMPL. Having apr_sendfile()
- * return APR_ENOTIMPL seems the cleanest way to handle this
- * case.
- */
- if (rv == APR_ENOTIMPL) {
+ /* If apr_sendfile() returns APR_ENOTIMPL, call send_the_file()
+ * to loop on apr_read/apr_send to send the file. Our Windows
+ * binary distributions (which work on Windows 9x/NT) are
+ * compiled on Windows NT. TransmitFile is not available on
+ * Windows 95/98 and we discover this at runtime when
+ * apr_sendfile() returns APR_ENOTIMPL. Having apr_sendfile()
+ * return APR_ENOTIMPL seems the cleanest way to handle this
+ * case.
+ */
+ if (rv == APR_ENOTIMPL) {
#endif
-
- rv = send_the_file(c, fd, &hdtr, foffset, flen, &bytes_sent);
+ apr_size_t unused_bytes_sent;
+
+ rv = send_the_file(c, fd, &hdtr, foffset, flen,
+ &unused_bytes_sent);
#if APR_HAS_SENDFILE
- }
-#endif
- fd = NULL;
- }
- else {
- rv = writev_it_all(c->client_socket,
- vec, nvec,
- nbytes, &bytes_sent);
}
-
- apr_brigade_destroy(b);
- if (rv != APR_SUCCESS) {
- /* XXX: log the error */
- if (more)
- apr_brigade_destroy(more);
- return rv;
- }
- nvec = 0;
- nvec_trailers = 0;
-
- b = more;
- more = NULL;
- } /* end while () */
+#endif
+ fd = NULL;
+ }
+ else {
+ apr_size_t unused_bytes_sent;
+
+ rv = writev_it_all(c->client_socket,
+ vec, nvec,
+ nbytes, &unused_bytes_sent);
+ }
+ apr_brigade_destroy(b);
+ if (rv != APR_SUCCESS) {
+ /* XXX: log the error */
+ if (more)
+ apr_brigade_destroy(more);
+ return rv;
+ }
+
b = more;
- } while (more);
+ more = NULL;
+ } /* end while () */
return APR_SUCCESS;
}