diff options
author | Stefan Fritsch <sf@sfritsch.de> | 2011-12-27 19:43:15 +0100 |
---|---|---|
committer | Stefan Fritsch <sf@sfritsch.de> | 2011-12-27 19:43:15 +0100 |
commit | 14a509fc3b1f16381b86dc746807a063d8562149 (patch) | |
tree | c2ecb0bb1a142e344b12602edd9d775a1a86407a /modules | |
parent | 5b56d06a01a150fc9685e6f913774be3f9deb49f (diff) | |
download | apache2-14a509fc3b1f16381b86dc746807a063d8562149.tar.gz |
Upstream tarball 2.2.20upstream/2.2.20
Diffstat (limited to 'modules')
-rw-r--r-- | modules/aaa/mod_authnz_ldap.c | 6 | ||||
-rw-r--r-- | modules/filters/mod_deflate.c | 13 | ||||
-rw-r--r-- | modules/filters/mod_filter.c | 5 | ||||
-rw-r--r-- | modules/filters/mod_reqtimeout.c | 7 | ||||
-rw-r--r-- | modules/http/byterange_filter.c | 456 |
5 files changed, 344 insertions, 143 deletions
diff --git a/modules/aaa/mod_authnz_ldap.c b/modules/aaa/mod_authnz_ldap.c index 037bbcff..ce1af3db 100644 --- a/modules/aaa/mod_authnz_ldap.c +++ b/modules/aaa/mod_authnz_ldap.c @@ -465,6 +465,12 @@ start_over: : (LDAP_INSUFFICIENT_RIGHTS == result) ? AUTH_DENIED #endif #endif +#ifdef LDAP_CONSTRAINT_VIOLATION + /* At least Sun Directory Server sends this if a user is + * locked. This is not covered by LDAP_SECURITY_ERROR. + */ + : (LDAP_CONSTRAINT_VIOLATION == result) ? AUTH_DENIED +#endif : AUTH_GENERAL_ERROR; } diff --git a/modules/filters/mod_deflate.c b/modules/filters/mod_deflate.c index 07ca194f..7856e35f 100644 --- a/modules/filters/mod_deflate.c +++ b/modules/filters/mod_deflate.c @@ -582,6 +582,19 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, apr_bucket *b; apr_size_t len; + /* + * Optimization: If we are a HEAD request and bytes_sent is not zero + * it means that we have passed the content-length filter once and + * have more data to sent. This means that the content-length filter + * could not determine our content-length for the response to the + * HEAD request anyway (the associated GET request would deliver the + * body in chunked encoding) and we can stop compressing. + */ + if (r->header_only && r->bytes_sent) { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + e = APR_BRIGADE_FIRST(bb); if (APR_BUCKET_IS_EOS(e)) { diff --git a/modules/filters/mod_filter.c b/modules/filters/mod_filter.c index c09f3f93..cbabd480 100644 --- a/modules/filters/mod_filter.c +++ b/modules/filters/mod_filter.c @@ -184,7 +184,12 @@ static int filter_lookup(ap_filter_t *f, ap_filter_rec_t *filter) str = apr_table_get(r->headers_in, provider->value); break; case RESPONSE_HEADERS: + /* Try r->headers_out first, fall back on err_headers_out. */ str = apr_table_get(r->headers_out, provider->value); + if (str) { + break; + } + str = apr_table_get(r->err_headers_out, provider->value); break; case SUBPROCESS_ENV: str = apr_table_get(r->subprocess_env, provider->value); diff --git a/modules/filters/mod_reqtimeout.c b/modules/filters/mod_reqtimeout.c index e0d79a5a..bc04e684 100644 --- a/modules/filters/mod_reqtimeout.c +++ b/modules/filters/mod_reqtimeout.c @@ -307,6 +307,13 @@ out: * 2s (SECONDS_TO_LINGER). */ apr_table_setn(f->c->notes, "short-lingering-close", "1"); + + /* + * Also, we must not allow keep-alive requests, as + * ap_finalize_protocol() may ignore our error status (if the timeout + * happened on a request body that is discarded). + */ + f->c->keepalive = AP_CONN_CLOSE; } return rv; } diff --git a/modules/http/byterange_filter.c b/modules/http/byterange_filter.c index e38d366e..6843df1b 100644 --- a/modules/http/byterange_filter.c +++ b/modules/http/byterange_filter.c @@ -55,65 +55,8 @@ #include <unistd.h> #endif -static int parse_byterange(char *range, apr_off_t clength, - apr_off_t *start, apr_off_t *end) -{ - char *dash = strchr(range, '-'); - char *errp; - apr_off_t number; - - if (!dash) { - return 0; - } - - if ((dash == range)) { - /* In the form "-5" */ - if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) { - return 0; - } - *start = clength - number; - *end = clength - 1; - } - else { - *dash++ = '\0'; - if (apr_strtoff(&number, range, &errp, 10) || *errp) { - return 0; - } - *start = number; - if (*dash) { - if (apr_strtoff(&number, dash, &errp, 10) || *errp) { - return 0; - } - *end = number; - } - else { /* "5-" */ - *end = clength - 1; - } - } - - if (*start < 0) { - *start = 0; - } - - if (*end >= clength) { - *end = clength - 1; - } - - if (*start > *end) { - return -1; - } - - return (*start > 0 || *end < clength); -} - -static int ap_set_byterange(request_rec *r); - -typedef struct byterange_ctx { - apr_bucket_brigade *bb; - int num_ranges; - char *boundary; - char *bound_head; -} byterange_ctx; +static int ap_set_byterange(request_rec *r, apr_off_t clength, + apr_array_header_t **indexes); /* * Here we try to be compatible with clients that want multipart/x-byteranges @@ -131,28 +74,200 @@ static int use_range_x(request_rec *r) } #define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT -#define PARTITION_ERR_FMT "apr_brigade_partition() failed " \ - "[%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]" + +static apr_status_t copy_brigade_range(apr_bucket_brigade *bb, + apr_bucket_brigade *bbout, + apr_off_t start, + apr_off_t end) +{ + apr_bucket *first = NULL, *last = NULL, *out_first = NULL, *e; + apr_uint64_t pos = 0, off_first = 0, off_last = 0; + apr_status_t rv; + const char *s; + apr_size_t len; + apr_uint64_t start64, end64; + apr_off_t pofft = 0; + + /* + * Once we know that start and end are >= 0 convert everything to apr_uint64_t. + * See the comments in apr_brigade_partition why. + * In short apr_off_t (for values >= 0)and apr_size_t fit into apr_uint64_t. + */ + start64 = (apr_uint64_t)start; + end64 = (apr_uint64_t)end; + + if (start < 0 || end < 0 || start64 > end64) + return APR_EINVAL; + + for (e = APR_BRIGADE_FIRST(bb); + e != APR_BRIGADE_SENTINEL(bb); + e = APR_BUCKET_NEXT(e)) + { + apr_uint64_t elen64; + /* we know that no bucket has undefined length (-1) */ + AP_DEBUG_ASSERT(e->length != (apr_size_t)(-1)); + elen64 = (apr_uint64_t)e->length; + if (!first && (elen64 + pos > start64)) { + first = e; + off_first = pos; + } + if (elen64 + pos > end64) { + last = e; + off_last = pos; + break; + } + pos += elen64; + } + if (!first || !last) + return APR_EINVAL; + + e = first; + while (1) + { + apr_bucket *copy; + AP_DEBUG_ASSERT(e != APR_BRIGADE_SENTINEL(bb)); + rv = apr_bucket_copy(e, ©); + if (rv != APR_SUCCESS) { + apr_brigade_cleanup(bbout); + return rv; + } + + APR_BRIGADE_INSERT_TAIL(bbout, copy); + if (e == first) { + if (off_first != start64) { + rv = apr_bucket_split(copy, (apr_size_t)(start64 - off_first)); + if (rv == APR_ENOTIMPL) { + rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + apr_brigade_cleanup(bbout); + return rv; + } + /* + * The read above might have morphed copy in a bucket + * of shorter length. So read and delete until we reached + * the correct bucket for splitting. + */ + while (start64 - off_first > (apr_uint64_t)copy->length) { + apr_bucket *tmp = APR_BUCKET_NEXT(copy); + off_first += (apr_uint64_t)copy->length; + APR_BUCKET_REMOVE(copy); + apr_bucket_destroy(copy); + copy = tmp; + rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + apr_brigade_cleanup(bbout); + return rv; + } + } + if (start64 > off_first) { + rv = apr_bucket_split(copy, (apr_size_t)(start64 - off_first)); + if (rv != APR_SUCCESS) { + apr_brigade_cleanup(bbout); + return rv; + } + } + else { + copy = APR_BUCKET_PREV(copy); + } + } + else if (rv != APR_SUCCESS) { + apr_brigade_cleanup(bbout); + return rv; + } + out_first = APR_BUCKET_NEXT(copy); + APR_BUCKET_REMOVE(copy); + apr_bucket_destroy(copy); + } + else { + out_first = copy; + } + } + if (e == last) { + if (e == first) { + off_last += start64 - off_first; + copy = out_first; + } + if (end64 - off_last != (apr_uint64_t)e->length) { + rv = apr_bucket_split(copy, (apr_size_t)(end64 + 1 - off_last)); + if (rv == APR_ENOTIMPL) { + rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + apr_brigade_cleanup(bbout); + return rv; + } + /* + * The read above might have morphed copy in a bucket + * of shorter length. So read until we reached + * the correct bucket for splitting. + */ + while (end64 + 1 - off_last > (apr_uint64_t)copy->length) { + off_last += (apr_uint64_t)copy->length; + copy = APR_BUCKET_NEXT(copy); + rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ); + if (rv != APR_SUCCESS) { + apr_brigade_cleanup(bbout); + return rv; + } + } + if (end64 < off_last + (apr_uint64_t)copy->length - 1) { + rv = apr_bucket_split(copy, end64 + 1 - off_last); + if (rv != APR_SUCCESS) { + apr_brigade_cleanup(bbout); + return rv; + } + } + } + else if (rv != APR_SUCCESS) { + apr_brigade_cleanup(bbout); + return rv; + } + copy = APR_BUCKET_NEXT(copy); + if (copy != APR_BRIGADE_SENTINEL(bbout)) { + APR_BUCKET_REMOVE(copy); + apr_bucket_destroy(copy); + } + } + break; + } + e = APR_BUCKET_NEXT(e); + } + + AP_DEBUG_ASSERT(APR_SUCCESS == apr_brigade_length(bbout, 1, &pofft)); + pos = (apr_uint64_t)pofft; + AP_DEBUG_ASSERT(pos == end64 - start64 + 1); + return APR_SUCCESS; +} + +typedef struct indexes_t { + apr_off_t start; + apr_off_t end; +} indexes_t; AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, apr_bucket_brigade *bb) { -#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1) request_rec *r = f->r; conn_rec *c = r->connection; - byterange_ctx *ctx; apr_bucket *e; apr_bucket_brigade *bsend; + apr_bucket_brigade *tmpbb; apr_off_t range_start; apr_off_t range_end; - char *current; apr_off_t clength = 0; apr_status_t rv; int found = 0; int num_ranges; - - /* Iterate through the brigade until reaching EOS or a bucket with - * unknown length. */ + char *boundary = NULL; + char *bound_head = NULL; + apr_array_header_t *indexes; + indexes_t *idx; + int original_status; + int i; + + /* + * Iterate through the brigade until reaching EOS or a bucket with + * unknown length. + */ for (e = APR_BRIGADE_FIRST(bb); (e != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(e) && e->length != (apr_size_t)-1); @@ -160,90 +275,80 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, clength += e->length; } - /* Don't attempt to do byte range work if this brigade doesn't + /* + * Don't attempt to do byte range work if this brigade doesn't * contain an EOS, or if any of the buckets has an unknown length; * this avoids the cases where it is expensive to perform - * byteranging (i.e. may require arbitrary amounts of memory). */ + * byteranging (i.e. may require arbitrary amounts of memory). + */ if (!APR_BUCKET_IS_EOS(e) || clength <= 0) { ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } - num_ranges = ap_set_byterange(r); + original_status = r->status; + num_ranges = ap_set_byterange(r, clength, &indexes); /* We have nothing to do, get out of the way. */ if (num_ranges == 0) { + r->status = original_status; ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } - ctx = apr_pcalloc(r->pool, sizeof(*ctx)); - ctx->num_ranges = num_ranges; - /* create a brigade in case we never call ap_save_brigade() */ - ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc); - - if (ctx->num_ranges > 1) { + if (num_ranges > 1) { /* Is ap_make_content_type required here? */ const char *orig_ct = ap_make_content_type(r, r->content_type); - ctx->boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx", - (apr_uint64_t)r->request_time, (long) getpid()); + boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx", + (apr_uint64_t)r->request_time, (long) getpid()); ap_set_content_type(r, apr_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/", "byteranges; boundary=", - ctx->boundary, NULL)); + boundary, NULL)); if (strcasecmp(orig_ct, NO_CONTENT_TYPE)) { - ctx->bound_head = apr_pstrcat(r->pool, - CRLF "--", ctx->boundary, - CRLF "Content-type: ", - orig_ct, - CRLF "Content-range: bytes ", - NULL); + bound_head = apr_pstrcat(r->pool, + CRLF "--", boundary, + CRLF "Content-type: ", + orig_ct, + CRLF "Content-range: bytes ", + NULL); } else { /* if we have no type for the content, do our best */ - ctx->bound_head = apr_pstrcat(r->pool, - CRLF "--", ctx->boundary, - CRLF "Content-range: bytes ", - NULL); + bound_head = apr_pstrcat(r->pool, + CRLF "--", boundary, + CRLF "Content-range: bytes ", + NULL); } - ap_xlate_proto_to_ascii(ctx->bound_head, strlen(ctx->bound_head)); + ap_xlate_proto_to_ascii(bound_head, strlen(bound_head)); } /* this brigade holds what we will be sending */ bsend = apr_brigade_create(r->pool, c->bucket_alloc); + tmpbb = apr_brigade_create(r->pool, c->bucket_alloc); - while ((current = ap_getword(r->pool, &r->range, ',')) - && (rv = parse_byterange(current, clength, &range_start, - &range_end))) { - apr_bucket *e2; - apr_bucket *ec; + idx = (indexes_t *)indexes->elts; + for (i = 0; i < indexes->nelts; i++, idx++) { + range_start = idx->start; + range_end = idx->end; - if (rv == -1) { - continue; - } - - /* These calls to apr_brigage_partition should only fail in - * pathological cases, e.g. a file being truncated whilst - * being served. */ - if ((rv = apr_brigade_partition(bb, range_start, &ec)) != APR_SUCCESS) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, - PARTITION_ERR_FMT, range_start, clength); - continue; - } - if ((rv = apr_brigade_partition(bb, range_end+1, &e2)) != APR_SUCCESS) { + rv = copy_brigade_range(bb, tmpbb, range_start, range_end); + if (rv != APR_SUCCESS ) { ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, - PARTITION_ERR_FMT, range_end+1, clength); + "copy_brigade_range() failed [%" APR_OFF_T_FMT + "-%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]", + range_start, range_end, clength); continue; } - found = 1; - /* For single range requests, we must produce Content-Range header. + /* + * For single range requests, we must produce Content-Range header. * Otherwise, we need to produce the multipart boundaries. */ - if (ctx->num_ranges == 1) { + if (num_ranges == 1) { apr_table_setn(r->headers_out, "Content-Range", apr_psprintf(r->pool, "bytes " BYTERANGE_FMT, range_start, range_end, clength)); @@ -251,7 +356,7 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, else { char *ts; - e = apr_bucket_pool_create(ctx->bound_head, strlen(ctx->bound_head), + e = apr_bucket_pool_create(bound_head, strlen(bound_head), r->pool, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bsend, e); @@ -263,23 +368,19 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, APR_BRIGADE_INSERT_TAIL(bsend, e); } - do { - apr_bucket *foo; - const char *str; - apr_size_t len; - - if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) { - /* As above; this should not fail since the bucket has - * a known length, but just to be sure, this takes - * care of uncopyable buckets that do somehow manage - * to slip through. */ - /* XXX: check for failure? */ - apr_bucket_read(ec, &str, &len, APR_BLOCK_READ); - apr_bucket_copy(ec, &foo); - } - APR_BRIGADE_INSERT_TAIL(bsend, foo); - ec = APR_BUCKET_NEXT(ec); - } while (ec != e2); + APR_BRIGADE_CONCAT(bsend, tmpbb); + if (i && !(i & 0x1F)) { + /* + * Every now and then, pass what we have down the filter chain. + * In this case, the content-length filter cannot calculate and + * set the content length and we must remove any Content-Length + * header already present. + */ + apr_table_unset(r->headers_out, "Content-Length"); + if ((rv = ap_pass_brigade(f->next, bsend)) != APR_SUCCESS) + return rv; + apr_brigade_cleanup(bsend); + } } if (found == 0) { @@ -294,11 +395,11 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, return ap_pass_brigade(f->next, bsend); } - if (ctx->num_ranges > 1) { + if (num_ranges > 1) { char *end; /* add the final boundary */ - end = apr_pstrcat(r->pool, CRLF "--", ctx->boundary, "--" CRLF, NULL); + end = apr_pstrcat(r->pool, CRLF "--", boundary, "--" CRLF, NULL); ap_xlate_proto_to_ascii(end, strlen(end)); e = apr_bucket_pool_create(end, strlen(end), r->pool, c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bsend, e); @@ -309,24 +410,32 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f, /* we're done with the original content - all of our data is in bsend. */ apr_brigade_cleanup(bb); + apr_brigade_destroy(tmpbb); /* send our multipart output */ return ap_pass_brigade(f->next, bsend); } -static int ap_set_byterange(request_rec *r) +static int ap_set_byterange(request_rec *r, apr_off_t clength, + apr_array_header_t **indexes) { const char *range; const char *if_range; const char *match; const char *ct; - int num_ranges; + char *cur; + int num_ranges = 0; + apr_off_t sum_lengths = 0; + indexes_t *idx; + int ranges = 1; + const char *it; if (r->assbackwards) { return 0; } - /* Check for Range request-header (HTTP/1.1) or Request-Range for + /* + * Check for Range request-header (HTTP/1.1) or Request-Range for * backwards-compatibility with second-draft Luotonen/Franks * byte-ranges (e.g. Netscape Navigator 2-3). * @@ -356,7 +465,8 @@ static int ap_set_byterange(request_rec *r) return 0; } - /* Check the If-Range header for Etag or Date. + /* + * Check the If-Range header for Etag or Date. * Note that this check will return false (as required) if either * of the two etags are weak. */ @@ -373,17 +483,77 @@ static int ap_set_byterange(request_rec *r) } } - if (!ap_strchr_c(range, ',')) { - /* a single range */ - num_ranges = 1; + range += 6; + it = range; + while (*it) { + if (*it++ == ',') { + ranges++; + } + } + it = range; + *indexes = apr_array_make(r->pool, ranges, sizeof(indexes_t)); + while ((cur = ap_getword(r->pool, &range, ','))) { + char *dash; + char *errp; + apr_off_t number, start, end; + + if (!(dash = strchr(cur, '-'))) { + break; + } + + if (dash == range) { + /* In the form "-5" */ + if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) { + break; + } + start = clength - number; + end = clength - 1; + } + else { + *dash++ = '\0'; + if (apr_strtoff(&number, cur, &errp, 10) || *errp) { + break; + } + start = number; + if (*dash) { + if (apr_strtoff(&number, dash, &errp, 10) || *errp) { + break; + } + end = number; + } + else { /* "5-" */ + end = clength - 1; + } + } + + if (start < 0) { + start = 0; + } + if (end >= clength) { + end = clength - 1; + } + + if (start > end) { + /* ignore? count? */ + break; + } + + idx = (indexes_t *)apr_array_push(*indexes); + idx->start = start; + idx->end = end; + sum_lengths += end - start + 1; + /* new set again */ + num_ranges++; } - else { - /* a multiple range */ - num_ranges = 2; + + if (sum_lengths >= clength) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "Sum of ranges not smaller than file, ignoring."); + return 0; } r->status = HTTP_PARTIAL_CONTENT; - r->range = range + 6; + r->range = it; return num_ranges; } |