You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by sf...@apache.org on 2010/06/19 11:25:47 UTC
svn commit: r956204 - /httpd/httpd/trunk/server/core_filters.c
Author: sf
Date: Sat Jun 19 09:25:46 2010
New Revision: 956204
URL: http://svn.apache.org/viewvc?rev=956204&view=rev
Log:
core_output_filter improvements:
- prevent more than 5 pipelined responses in the output brigade, to avoid
excessive FD usage and related DoS attacks.
- if non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER, don't send the
entire brigade non-blocking, but only up to the bucket that brought us
over THRESHOLD_MAX_BUFFER. This should allow better use of async write
completion.
Modified:
httpd/httpd/trunk/server/core_filters.c
Modified: httpd/httpd/trunk/server/core_filters.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/server/core_filters.c?rev=956204&r1=956203&r2=956204&view=diff
==============================================================================
--- httpd/httpd/trunk/server/core_filters.c (original)
+++ httpd/httpd/trunk/server/core_filters.c Sat Jun 19 09:25:46 2010
@@ -344,8 +344,10 @@ static apr_status_t sendfile_nonblocking
conn_rec *c);
#endif
+/* XXX: Should these be configurable parameters? */
#define THRESHOLD_MIN_WRITE 4096
#define THRESHOLD_MAX_BUFFER 65536
+#define MAX_REQUESTS_IN_PIPELINE 5
/* Optional function coming from mod_logio, used for logging of output
* traffic
@@ -358,8 +360,9 @@ apr_status_t ap_core_output_filter(ap_fi
core_net_rec *net = f->ctx;
core_output_filter_ctx_t *ctx = net->out_ctx;
apr_bucket_brigade *bb = NULL;
- apr_bucket *bucket, *next;
+ apr_bucket *bucket, *next, *flush_upto = NULL;
apr_size_t bytes_in_brigade, non_file_bytes_in_brigade;
+ int eor_buckets_in_brigade;
apr_status_t rv;
/* Fail quickly if the connection has already been aborted. */
@@ -431,7 +434,14 @@ apr_status_t ap_core_output_filter(ap_fi
* streaming out lots of data faster than the data can be
* sent to the client.)
*
- * 4) The brigade contains at least THRESHOLD_MIN_WRITE
+ * 4) The request is in CONN_STATE_HANDLER state, and the brigade
+ * contains at least MAX_REQUESTS_IN_PIPELINE EOR buckets:
+ * Do blocking writes until less than MAX_REQUESTS_IN_PIPELINE EOR
+ * buckets are left. (The point of this rule is to prevent too many
+ * FDs being kept open by pipelined requests, possibly allowing a
+ * DoS).
+ *
+ * 5) The brigade contains at least THRESHOLD_MIN_WRITE
* bytes: Do a nonblocking write of as much data as possible,
* then save the rest in ctx->buffered_bb.
*/
@@ -452,24 +462,12 @@ apr_status_t ap_core_output_filter(ap_fi
bytes_in_brigade = 0;
non_file_bytes_in_brigade = 0;
+ eor_buckets_in_brigade = 0;
for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb);
bucket = next) {
next = APR_BUCKET_NEXT(bucket);
- if (APR_BUCKET_IS_FLUSH(bucket)) {
- ctx->tmp_flush_bb = apr_brigade_split_ex(bb, next, ctx->tmp_flush_bb);
- rv = send_brigade_blocking(net->client_socket, bb,
- &(ctx->bytes_written), c);
- if (rv != APR_SUCCESS) {
- /* The client has aborted the connection */
- c->aborted = 1;
- return rv;
- }
- APR_BRIGADE_CONCAT(bb, ctx->tmp_flush_bb);
- next = APR_BRIGADE_FIRST(bb);
- bytes_in_brigade = 0;
- non_file_bytes_in_brigade = 0;
- }
- else if (!APR_BUCKET_IS_METADATA(bucket)) {
+
+ if (!APR_BUCKET_IS_METADATA(bucket)) {
if (bucket->length == (apr_size_t)-1) {
const char *data;
apr_size_t length;
@@ -486,12 +484,38 @@ apr_status_t ap_core_output_filter(ap_fi
non_file_bytes_in_brigade += bucket->length;
}
}
+ else if (AP_BUCKET_IS_EOR(bucket)) {
+ eor_buckets_in_brigade++;
+ }
+
+ if (APR_BUCKET_IS_FLUSH(bucket) ||
+ (non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER) ||
+ (eor_buckets_in_brigade > MAX_REQUESTS_IN_PIPELINE) )
+ {
+ if (APLOGctrace6(c)) {
+ char *reason = APR_BUCKET_IS_FLUSH(bucket) ?
+ "FLUSH bucket" :
+ (non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER) ?
+ "THRESHOLD_MAX_BUFFER" :
+ "MAX_REQUESTS_IN_PIPELINE";
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
+ "core_output_filter: flushing because of %s",
+ reason);
+ }
+ /*
+ * Defer the actual blocking write to avoid doing many writes.
+ */
+ flush_upto = next;
+
+ bytes_in_brigade = 0;
+ non_file_bytes_in_brigade = 0;
+ eor_buckets_in_brigade = 0;
+ }
}
- if (non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER) {
- /* ### Writing the entire brigade may be excessive; we really just
- * ### need to send enough data to be under THRESHOLD_MAX_BUFFER.
- */
+ if (flush_upto != NULL) {
+ ctx->tmp_flush_bb = apr_brigade_split_ex(bb, flush_upto,
+ ctx->tmp_flush_bb);
rv = send_brigade_blocking(net->client_socket, bb,
&(ctx->bytes_written), c);
if (rv != APR_SUCCESS) {
@@ -499,8 +523,10 @@ apr_status_t ap_core_output_filter(ap_fi
c->aborted = 1;
return rv;
}
+ APR_BRIGADE_CONCAT(bb, ctx->tmp_flush_bb);
}
- else if (bytes_in_brigade >= THRESHOLD_MIN_WRITE) {
+
+ if (bytes_in_brigade >= THRESHOLD_MIN_WRITE) {
rv = send_brigade_nonblocking(net->client_socket, bb,
&(ctx->bytes_written), c);
if ((rv != APR_SUCCESS) && (!APR_STATUS_IS_EAGAIN(rv))) {