📄 http_protocol.c
字号:
/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as * applicable. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *//* * http_protocol.c --- routines which directly communicate with the client. * * Code originally by Rob McCool; much redone by Robert S. Thau * and the Apache Software Foundation. */#include "apr.h"#include "apr_strings.h"#include "apr_buckets.h"#include "apr_lib.h"#include "apr_signal.h"#define APR_WANT_STDIO /* for sscanf */#define APR_WANT_STRFUNC#define APR_WANT_MEMFUNC#include "apr_want.h"#define CORE_PRIVATE#include "util_filter.h"#include "ap_config.h"#include "httpd.h"#include "http_config.h"#include "http_core.h"#include "http_protocol.h"#include "http_main.h"#include "http_request.h"#include "http_vhost.h"#include "http_log.h" /* For errors detected in basic auth common * support code... */#include "apr_date.h" /* For apr_date_parse_http and APR_DATE_BAD */#include "util_charset.h"#include "util_ebcdic.h"#include "util_time.h"#include "mod_core.h"#if APR_HAVE_STDARG_H#include <stdarg.h>#endif#if APR_HAVE_UNISTD_H#include <unistd.h>#endif/* New Apache routine to map status codes into array indicies * e.g. 100 -> 0, 101 -> 1, 200 -> 2 ... * The number of status lines must equal the value of RESPONSE_CODES (httpd.h) * and must be listed in order. */#ifdef UTS21/* The second const triggers an assembler bug on UTS 2.1. * Another workaround is to move some code out of this file into another, * but this is easier. Dave Dykstra, 3/31/99 */static const char * status_lines[RESPONSE_CODES] =#elsestatic const char * const status_lines[RESPONSE_CODES] =#endif{ "100 Continue", "101 Switching Protocols", "102 Processing",#define LEVEL_200 3 "200 OK", "201 Created", "202 Accepted", "203 Non-Authoritative Information", "204 No Content", "205 Reset Content", "206 Partial Content", "207 Multi-Status",#define LEVEL_300 11 "300 Multiple Choices", "301 Moved Permanently", "302 Found", "303 See Other", "304 Not Modified", "305 Use Proxy", "306 unused", "307 Temporary Redirect",#define LEVEL_400 19 "400 Bad Request", "401 Authorization Required", "402 Payment Required", "403 Forbidden", "404 Not Found", "405 Method Not Allowed", "406 Not Acceptable", "407 Proxy Authentication Required", "408 Request Time-out", "409 Conflict", "410 Gone", "411 Length Required", "412 Precondition Failed", "413 Request Entity Too Large", "414 Request-URI Too Large", "415 Unsupported Media Type", "416 Requested Range Not Satisfiable", "417 Expectation Failed", "418 unused", "419 unused", "420 unused", "421 unused", "422 Unprocessable Entity", "423 Locked", "424 Failed Dependency", /* This is a hack, but it is required for ap_index_of_response * to work with 426. */ "425 No code", "426 Upgrade Required",#define LEVEL_500 46 "500 Internal Server Error", "501 Method Not Implemented", "502 Bad Gateway", "503 Service Temporarily Unavailable", "504 Gateway Time-out", "505 HTTP Version Not Supported", "506 Variant Also Negotiates", "507 Insufficient Storage", "508 unused", "509 unused", "510 Not Extended"};APR_HOOK_STRUCT( APR_HOOK_LINK(insert_error_filter))AP_IMPLEMENT_HOOK_VOID(insert_error_filter, (request_rec *r), (r))/* The index of the first bit field that is used to index into a limit * bitmask. M_INVALID + 1 to METHOD_NUMBER_LAST. */#define METHOD_NUMBER_FIRST (M_INVALID + 1)/* The max method number. Method numbers are used to shift bitmasks, * so this cannot exceed 63, and all bits high is equal to -1, which is a * special flag, so the last bit used has index 62. */#define METHOD_NUMBER_LAST 62AP_DECLARE(int) ap_set_keepalive(request_rec *r){ int ka_sent = 0; int wimpy = ap_find_token(r->pool, apr_table_get(r->headers_out, "Connection"), "close"); const char *conn = apr_table_get(r->headers_in, "Connection"); /* The following convoluted conditional determines whether or not * the current connection should remain persistent after this response * (a.k.a. HTTP Keep-Alive) and whether or not the output message * body should use the HTTP/1.1 chunked transfer-coding. In English, * * IF we have not marked this connection as errored; * and the response body has a defined length due to the status code * being 304 or 204, the request method being HEAD, already * having defined Content-Length or Transfer-Encoding: chunked, or * the request version being HTTP/1.1 and thus capable of being set * as chunked [we know the (r->chunked = 1) side-effect is ugly]; * and the server configuration enables keep-alive; * and the server configuration has a reasonable inter-request timeout; * and there is no maximum # requests or the max hasn't been reached; * and the response status does not require a close; * and the response generator has not already indicated close; * and the client did not request non-persistence (Connection: close); * and we haven't been configured to ignore the buggy twit * or they're a buggy twit coming through a HTTP/1.1 proxy * and the client is requesting an HTTP/1.0-style keep-alive * or the client claims to be HTTP/1.1 compliant (perhaps a proxy); * THEN we can be persistent, which requires more headers be output. * * Note that the condition evaluation order is extremely important. */ if ((r->connection->keepalive != AP_CONN_CLOSE) && ((r->status == HTTP_NOT_MODIFIED) || (r->status == HTTP_NO_CONTENT) || r->header_only || apr_table_get(r->headers_out, "Content-Length") || ap_find_last_token(r->pool, apr_table_get(r->headers_out, "Transfer-Encoding"), "chunked") || ((r->proto_num >= HTTP_VERSION(1,1)) && (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */ && r->server->keep_alive && (r->server->keep_alive_timeout > 0) && ((r->server->keep_alive_max == 0) || (r->server->keep_alive_max > r->connection->keepalives)) && !ap_status_drops_connection(r->status) && !wimpy && !ap_find_token(r->pool, conn, "close") && (!apr_table_get(r->subprocess_env, "nokeepalive") || apr_table_get(r->headers_in, "Via")) && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive")) || (r->proto_num >= HTTP_VERSION(1,1)))) { int left = r->server->keep_alive_max - r->connection->keepalives; r->connection->keepalive = AP_CONN_KEEPALIVE; r->connection->keepalives++; /* If they sent a Keep-Alive token, send one back */ if (ka_sent) { if (r->server->keep_alive_max) { apr_table_setn(r->headers_out, "Keep-Alive", apr_psprintf(r->pool, "timeout=%d, max=%d", (int)apr_time_sec(r->server->keep_alive_timeout), left)); } else { apr_table_setn(r->headers_out, "Keep-Alive", apr_psprintf(r->pool, "timeout=%d", (int)apr_time_sec(r->server->keep_alive_timeout))); } apr_table_mergen(r->headers_out, "Connection", "Keep-Alive"); } return 1; } /* Otherwise, we need to indicate that we will be closing this * connection immediately after the current response. * * We only really need to send "close" to HTTP/1.1 clients, but we * always send it anyway, because a broken proxy may identify itself * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag * to a HTTP/1.1 client. Better safe than sorry. */ if (!wimpy) { apr_table_mergen(r->headers_out, "Connection", "close"); } r->connection->keepalive = AP_CONN_CLOSE; return 0;}AP_DECLARE(int) ap_meets_conditions(request_rec *r){ const char *etag; const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch; apr_time_t tmp_time; apr_int64_t mtime; /* Check for conditional requests --- note that we only want to do * this if we are successful so far and we are not processing a * subrequest or an ErrorDocument. * * The order of the checks is important, since ETag checks are supposed * to be more accurate than checks relative to the modification time. * However, not all documents are guaranteed to *have* ETags, and some * might have Last-Modified values w/o ETags, so this gets a little * complicated. */ if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) { return OK; } etag = apr_table_get(r->headers_out, "ETag"); /* All of our comparisons must be in seconds, because that's the * highest time resolution the HTTP specification allows. */ /* XXX: we should define a "time unset" constant */ tmp_time = ((r->mtime != 0) ? r->mtime : apr_time_now()); mtime = apr_time_sec(tmp_time); /* If an If-Match request-header field was given * AND the field value is not "*" (meaning match anything) * AND if our strong ETag does not match any entity tag in that field, * respond with a status of 412 (Precondition Failed). */ if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) { if (if_match[0] != '*' && (etag == NULL || etag[0] == 'W' || !ap_find_list_item(r->pool, if_match, etag))) { return HTTP_PRECONDITION_FAILED; } } else { /* Else if a valid If-Unmodified-Since request-header field was given * AND the requested resource has been modified since the time * specified in this field, then the server MUST * respond with a status of 412 (Precondition Failed). */ if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since"); if (if_unmodified != NULL) { apr_time_t ius = apr_date_parse_http(if_unmodified); if ((ius != APR_DATE_BAD) && (mtime > apr_time_sec(ius))) { return HTTP_PRECONDITION_FAILED; } } } /* If an If-None-Match request-header field was given * AND the field value is "*" (meaning match anything) * OR our ETag matches any of the entity tags in that field, fail. * * If the request method was GET or HEAD, failure means the server * SHOULD respond with a 304 (Not Modified) response. * For all other request methods, failure means the server MUST * respond with a status of 412 (Precondition Failed). * * GET or HEAD allow weak etag comparison, all other methods require * strong comparison. We can only use weak if it's not a range request. */ if_nonematch = apr_table_get(r->headers_in, "If-None-Match"); if (if_nonematch != NULL) { if (r->method_number == M_GET) { if (if_nonematch[0] == '*') { return HTTP_NOT_MODIFIED; } if (etag != NULL) { if (apr_table_get(r->headers_in, "Range")) { if (etag[0] != 'W' && ap_find_list_item(r->pool, if_nonematch, etag)) { return HTTP_NOT_MODIFIED; } } else if (ap_strstr_c(if_nonematch, etag)) { return HTTP_NOT_MODIFIED; } } } else if (if_nonematch[0] == '*' || (etag != NULL && ap_find_list_item(r->pool, if_nonematch, etag))) { return HTTP_PRECONDITION_FAILED; } } /* Else if a valid If-Modified-Since request-header field was given * AND it is a GET or HEAD request * AND the requested resource has not been modified since the time * specified in this field, then the server MUST * respond with a status of 304 (Not Modified). * A date later than the server's current request time is invalid. */ else if ((r->method_number == M_GET) && ((if_modified_since = apr_table_get(r->headers_in, "If-Modified-Since")) != NULL)) { apr_time_t ims_time; apr_int64_t ims, reqtime; ims_time = apr_date_parse_http(if_modified_since); ims = apr_time_sec(ims_time); reqtime = apr_time_sec(r->request_time); if ((ims >= mtime) && (ims <= reqtime)) { return HTTP_NOT_MODIFIED; } } return OK;}/** * Singleton registry of additional methods. This maps new method names * such as "MYGET" to methnums, which are int offsets into bitmasks. * * This follows the same technique as standard M_GET, M_POST, etc. These * are dynamically assigned when modules are loaded and <Limit GET MYGET> * directives are processed. */static apr_hash_t *methods_registry = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -