📄 http_protocol.c
字号:
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}/* * 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) { /* no such thing as a response protocol */ return; } if (!r->status_line) { r->status_line = status_lines[ap_index_of_response(r->status)]; } /* Note that we must downgrade before checking for force responses. */ if (r->proto_num > HTTP_VERSION(1,0) && apr_table_get(r->subprocess_env, "downgrade-1.0")) { r->proto_num = HTTP_VERSION(1,0); } /* kludge around broken browsers when indicated by force-response-1.0 */ if (r->proto_num == HTTP_VERSION(1,0) && apr_table_get(r->subprocess_env, "force-response-1.0")) { *protocol = "HTTP/1.0"; r->connection->keepalive = AP_CONN_CLOSE; } else { *protocol = AP_SERVER_PROTOCOL; }}/* fill "bb" with a barebones/initial HTTP response header */static void basic_http_header(request_rec *r, apr_bucket_brigade *bb, const char *protocol){ char *date; const char *server; header_struct h; struct iovec vec[4]; if (r->assbackwards) { /* there are no headers to send */ return; } /* Output the HTTP/1.x Status-Line and the Date and Server fields */ vec[0].iov_base = (void *)protocol; vec[0].iov_len = strlen(protocol); vec[1].iov_base = (void *)" "; vec[1].iov_len = sizeof(" ") - 1; vec[2].iov_base = (void *)(r->status_line); vec[2].iov_len = strlen(r->status_line); vec[3].iov_base = (void *)CRLF; vec[3].iov_len = sizeof(CRLF) - 1;#if APR_CHARSET_EBCDIC { char *tmp; apr_size_t len; tmp = apr_pstrcatv(r->pool, vec, 4, &len); ap_xlate_proto_to_ascii(tmp, len); apr_brigade_write(bb, NULL, NULL, tmp, len); }#else apr_brigade_writev(bb, NULL, NULL, vec, 4);#endif date = apr_palloc(r->pool, APR_RFC822_DATE_LEN); ap_recent_rfc822_date(date, r->request_time); h.pool = r->pool; h.bb = bb; form_header_field(&h, "Date", date); /* keep the set-by-proxy server header, otherwise * generate a new server header */ if (r->proxyreq != PROXYREQ_NONE) { server = apr_table_get(r->headers_out, "Server"); if (server) { form_header_field(&h, "Server", server); } } else { form_header_field(&h, "Server", ap_get_server_version()); } /* unset so we don't send them again */ apr_table_unset(r->headers_out, "Date"); /* Avoid bogosity */ apr_table_unset(r->headers_out, "Server");}AP_DECLARE(void) ap_basic_http_header(request_rec *r, apr_bucket_brigade *bb){ const char *protocol; basic_http_header_check(r, &protocol); basic_http_header(r, bb, protocol);}/* Navigator versions 2.x, 3.x and 4.0 betas up to and including 4.0b2 * have a header parsing bug. If the terminating \r\n occur starting * at offset 256, 257 or 258 of output then it will not properly parse * the headers. Curiously it doesn't exhibit this problem at 512, 513. * We are guessing that this is because their initial read of a new request * uses a 256 byte buffer, and subsequent reads use a larger buffer. * So the problem might exist at different offsets as well. * * This should also work on keepalive connections assuming they use the * same small buffer for the first read of each new request. * * At any rate, we check the bytes written so far and, if we are about to * tickle the bug, we instead insert a bogus padding header. Since the bug * manifests as a broken image in Navigator, users blame the server. :( * It is more expensive to check the User-Agent than it is to just add the * bytes, so we haven't used the BrowserMatch feature here. */static void terminate_header(apr_bucket_brigade *bb){ char tmp[] = "X-Pad: avoid browser bug" CRLF; char crlf[] = CRLF; apr_off_t len; apr_size_t buflen; (void) apr_brigade_length(bb, 1, &len); if (len >= 255 && len <= 257) { buflen = strlen(tmp); ap_xlate_proto_to_ascii(tmp, buflen); apr_brigade_write(bb, NULL, NULL, tmp, buflen); } buflen = strlen(crlf); ap_xlate_proto_to_ascii(crlf, buflen); apr_brigade_write(bb, NULL, NULL, crlf, buflen);}/* Build the Allow field-value from the request handler method mask. * Note that we always allow TRACE, since it is handled below. */static char *make_allow(request_rec *r){ char *list; apr_int64_t mask; apr_array_header_t *allow = apr_array_make(r->pool, 10, sizeof(char *)); apr_hash_index_t *hi = apr_hash_first(r->pool, methods_registry); mask = r->allowed_methods->method_mask; for (; hi; hi = apr_hash_next(hi)) { const void *key; void *val; apr_hash_this(hi, &key, NULL, &val); if ((mask & (AP_METHOD_BIT << *(int *)val)) != 0) { *(const char **)apr_array_push(allow) = key; /* the M_GET method actually refers to two methods */ if (*(int *)val == M_GET) *(const char **)apr_array_push(allow) = "HEAD"; } } /* TRACE is always allowed */ *(const char **)apr_array_push(allow) = "TRACE"; list = apr_array_pstrcat(r->pool, allow, ','); /* ### this is rather annoying. we should enforce registration of ### these methods */ if ((mask & (AP_METHOD_BIT << M_INVALID)) && (r->allowed_methods->method_list != NULL) && (r->allowed_methods->method_list->nelts != 0)) { int i; char **xmethod = (char **) r->allowed_methods->method_list->elts; /* * Append all of the elements of r->allowed_methods->method_list */ for (i = 0; i < r->allowed_methods->method_list->nelts; ++i) { list = apr_pstrcat(r->pool, list, ",", xmethod[i], NULL); } } return list;}AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r){ int rv; apr_bucket_brigade *b; header_struct h; if (r->method_number != M_TRACE) { return DECLINED; } /* Get the original request */ while (r->prev) { r = r->prev; } if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY))) { return rv; } ap_set_content_type(r, "message/http"); /* Now we recreate the request, and echo it back */ b = apr_brigade_create(r->pool, r->connection->bucket_alloc); apr_brigade_putstrs(b, NULL, NULL, r->the_request, CRLF, NULL); h.pool = r->pool; h.bb = b; apr_table_do((int (*) (void *, const char *, const char *)) form_header_field, (void *) &h, r->headers_in, NULL); apr_brigade_puts(b, NULL, NULL, CRLF); ap_pass_brigade(r->output_filters, b); return DONE;}AP_DECLARE(int) ap_send_http_options(request_rec *r){ if (r->assbackwards) { return DECLINED; } apr_table_setn(r->headers_out, "Allow", make_allow(r)); /* the request finalization will send an EOS, which will flush all * the headers out (including the Allow header) */ return OK;}/* 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, ',')); }}AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct){ if (!ct) { r->content_type = NULL; } else if (!r->content_type || strcmp(r->content_type, ct)) { r->content_type = ct; /* Insert filters requested by the AddOutputFiltersByType * configuration directive. Content-type filters must be * inserted after the content handlers have run because * only then, do we reliably know the content-type. */ ap_add_output_filters_by_type(r); }}typedef struct header_filter_ctx { int headers_sent;} header_filter_ctx;AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f, apr_bucket_brigade *b){ request_rec *r = f->r; conn_rec *c = r->connection; const char *clheader; const char *protocol; apr_bucket *e; apr_bucket_brigade *b2; header_struct h; header_filter_ctx *ctx = f->ctx; AP_DEBUG_ASSERT(!r->main); if (r->header_only) { if (!ctx) { ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx)); } else if (ctx->headers_sent) { apr_brigade_destroy(b); return OK; } } APR_BRIGADE_FOREACH(e, b) { if (e->type == &ap_bucket_type_error) { ap_bucket_error *eb = e->data;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -