📄 http_filters.c
字号:
ctx->eos_sent = 1; return APR_SUCCESS; case BODY_CHUNK: case BODY_CHUNK_PART: { apr_brigade_cleanup(bb); /* We need to read the CRLF after the chunk. */ if (ctx->state == BODY_CHUNK) { rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, block, 0); if (block == APR_NONBLOCK_READ && ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) || (APR_STATUS_IS_EAGAIN(rv)) )) { return APR_EAGAIN; } /* If we get an error, then leave */ if (rv != APR_SUCCESS) { return rv; } /* * We really don't care whats on this line. If it is RFC * compliant it should be only \r\n. If there is more * before we just ignore it as long as we do not get over * the limit for request lines. */ rv = get_remaining_chunk_line(ctx, bb, f->r->server->limit_req_line); apr_brigade_cleanup(bb); if (APR_STATUS_IS_EAGAIN(rv)) { return rv; } } else { rv = APR_SUCCESS; } if (rv == APR_SUCCESS) { /* Read the real chunk line. */ rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE, block, 0); /* Test timeout */ if (block == APR_NONBLOCK_READ && ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) || (APR_STATUS_IS_EAGAIN(rv)) )) { ctx->state = BODY_CHUNK_PART; return APR_EAGAIN; } ctx->state = BODY_CHUNK; if (rv == APR_SUCCESS) { rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line); if (APR_STATUS_IS_EAGAIN(rv)) { ctx->state = BODY_CHUNK_PART; apr_brigade_cleanup(bb); return rv; } if (rv == APR_SUCCESS) { ctx->remaining = get_chunk_size(ctx->chunk_ln); if (ctx->remaining == INVALID_CHAR) { rv = APR_EGENERAL; http_error = HTTP_SERVICE_UNAVAILABLE; } } } apr_brigade_cleanup(bb); } /* Detect chunksize error (such as overflow) */ if (rv != APR_SUCCESS || ctx->remaining < 0) { ctx->remaining = 0; /* Reset it in case we have to * come back here later */ bail_out_on_error(ctx, f, http_error); return rv; } if (!ctx->remaining) { /* Handle trailers by calling ap_get_mime_headers again! */ ctx->state = BODY_NONE; ap_get_mime_headers(f->r); e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); ctx->eos_sent = 1; return APR_SUCCESS; } } break; } } /* Ensure that the caller can not go over our boundary point. */ if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK) { if (ctx->remaining < readbytes) { readbytes = ctx->remaining; } AP_DEBUG_ASSERT(readbytes > 0); } rv = ap_get_brigade(f->next, b, mode, block, readbytes); if (rv != APR_SUCCESS) { return rv; } /* How many bytes did we just read? */ apr_brigade_length(b, 0, &totalread); /* If this happens, we have a bucket of unknown length. Die because * it means our assumptions have changed. */ AP_DEBUG_ASSERT(totalread >= 0); if (ctx->state != BODY_NONE) { ctx->remaining -= totalread; } /* If we have no more bytes remaining on a C-L request, * save the callter a roundtrip to discover EOS. */ if (ctx->state == BODY_LENGTH && ctx->remaining == 0) { e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(b, e); } /* We have a limit in effect. */ if (ctx->limit) { /* FIXME: Note that we might get slightly confused on chunked inputs * as we'd need to compensate for the chunk lengths which may not * really count. This seems to be up for interpretation. */ ctx->limit_used += totalread; if (ctx->limit < ctx->limit_used) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, "Read content-length of %" APR_OFF_T_FMT " is larger than the configured limit" " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit); apr_brigade_cleanup(bb); e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL, f->r->pool, f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); e = apr_bucket_eos_create(f->c->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, e); ctx->eos_sent = 1; return ap_pass_brigade(f->r->output_filters, bb); } } return APR_SUCCESS;}/** * Parse a chunk extension, detect overflow. * There are two error cases: * 1) If the conversion would require too many bits, a -1 is returned. * 2) If the conversion used the correct number of bits, but an overflow * caused only the sign bit to flip, then that negative number is * returned. * In general, any negative number can be considered an overflow error. */static long get_chunk_size(char *b){ long chunksize = 0; size_t chunkbits = sizeof(long) * 8; ap_xlate_proto_from_ascii(b, strlen(b)); if (!apr_isxdigit(*b)) { /* * Detect invalid character at beginning. This also works for empty * chunk size lines. */ return INVALID_CHAR; } /* Skip leading zeros */ while (*b == '0') { ++b; } while (apr_isxdigit(*b) && (chunkbits > 0)) { int xvalue = 0; if (*b >= '0' && *b <= '9') { xvalue = *b - '0'; } else if (*b >= 'A' && *b <= 'F') { xvalue = *b - 'A' + 0xa; } else if (*b >= 'a' && *b <= 'f') { xvalue = *b - 'a' + 0xa; } chunksize = (chunksize << 4) | xvalue; chunkbits -= 4; ++b; } if (apr_isxdigit(*b) && (chunkbits <= 0)) { /* overflow */ return -1; } return chunksize;}typedef struct header_struct { apr_pool_t *pool; apr_bucket_brigade *bb;} header_struct;/* Send a single HTTP header field to the client. Note that this function * is used in calls to table_do(), so their interfaces are co-dependent. * In other words, don't change this one without checking table_do in alloc.c. * It returns true unless there was a write error of some kind. */static int form_header_field(header_struct *h, const char *fieldname, const char *fieldval){#if APR_CHARSET_EBCDIC char *headfield; apr_size_t len; apr_size_t name_len; apr_size_t val_len; char *next; name_len = strlen(fieldname); val_len = strlen(fieldval); len = name_len + val_len + 4; /* 4 for ": " plus CRLF */ headfield = (char *)apr_palloc(h->pool, len + 1); memcpy(headfield, fieldname, name_len); next = headfield + name_len; *next++ = ':'; *next++ = ' '; memcpy(next, fieldval, val_len); next += val_len; *next++ = CR; *next++ = LF; *next = 0; ap_xlate_proto_to_ascii(headfield, len); apr_brigade_write(h->bb, NULL, NULL, headfield, len);#else struct iovec vec[4]; struct iovec *v = vec; v->iov_base = (void *)fieldname; v->iov_len = strlen(fieldname); v++; v->iov_base = ": "; v->iov_len = sizeof(": ") - 1; v++; v->iov_base = (void *)fieldval; v->iov_len = strlen(fieldval); v++; v->iov_base = CRLF; v->iov_len = sizeof(CRLF) - 1; apr_brigade_writev(h->bb, NULL, NULL, vec, 4);#endif /* !APR_CHARSET_EBCDIC */ return 1;}/* This routine is called by apr_table_do and merges all instances of * the passed field values into a single array that will be further * processed by some later routine. Originally intended to help split * and recombine multiple Vary fields, though it is generic to any field * consisting of comma/space-separated tokens. */static int uniq_field_values(void *d, const char *key, const char *val){ apr_array_header_t *values; char *start; char *e; char **strpp; int i; values = (apr_array_header_t *)d; e = apr_pstrdup(values->pool, val); do { /* Find a non-empty fieldname */ while (*e == ',' || apr_isspace(*e)) { ++e; } if (*e == '\0') { break; } start = e; while (*e != '\0' && *e != ',' && !apr_isspace(*e)) { ++e; } if (*e != '\0') { *e++ = '\0'; } /* Now add it to values if it isn't already represented. * Could be replaced by a ap_array_strcasecmp() if we had one. */ for (i = 0, strpp = (char **) values->elts; i < values->nelts; ++i, ++strpp) { if (*strpp && strcasecmp(*strpp, start) == 0) { break; } } if (i == values->nelts) { /* if not found */ *(char **)apr_array_push(values) = start; } } while (*e != '\0'); return 1;}/* * Since some clients choke violently on multiple Vary fields, or * Vary fields with duplicate tokens, combine any multiples and remove * any duplicates. */static void fixup_vary(request_rec *r){ apr_array_header_t *varies; varies = apr_array_make(r->pool, 5, sizeof(char *)); /* Extract all Vary fields from the headers_out, separate each into * its comma-separated fieldname values, and then add them to varies * if not already present in the array. */ apr_table_do((int (*)(void *, const char *, const char *))uniq_field_values, (void *) varies, r->headers_out, "Vary", NULL); /* If we found any, replace old Vary fields with unique-ified value */ if (varies->nelts > 0) { apr_table_setn(r->headers_out, "Vary", apr_array_pstrcat(r->pool, varies, ',')); }}/* Send a request's HTTP response headers to the client. */static apr_status_t send_all_header_fields(header_struct *h, const request_rec *r){ const apr_array_header_t *elts; const apr_table_entry_t *t_elt; const apr_table_entry_t *t_end; struct iovec *vec; struct iovec *vec_next; elts = apr_table_elts(r->headers_out); if (elts->nelts == 0) { return APR_SUCCESS; } t_elt = (const apr_table_entry_t *)(elts->elts); t_end = t_elt + elts->nelts; vec = (struct iovec *)apr_palloc(h->pool, 4 * elts->nelts * sizeof(struct iovec)); vec_next = vec; /* For each field, generate * name ": " value CRLF */ do { vec_next->iov_base = (void*)(t_elt->key); vec_next->iov_len = strlen(t_elt->key); vec_next++; vec_next->iov_base = ": "; vec_next->iov_len = sizeof(": ") - 1; vec_next++; vec_next->iov_base = (void*)(t_elt->val); vec_next->iov_len = strlen(t_elt->val); vec_next++; vec_next->iov_base = CRLF; vec_next->iov_len = sizeof(CRLF) - 1; vec_next++; t_elt++; } while (t_elt < t_end);#if APR_CHARSET_EBCDIC { apr_size_t len; char *tmp = apr_pstrcatv(r->pool, vec, vec_next - vec, &len); ap_xlate_proto_to_ascii(tmp, len); return apr_brigade_write(h->bb, NULL, NULL, tmp, len); }#else return apr_brigade_writev(h->bb, NULL, NULL, vec, vec_next - vec);#endif}/* Confirm that the status line is well-formed and matches r->status. * If they don't match, a filter may have negated the status line set by a * handler. * Zap r->status_line if bad. */static void validate_status_line(request_rec *r){ char *end; if (r->status_line && (strlen(r->status_line) <= 4 || apr_strtoi64(r->status_line, &end, 10) != r->status || *end != ' ' || (end - 3) != r->status_line)) { r->status_line = NULL; }}/* * Determine the protocol to use for the response. Potentially downgrade * to HTTP/1.0 in some situations and/or turn off keepalives. * * also prepare r->status_line. */static void basic_http_header_check(request_rec *r, const char **protocol){ if (r->assbackwards) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -