📄 http_protocol.c
字号:
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}/* * 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); /* For TRACE below */ core_server_config *conf = ap_get_module_config(r->server->module_config, &core_module); 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 tested on a per-server basis */ if (conf->trace_enable != AP_TRACE_DISABLE) *(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){ core_server_config *conf; int rv; apr_bucket_brigade *bb; header_struct h; apr_bucket *b; int body; char *bodyread = NULL, *bodyoff; apr_size_t bodylen = 0; apr_size_t bodybuf; long res; if (r->method_number != M_TRACE) { return DECLINED; } /* Get the original request */ while (r->prev) { r = r->prev; } conf = (core_server_config *)ap_get_module_config(r->server->module_config, &core_module); if (conf->trace_enable == AP_TRACE_DISABLE) { apr_table_setn(r->notes, "error-notes", "TRACE denied by server configuration"); return HTTP_FORBIDDEN; } if (conf->trace_enable == AP_TRACE_EXTENDED) /* XX should be = REQUEST_CHUNKED_PASS */ body = REQUEST_CHUNKED_DECHUNK; else body = REQUEST_NO_BODY; if ((rv = ap_setup_client_block(r, body))) { if (rv == HTTP_REQUEST_ENTITY_TOO_LARGE) apr_table_setn(r->notes, "error-notes", "TRACE with a request body is not allowed"); return rv; } if (ap_should_client_block(r)) { if (r->remaining > 0) { if (r->remaining > 65536) { apr_table_setn(r->notes, "error-notes", "Extended TRACE request bodies cannot exceed 64k"); return HTTP_REQUEST_ENTITY_TOO_LARGE; } /* always 32 extra bytes to catch chunk header exceptions */ bodybuf = (apr_size_t)r->remaining + 32; } else { /* Add an extra 8192 for chunk headers */ bodybuf = 73730; } bodyoff = bodyread = apr_palloc(r->pool, bodybuf); /* only while we have enough for a chunked header */ while ((!bodylen || bodybuf >= 32) && (res = ap_get_client_block(r, bodyoff, bodybuf)) > 0) { bodylen += res; bodybuf -= res; bodyoff += res; } if (res > 0 && bodybuf < 32) { /* discard_rest_of_request_body into our buffer */ while (ap_get_client_block(r, bodyread, bodylen) > 0) ; apr_table_setn(r->notes, "error-notes", "Extended TRACE request bodies cannot exceed 64k"); return HTTP_REQUEST_ENTITY_TOO_LARGE; } if (res < 0) { return HTTP_BAD_REQUEST; } } ap_set_content_type(r, "message/http"); /* Now we recreate the request, and echo it back */ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); apr_brigade_putstrs(bb, NULL, NULL, r->the_request, CRLF, NULL); h.pool = r->pool; h.bb = bb; apr_table_do((int (*) (void *, const char *, const char *)) form_header_field, (void *) &h, r->headers_in, NULL); apr_brigade_puts(bb, NULL, NULL, CRLF); /* If configured to accept a body, echo the body */ if (bodylen) { b = apr_bucket_pool_create(bodyread, bodylen, r->pool, bb->bucket_alloc); APR_BRIGADE_INSERT_TAIL(bb, b); } ap_pass_brigade(r->output_filters, bb); 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; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -