|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* http_protocol.c --- routines which directly communicate with the client.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Code originally by Rob McCool; much redone by Robert S. Thau
|
|
Packit |
90a5c9 |
* and the Apache Software Foundation.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "apr.h"
|
|
Packit |
90a5c9 |
#include "apr_strings.h"
|
|
Packit |
90a5c9 |
#include "apr_buckets.h"
|
|
Packit |
90a5c9 |
#include "apr_lib.h"
|
|
Packit |
90a5c9 |
#include "apr_signal.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define APR_WANT_STDIO /* for sscanf */
|
|
Packit |
90a5c9 |
#define APR_WANT_STRFUNC
|
|
Packit |
90a5c9 |
#define APR_WANT_MEMFUNC
|
|
Packit |
90a5c9 |
#include "apr_want.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "util_filter.h"
|
|
Packit |
90a5c9 |
#include "ap_config.h"
|
|
Packit |
90a5c9 |
#include "httpd.h"
|
|
Packit |
90a5c9 |
#include "http_config.h"
|
|
Packit |
90a5c9 |
#include "http_core.h"
|
|
Packit |
90a5c9 |
#include "http_protocol.h"
|
|
Packit |
90a5c9 |
#include "http_main.h"
|
|
Packit |
90a5c9 |
#include "http_request.h"
|
|
Packit |
90a5c9 |
#include "http_vhost.h"
|
|
Packit |
90a5c9 |
#include "http_log.h" /* For errors detected in basic auth common
|
|
Packit |
90a5c9 |
* support code... */
|
|
Packit |
90a5c9 |
#include "apr_date.h" /* For apr_date_parse_http and APR_DATE_BAD */
|
|
Packit |
90a5c9 |
#include "util_charset.h"
|
|
Packit |
90a5c9 |
#include "util_ebcdic.h"
|
|
Packit |
90a5c9 |
#include "util_time.h"
|
|
Packit |
90a5c9 |
#include "ap_mpm.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "mod_core.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#if APR_HAVE_STDARG_H
|
|
Packit |
90a5c9 |
#include <stdarg.h>
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
#if APR_HAVE_UNISTD_H
|
|
Packit |
90a5c9 |
#include <unistd.h>
|
|
Packit |
90a5c9 |
#endif
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
APLOG_USE_MODULE(http);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* New Apache routine to map status codes into array indicies
|
|
Packit |
90a5c9 |
* e.g. 100 -> 0, 101 -> 1, 200 -> 2 ...
|
|
Packit |
90a5c9 |
* The number of status lines must equal the value of
|
|
Packit |
90a5c9 |
* RESPONSE_CODES (httpd.h) and must be listed in order.
|
|
Packit |
90a5c9 |
* No gaps are allowed between X00 and the largest Xnn
|
|
Packit |
90a5c9 |
* for any X (see ap_index_of_response).
|
|
Packit |
90a5c9 |
* When adding a new code here, add a define to httpd.h
|
|
Packit |
90a5c9 |
* as well.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static const char * const status_lines[RESPONSE_CODES] =
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
"100 Continue",
|
|
Packit |
90a5c9 |
"101 Switching Protocols",
|
|
Packit |
90a5c9 |
"102 Processing",
|
|
Packit |
90a5c9 |
#define LEVEL_200 3
|
|
Packit |
90a5c9 |
"200 OK",
|
|
Packit |
90a5c9 |
"201 Created",
|
|
Packit |
90a5c9 |
"202 Accepted",
|
|
Packit |
90a5c9 |
"203 Non-Authoritative Information",
|
|
Packit |
90a5c9 |
"204 No Content",
|
|
Packit |
90a5c9 |
"205 Reset Content",
|
|
Packit |
90a5c9 |
"206 Partial Content",
|
|
Packit |
90a5c9 |
"207 Multi-Status",
|
|
Packit |
90a5c9 |
"208 Already Reported",
|
|
Packit |
90a5c9 |
NULL, /* 209 */
|
|
Packit |
90a5c9 |
NULL, /* 210 */
|
|
Packit |
90a5c9 |
NULL, /* 211 */
|
|
Packit |
90a5c9 |
NULL, /* 212 */
|
|
Packit |
90a5c9 |
NULL, /* 213 */
|
|
Packit |
90a5c9 |
NULL, /* 214 */
|
|
Packit |
90a5c9 |
NULL, /* 215 */
|
|
Packit |
90a5c9 |
NULL, /* 216 */
|
|
Packit |
90a5c9 |
NULL, /* 217 */
|
|
Packit |
90a5c9 |
NULL, /* 218 */
|
|
Packit |
90a5c9 |
NULL, /* 219 */
|
|
Packit |
90a5c9 |
NULL, /* 220 */
|
|
Packit |
90a5c9 |
NULL, /* 221 */
|
|
Packit |
90a5c9 |
NULL, /* 222 */
|
|
Packit |
90a5c9 |
NULL, /* 223 */
|
|
Packit |
90a5c9 |
NULL, /* 224 */
|
|
Packit |
90a5c9 |
NULL, /* 225 */
|
|
Packit |
90a5c9 |
"226 IM Used",
|
|
Packit |
90a5c9 |
#define LEVEL_300 30
|
|
Packit |
90a5c9 |
"300 Multiple Choices",
|
|
Packit |
90a5c9 |
"301 Moved Permanently",
|
|
Packit |
90a5c9 |
"302 Found",
|
|
Packit |
90a5c9 |
"303 See Other",
|
|
Packit |
90a5c9 |
"304 Not Modified",
|
|
Packit |
90a5c9 |
"305 Use Proxy",
|
|
Packit |
90a5c9 |
NULL, /* 306 */
|
|
Packit |
90a5c9 |
"307 Temporary Redirect",
|
|
Packit |
90a5c9 |
"308 Permanent Redirect",
|
|
Packit |
90a5c9 |
#define LEVEL_400 39
|
|
Packit |
90a5c9 |
"400 Bad Request",
|
|
Packit |
90a5c9 |
"401 Unauthorized",
|
|
Packit |
90a5c9 |
"402 Payment Required",
|
|
Packit |
90a5c9 |
"403 Forbidden",
|
|
Packit |
90a5c9 |
"404 Not Found",
|
|
Packit |
90a5c9 |
"405 Method Not Allowed",
|
|
Packit |
90a5c9 |
"406 Not Acceptable",
|
|
Packit |
90a5c9 |
"407 Proxy Authentication Required",
|
|
Packit |
90a5c9 |
"408 Request Timeout",
|
|
Packit |
90a5c9 |
"409 Conflict",
|
|
Packit |
90a5c9 |
"410 Gone",
|
|
Packit |
90a5c9 |
"411 Length Required",
|
|
Packit |
90a5c9 |
"412 Precondition Failed",
|
|
Packit |
90a5c9 |
"413 Request Entity Too Large",
|
|
Packit |
90a5c9 |
"414 Request-URI Too Long",
|
|
Packit |
90a5c9 |
"415 Unsupported Media Type",
|
|
Packit |
90a5c9 |
"416 Requested Range Not Satisfiable",
|
|
Packit |
90a5c9 |
"417 Expectation Failed",
|
|
Packit |
90a5c9 |
NULL, /* 418 */
|
|
Packit |
90a5c9 |
NULL, /* 419 */
|
|
Packit |
90a5c9 |
NULL, /* 420 */
|
|
Packit |
90a5c9 |
"421 Misdirected Request",
|
|
Packit |
90a5c9 |
"422 Unprocessable Entity",
|
|
Packit |
90a5c9 |
"423 Locked",
|
|
Packit |
90a5c9 |
"424 Failed Dependency",
|
|
Packit |
90a5c9 |
NULL, /* 425 */
|
|
Packit |
90a5c9 |
"426 Upgrade Required",
|
|
Packit |
90a5c9 |
NULL, /* 427 */
|
|
Packit |
90a5c9 |
"428 Precondition Required",
|
|
Packit |
90a5c9 |
"429 Too Many Requests",
|
|
Packit |
90a5c9 |
NULL, /* 430 */
|
|
Packit |
90a5c9 |
"431 Request Header Fields Too Large",
|
|
Packit |
90a5c9 |
NULL, /* 432 */
|
|
Packit |
90a5c9 |
NULL, /* 433 */
|
|
Packit |
90a5c9 |
NULL, /* 434 */
|
|
Packit |
90a5c9 |
NULL, /* 435 */
|
|
Packit |
90a5c9 |
NULL, /* 436 */
|
|
Packit |
90a5c9 |
NULL, /* 437 */
|
|
Packit |
90a5c9 |
NULL, /* 438 */
|
|
Packit |
90a5c9 |
NULL, /* 439 */
|
|
Packit |
90a5c9 |
NULL, /* 440 */
|
|
Packit |
90a5c9 |
NULL, /* 441 */
|
|
Packit |
90a5c9 |
NULL, /* 442 */
|
|
Packit |
90a5c9 |
NULL, /* 443 */
|
|
Packit |
90a5c9 |
NULL, /* 444 */
|
|
Packit |
90a5c9 |
NULL, /* 445 */
|
|
Packit |
90a5c9 |
NULL, /* 446 */
|
|
Packit |
90a5c9 |
NULL, /* 447 */
|
|
Packit |
90a5c9 |
NULL, /* 448 */
|
|
Packit |
90a5c9 |
NULL, /* 449 */
|
|
Packit |
90a5c9 |
NULL, /* 450 */
|
|
Packit |
90a5c9 |
"451 Unavailable For Legal Reasons",
|
|
Packit |
90a5c9 |
#define LEVEL_500 91
|
|
Packit |
90a5c9 |
"500 Internal Server Error",
|
|
Packit |
90a5c9 |
"501 Not Implemented",
|
|
Packit |
90a5c9 |
"502 Bad Gateway",
|
|
Packit |
90a5c9 |
"503 Service Unavailable",
|
|
Packit |
90a5c9 |
"504 Gateway Timeout",
|
|
Packit |
90a5c9 |
"505 HTTP Version Not Supported",
|
|
Packit |
90a5c9 |
"506 Variant Also Negotiates",
|
|
Packit |
90a5c9 |
"507 Insufficient Storage",
|
|
Packit |
90a5c9 |
"508 Loop Detected",
|
|
Packit |
90a5c9 |
NULL, /* 509 */
|
|
Packit |
90a5c9 |
"510 Not Extended",
|
|
Packit |
90a5c9 |
"511 Network Authentication Required"
|
|
Packit |
90a5c9 |
};
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
APR_HOOK_STRUCT(
|
|
Packit |
90a5c9 |
APR_HOOK_LINK(insert_error_filter)
|
|
Packit |
90a5c9 |
)
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_IMPLEMENT_HOOK_VOID(insert_error_filter, (request_rec *r), (r))
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* The index of the first bit field that is used to index into a limit
|
|
Packit |
90a5c9 |
* bitmask. M_INVALID + 1 to METHOD_NUMBER_LAST.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
#define METHOD_NUMBER_FIRST (M_INVALID + 1)
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* The max method number. Method numbers are used to shift bitmasks,
|
|
Packit |
90a5c9 |
* so this cannot exceed 63, and all bits high is equal to -1, which is a
|
|
Packit |
90a5c9 |
* special flag, so the last bit used has index 62.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
#define METHOD_NUMBER_LAST 62
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int is_mpm_running(void)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int mpm_state = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (mpm_state == AP_MPMQ_STOPPING) {
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(int) ap_set_keepalive(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int ka_sent = 0;
|
|
Packit |
90a5c9 |
int left = r->server->keep_alive_max - r->connection->keepalives;
|
|
Packit |
90a5c9 |
int wimpy = ap_find_token(r->pool,
|
|
Packit |
90a5c9 |
apr_table_get(r->headers_out, "Connection"),
|
|
Packit |
90a5c9 |
"close");
|
|
Packit |
90a5c9 |
const char *conn = apr_table_get(r->headers_in, "Connection");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* The following convoluted conditional determines whether or not
|
|
Packit |
90a5c9 |
* the current connection should remain persistent after this response
|
|
Packit |
90a5c9 |
* (a.k.a. HTTP Keep-Alive) and whether or not the output message
|
|
Packit |
90a5c9 |
* body should use the HTTP/1.1 chunked transfer-coding. In English,
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* IF we have not marked this connection as errored;
|
|
Packit |
90a5c9 |
* and the client isn't expecting 100-continue (PR47087 - more
|
|
Packit |
90a5c9 |
* input here could be the client continuing when we're
|
|
Packit |
90a5c9 |
* closing the request).
|
|
Packit |
90a5c9 |
* and the response body has a defined length due to the status code
|
|
Packit |
90a5c9 |
* being 304 or 204, the request method being HEAD, already
|
|
Packit |
90a5c9 |
* having defined Content-Length or Transfer-Encoding: chunked, or
|
|
Packit |
90a5c9 |
* the request version being HTTP/1.1 and thus capable of being set
|
|
Packit |
90a5c9 |
* as chunked [we know the (r->chunked = 1) side-effect is ugly];
|
|
Packit |
90a5c9 |
* and the server configuration enables keep-alive;
|
|
Packit |
90a5c9 |
* and the server configuration has a reasonable inter-request timeout;
|
|
Packit |
90a5c9 |
* and there is no maximum # requests or the max hasn't been reached;
|
|
Packit |
90a5c9 |
* and the response status does not require a close;
|
|
Packit |
90a5c9 |
* and the response generator has not already indicated close;
|
|
Packit |
90a5c9 |
* and the client did not request non-persistence (Connection: close);
|
|
Packit |
90a5c9 |
* and we haven't been configured to ignore the buggy twit
|
|
Packit |
90a5c9 |
* or they're a buggy twit coming through a HTTP/1.1 proxy
|
|
Packit |
90a5c9 |
* and the client is requesting an HTTP/1.0-style keep-alive
|
|
Packit |
90a5c9 |
* or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
|
|
Packit |
90a5c9 |
* and this MPM process is not already exiting
|
|
Packit |
90a5c9 |
* THEN we can be persistent, which requires more headers be output.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Note that the condition evaluation order is extremely important.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if ((r->connection->keepalive != AP_CONN_CLOSE)
|
|
Packit |
90a5c9 |
&& !r->expecting_100
|
|
Packit |
90a5c9 |
&& (r->header_only
|
|
Packit |
90a5c9 |
|| AP_STATUS_IS_HEADER_ONLY(r->status)
|
|
Packit |
90a5c9 |
|| apr_table_get(r->headers_out, "Content-Length")
|
|
Packit |
90a5c9 |
|| ap_find_last_token(r->pool,
|
|
Packit |
90a5c9 |
apr_table_get(r->headers_out,
|
|
Packit |
90a5c9 |
"Transfer-Encoding"),
|
|
Packit |
90a5c9 |
"chunked")
|
|
Packit |
90a5c9 |
|| ((r->proto_num >= HTTP_VERSION(1,1))
|
|
Packit |
90a5c9 |
&& (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */
|
|
Packit |
90a5c9 |
&& r->server->keep_alive
|
|
Packit |
90a5c9 |
&& (r->server->keep_alive_timeout > 0)
|
|
Packit |
90a5c9 |
&& ((r->server->keep_alive_max == 0)
|
|
Packit |
90a5c9 |
|| (left > 0))
|
|
Packit |
90a5c9 |
&& !ap_status_drops_connection(r->status)
|
|
Packit |
90a5c9 |
&& !wimpy
|
|
Packit |
90a5c9 |
&& !ap_find_token(r->pool, conn, "close")
|
|
Packit |
90a5c9 |
&& (!apr_table_get(r->subprocess_env, "nokeepalive")
|
|
Packit |
90a5c9 |
|| apr_table_get(r->headers_in, "Via"))
|
|
Packit |
90a5c9 |
&& ((ka_sent = ap_find_token(r->pool, conn, "keep-alive"))
|
|
Packit |
90a5c9 |
|| (r->proto_num >= HTTP_VERSION(1,1)))
|
|
Packit |
90a5c9 |
&& is_mpm_running()) {
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->connection->keepalive = AP_CONN_KEEPALIVE;
|
|
Packit |
90a5c9 |
r->connection->keepalives++;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If they sent a Keep-Alive token, send one back */
|
|
Packit |
90a5c9 |
if (ka_sent) {
|
|
Packit |
90a5c9 |
if (r->server->keep_alive_max) {
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Keep-Alive",
|
|
Packit |
90a5c9 |
apr_psprintf(r->pool, "timeout=%d, max=%d",
|
|
Packit |
90a5c9 |
(int)apr_time_sec(r->server->keep_alive_timeout),
|
|
Packit |
90a5c9 |
left));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Keep-Alive",
|
|
Packit |
90a5c9 |
apr_psprintf(r->pool, "timeout=%d",
|
|
Packit |
90a5c9 |
(int)apr_time_sec(r->server->keep_alive_timeout)));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
apr_table_mergen(r->headers_out, "Connection", "Keep-Alive");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Otherwise, we need to indicate that we will be closing this
|
|
Packit |
90a5c9 |
* connection immediately after the current response.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* We only really need to send "close" to HTTP/1.1 clients, but we
|
|
Packit |
90a5c9 |
* always send it anyway, because a broken proxy may identify itself
|
|
Packit |
90a5c9 |
* as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
|
|
Packit |
90a5c9 |
* to a HTTP/1.1 client. Better safe than sorry.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (!wimpy) {
|
|
Packit |
90a5c9 |
apr_table_mergen(r->headers_out, "Connection", "close");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* If we had previously been a keepalive connection and this
|
|
Packit |
90a5c9 |
* is the last one, then bump up the number of keepalives
|
|
Packit |
90a5c9 |
* we've had
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if ((r->connection->keepalive != AP_CONN_CLOSE)
|
|
Packit |
90a5c9 |
&& r->server->keep_alive_max
|
|
Packit |
90a5c9 |
&& !left) {
|
|
Packit |
90a5c9 |
r->connection->keepalives++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
r->connection->keepalive = AP_CONN_CLOSE;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(ap_condition_e) ap_condition_if_match(request_rec *r,
|
|
Packit |
90a5c9 |
apr_table_t *headers)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *if_match, *etag;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* A server MUST use the strong comparison function (see section 13.3.3)
|
|
Packit |
90a5c9 |
* to compare the entity tags in If-Match.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
|
|
Packit |
90a5c9 |
if (if_match[0] == '*'
|
|
Packit |
90a5c9 |
|| ((etag = apr_table_get(headers, "ETag")) != NULL
|
|
Packit |
90a5c9 |
&& ap_find_etag_strong(r->pool, if_match, etag))) {
|
|
Packit |
90a5c9 |
return AP_CONDITION_STRONG;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AP_CONDITION_NOMATCH;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return AP_CONDITION_NONE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(ap_condition_e) ap_condition_if_unmodified_since(request_rec *r,
|
|
Packit |
90a5c9 |
apr_table_t *headers)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *if_unmodified;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
|
|
Packit |
90a5c9 |
if (if_unmodified) {
|
|
Packit |
90a5c9 |
apr_int64_t mtime, reqtime;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_time_t ius = apr_time_sec(apr_date_parse_http(if_unmodified));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* All of our comparisons must be in seconds, because that's the
|
|
Packit |
90a5c9 |
* highest time resolution the HTTP specification allows.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
mtime = apr_time_sec(apr_date_parse_http(
|
|
Packit |
90a5c9 |
apr_table_get(headers, "Last-Modified")));
|
|
Packit |
90a5c9 |
if (mtime == APR_DATE_BAD) {
|
|
Packit |
90a5c9 |
mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now());
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
reqtime = apr_time_sec(apr_date_parse_http(
|
|
Packit |
90a5c9 |
apr_table_get(headers, "Date")));
|
|
Packit |
90a5c9 |
if (!reqtime) {
|
|
Packit |
90a5c9 |
reqtime = apr_time_sec(r->request_time);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((ius != APR_DATE_BAD) && (mtime > ius)) {
|
|
Packit |
90a5c9 |
if (reqtime < mtime + 60) {
|
|
Packit |
90a5c9 |
if (apr_table_get(r->headers_in, "Range")) {
|
|
Packit |
90a5c9 |
/* weak matches not allowed with Range requests */
|
|
Packit |
90a5c9 |
return AP_CONDITION_NOMATCH;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AP_CONDITION_WEAK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AP_CONDITION_STRONG;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AP_CONDITION_NOMATCH;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return AP_CONDITION_NONE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(ap_condition_e) ap_condition_if_none_match(request_rec *r,
|
|
Packit |
90a5c9 |
apr_table_t *headers)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *if_nonematch, *etag;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
|
|
Packit |
90a5c9 |
if (if_nonematch != NULL) {
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (if_nonematch[0] == '*') {
|
|
Packit |
90a5c9 |
return AP_CONDITION_STRONG;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* See section 13.3.3 for rules on how to determine if two entities tags
|
|
Packit |
90a5c9 |
* match. The weak comparison function can only be used with GET or HEAD
|
|
Packit |
90a5c9 |
* requests.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (r->method_number == M_GET) {
|
|
Packit |
90a5c9 |
if ((etag = apr_table_get(headers, "ETag")) != NULL) {
|
|
Packit |
90a5c9 |
if (apr_table_get(r->headers_in, "Range")) {
|
|
Packit |
90a5c9 |
if (ap_find_etag_strong(r->pool, if_nonematch, etag)) {
|
|
Packit |
90a5c9 |
return AP_CONDITION_STRONG;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
if (ap_find_etag_weak(r->pool, if_nonematch, etag)) {
|
|
Packit |
90a5c9 |
return AP_CONDITION_WEAK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
else if ((etag = apr_table_get(headers, "ETag")) != NULL
|
|
Packit |
90a5c9 |
&& ap_find_etag_strong(r->pool, if_nonematch, etag)) {
|
|
Packit |
90a5c9 |
return AP_CONDITION_STRONG;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return AP_CONDITION_NOMATCH;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return AP_CONDITION_NONE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(ap_condition_e) ap_condition_if_modified_since(request_rec *r,
|
|
Packit |
90a5c9 |
apr_table_t *headers)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *if_modified_since;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((if_modified_since = apr_table_get(r->headers_in, "If-Modified-Since"))
|
|
Packit |
90a5c9 |
!= NULL) {
|
|
Packit |
90a5c9 |
apr_int64_t mtime;
|
|
Packit |
90a5c9 |
apr_int64_t ims, reqtime;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* All of our comparisons must be in seconds, because that's the
|
|
Packit |
90a5c9 |
* highest time resolution the HTTP specification allows.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
mtime = apr_time_sec(apr_date_parse_http(
|
|
Packit |
90a5c9 |
apr_table_get(headers, "Last-Modified")));
|
|
Packit |
90a5c9 |
if (mtime == APR_DATE_BAD) {
|
|
Packit |
90a5c9 |
mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now());
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
reqtime = apr_time_sec(apr_date_parse_http(
|
|
Packit |
90a5c9 |
apr_table_get(headers, "Date")));
|
|
Packit |
90a5c9 |
if (!reqtime) {
|
|
Packit |
90a5c9 |
reqtime = apr_time_sec(r->request_time);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ims = apr_time_sec(apr_date_parse_http(if_modified_since));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ims >= mtime && ims <= reqtime) {
|
|
Packit |
90a5c9 |
if (reqtime < mtime + 60) {
|
|
Packit |
90a5c9 |
if (apr_table_get(r->headers_in, "Range")) {
|
|
Packit |
90a5c9 |
/* weak matches not allowed with Range requests */
|
|
Packit |
90a5c9 |
return AP_CONDITION_NOMATCH;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AP_CONDITION_WEAK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AP_CONDITION_STRONG;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AP_CONDITION_NOMATCH;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return AP_CONDITION_NONE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(ap_condition_e) ap_condition_if_range(request_rec *r,
|
|
Packit |
90a5c9 |
apr_table_t *headers)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *if_range, *etag;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((if_range = apr_table_get(r->headers_in, "If-Range"))
|
|
Packit |
90a5c9 |
&& apr_table_get(r->headers_in, "Range")) {
|
|
Packit |
90a5c9 |
if (if_range[0] == '"') {
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((etag = apr_table_get(headers, "ETag"))
|
|
Packit |
90a5c9 |
&& !strcmp(if_range, etag)) {
|
|
Packit |
90a5c9 |
return AP_CONDITION_STRONG;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AP_CONDITION_NOMATCH;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
apr_int64_t mtime;
|
|
Packit |
90a5c9 |
apr_int64_t rtime, reqtime;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* All of our comparisons must be in seconds, because that's the
|
|
Packit |
90a5c9 |
* highest time resolution the HTTP specification allows.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
mtime = apr_time_sec(apr_date_parse_http(
|
|
Packit |
90a5c9 |
apr_table_get(headers, "Last-Modified")));
|
|
Packit |
90a5c9 |
if (mtime == APR_DATE_BAD) {
|
|
Packit |
90a5c9 |
mtime = apr_time_sec(r->mtime ? r->mtime : apr_time_now());
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
reqtime = apr_time_sec(apr_date_parse_http(
|
|
Packit |
90a5c9 |
apr_table_get(headers, "Date")));
|
|
Packit |
90a5c9 |
if (!reqtime) {
|
|
Packit |
90a5c9 |
reqtime = apr_time_sec(r->request_time);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
rtime = apr_time_sec(apr_date_parse_http(if_range));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (rtime == mtime) {
|
|
Packit |
90a5c9 |
if (reqtime < mtime + 60) {
|
|
Packit |
90a5c9 |
/* weak matches not allowed with Range requests */
|
|
Packit |
90a5c9 |
return AP_CONDITION_NOMATCH;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AP_CONDITION_STRONG;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return AP_CONDITION_NOMATCH;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return AP_CONDITION_NONE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(int) ap_meets_conditions(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int not_modified = -1; /* unset by default */
|
|
Packit |
90a5c9 |
ap_condition_e cond;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Check for conditional requests --- note that we only want to do
|
|
Packit |
90a5c9 |
* this if we are successful so far and we are not processing a
|
|
Packit |
90a5c9 |
* subrequest or an ErrorDocument.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* The order of the checks is important, since ETag checks are supposed
|
|
Packit |
90a5c9 |
* to be more accurate than checks relative to the modification time.
|
|
Packit |
90a5c9 |
* However, not all documents are guaranteed to *have* ETags, and some
|
|
Packit |
90a5c9 |
* might have Last-Modified values w/o ETags, so this gets a little
|
|
Packit |
90a5c9 |
* complicated.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If an If-Match request-header field was given
|
|
Packit |
90a5c9 |
* AND the field value is not "*" (meaning match anything)
|
|
Packit |
90a5c9 |
* AND if our strong ETag does not match any entity tag in that field,
|
|
Packit |
90a5c9 |
* respond with a status of 412 (Precondition Failed).
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
cond = ap_condition_if_match(r, r->headers_out);
|
|
Packit |
90a5c9 |
if (AP_CONDITION_NOMATCH == cond) {
|
|
Packit |
90a5c9 |
return HTTP_PRECONDITION_FAILED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Else if a valid If-Unmodified-Since request-header field was given
|
|
Packit |
90a5c9 |
* AND the requested resource has been modified since the time
|
|
Packit |
90a5c9 |
* specified in this field, then the server MUST
|
|
Packit |
90a5c9 |
* respond with a status of 412 (Precondition Failed).
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
cond = ap_condition_if_unmodified_since(r, r->headers_out);
|
|
Packit |
90a5c9 |
if (AP_CONDITION_NOMATCH == cond) {
|
|
Packit |
90a5c9 |
not_modified = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (cond >= AP_CONDITION_WEAK) {
|
|
Packit |
90a5c9 |
return HTTP_PRECONDITION_FAILED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If an If-None-Match request-header field was given
|
|
Packit |
90a5c9 |
* AND the field value is "*" (meaning match anything)
|
|
Packit |
90a5c9 |
* OR our ETag matches any of the entity tags in that field, fail.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If the request method was GET or HEAD, failure means the server
|
|
Packit |
90a5c9 |
* SHOULD respond with a 304 (Not Modified) response.
|
|
Packit |
90a5c9 |
* For all other request methods, failure means the server MUST
|
|
Packit |
90a5c9 |
* respond with a status of 412 (Precondition Failed).
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* GET or HEAD allow weak etag comparison, all other methods require
|
|
Packit |
90a5c9 |
* strong comparison. We can only use weak if it's not a range request.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
cond = ap_condition_if_none_match(r, r->headers_out);
|
|
Packit |
90a5c9 |
if (AP_CONDITION_NOMATCH == cond) {
|
|
Packit |
90a5c9 |
not_modified = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (cond >= AP_CONDITION_WEAK) {
|
|
Packit |
90a5c9 |
if (r->method_number == M_GET) {
|
|
Packit |
90a5c9 |
if (not_modified) {
|
|
Packit |
90a5c9 |
not_modified = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return HTTP_PRECONDITION_FAILED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If a valid If-Modified-Since request-header field was given
|
|
Packit |
90a5c9 |
* AND it is a GET or HEAD request
|
|
Packit |
90a5c9 |
* AND the requested resource has not been modified since the time
|
|
Packit |
90a5c9 |
* specified in this field, then the server MUST
|
|
Packit |
90a5c9 |
* respond with a status of 304 (Not Modified).
|
|
Packit |
90a5c9 |
* A date later than the server's current request time is invalid.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
cond = ap_condition_if_modified_since(r, r->headers_out);
|
|
Packit |
90a5c9 |
if (AP_CONDITION_NOMATCH == cond) {
|
|
Packit |
90a5c9 |
not_modified = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (cond >= AP_CONDITION_WEAK) {
|
|
Packit |
90a5c9 |
if (r->method_number == M_GET) {
|
|
Packit |
90a5c9 |
if (not_modified) {
|
|
Packit |
90a5c9 |
not_modified = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If an If-Range and an Range header is present, we must return
|
|
Packit |
90a5c9 |
* 200 OK. The byterange filter will convert it to a range response.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
cond = ap_condition_if_range(r, r->headers_out);
|
|
Packit |
90a5c9 |
if (cond > AP_CONDITION_NONE) {
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (not_modified == 1) {
|
|
Packit |
90a5c9 |
return HTTP_NOT_MODIFIED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/**
|
|
Packit |
90a5c9 |
* Singleton registry of additional methods. This maps new method names
|
|
Packit |
90a5c9 |
* such as "MYGET" to methnums, which are int offsets into bitmasks.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* This follows the same technique as standard M_GET, M_POST, etc. These
|
|
Packit |
90a5c9 |
* are dynamically assigned when modules are loaded and <Limit GET MYGET>
|
|
Packit |
90a5c9 |
* directives are processed.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_hash_t *methods_registry = NULL;
|
|
Packit |
90a5c9 |
static int cur_method_number = METHOD_NUMBER_FIRST;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* internal function to register one method/number pair */
|
|
Packit |
90a5c9 |
static void register_one_method(apr_pool_t *p, const char *methname,
|
|
Packit |
90a5c9 |
int methnum)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int *pnum = apr_palloc(p, sizeof(*pnum));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
*pnum = methnum;
|
|
Packit |
90a5c9 |
apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, pnum);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* This internal function is used to clear the method registry
|
|
Packit |
90a5c9 |
* and reset the cur_method_number counter.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static apr_status_t ap_method_registry_destroy(void *notused)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
methods_registry = NULL;
|
|
Packit |
90a5c9 |
cur_method_number = METHOD_NUMBER_FIRST;
|
|
Packit |
90a5c9 |
return APR_SUCCESS;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
methods_registry = apr_hash_make(p);
|
|
Packit |
90a5c9 |
apr_pool_cleanup_register(p, NULL,
|
|
Packit |
90a5c9 |
ap_method_registry_destroy,
|
|
Packit |
90a5c9 |
apr_pool_cleanup_null);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* put all the standard methods into the registry hash to ease the
|
|
Packit |
90a5c9 |
* mapping operations between name and number
|
|
Packit |
90a5c9 |
* HEAD is a special-instance of the GET method and shares the same ID
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
register_one_method(p, "GET", M_GET);
|
|
Packit |
90a5c9 |
register_one_method(p, "HEAD", M_GET);
|
|
Packit |
90a5c9 |
register_one_method(p, "PUT", M_PUT);
|
|
Packit |
90a5c9 |
register_one_method(p, "POST", M_POST);
|
|
Packit |
90a5c9 |
register_one_method(p, "DELETE", M_DELETE);
|
|
Packit |
90a5c9 |
register_one_method(p, "CONNECT", M_CONNECT);
|
|
Packit |
90a5c9 |
register_one_method(p, "OPTIONS", M_OPTIONS);
|
|
Packit |
90a5c9 |
register_one_method(p, "TRACE", M_TRACE);
|
|
Packit |
90a5c9 |
register_one_method(p, "PATCH", M_PATCH);
|
|
Packit |
90a5c9 |
register_one_method(p, "PROPFIND", M_PROPFIND);
|
|
Packit |
90a5c9 |
register_one_method(p, "PROPPATCH", M_PROPPATCH);
|
|
Packit |
90a5c9 |
register_one_method(p, "MKCOL", M_MKCOL);
|
|
Packit |
90a5c9 |
register_one_method(p, "COPY", M_COPY);
|
|
Packit |
90a5c9 |
register_one_method(p, "MOVE", M_MOVE);
|
|
Packit |
90a5c9 |
register_one_method(p, "LOCK", M_LOCK);
|
|
Packit |
90a5c9 |
register_one_method(p, "UNLOCK", M_UNLOCK);
|
|
Packit |
90a5c9 |
register_one_method(p, "VERSION-CONTROL", M_VERSION_CONTROL);
|
|
Packit |
90a5c9 |
register_one_method(p, "CHECKOUT", M_CHECKOUT);
|
|
Packit |
90a5c9 |
register_one_method(p, "UNCHECKOUT", M_UNCHECKOUT);
|
|
Packit |
90a5c9 |
register_one_method(p, "CHECKIN", M_CHECKIN);
|
|
Packit |
90a5c9 |
register_one_method(p, "UPDATE", M_UPDATE);
|
|
Packit |
90a5c9 |
register_one_method(p, "LABEL", M_LABEL);
|
|
Packit |
90a5c9 |
register_one_method(p, "REPORT", M_REPORT);
|
|
Packit |
90a5c9 |
register_one_method(p, "MKWORKSPACE", M_MKWORKSPACE);
|
|
Packit |
90a5c9 |
register_one_method(p, "MKACTIVITY", M_MKACTIVITY);
|
|
Packit |
90a5c9 |
register_one_method(p, "BASELINE-CONTROL", M_BASELINE_CONTROL);
|
|
Packit |
90a5c9 |
register_one_method(p, "MERGE", M_MERGE);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int *methnum;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (methods_registry == NULL) {
|
|
Packit |
90a5c9 |
ap_method_registry_init(p);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (methname == NULL) {
|
|
Packit |
90a5c9 |
return M_INVALID;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Check if the method was previously registered. If it was
|
|
Packit |
90a5c9 |
* return the associated method number.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
methnum = (int *)apr_hash_get(methods_registry, methname,
|
|
Packit |
90a5c9 |
APR_HASH_KEY_STRING);
|
|
Packit |
90a5c9 |
if (methnum != NULL)
|
|
Packit |
90a5c9 |
return *methnum;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (cur_method_number > METHOD_NUMBER_LAST) {
|
|
Packit |
90a5c9 |
/* The method registry has run out of dynamically
|
|
Packit |
90a5c9 |
* assignable method numbers. Log this and return M_INVALID.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p, APLOGNO(01610)
|
|
Packit |
90a5c9 |
"Maximum new request methods %d reached while "
|
|
Packit |
90a5c9 |
"registering method %s.",
|
|
Packit |
90a5c9 |
METHOD_NUMBER_LAST, methname);
|
|
Packit |
90a5c9 |
return M_INVALID;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
register_one_method(p, methname, cur_method_number);
|
|
Packit |
90a5c9 |
return cur_method_number++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define UNKNOWN_METHOD (-1)
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
static int lookup_builtin_method(const char *method, apr_size_t len)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
/* Note: the following code was generated by the "shilka" tool from
|
|
Packit |
90a5c9 |
the "cocom" parsing/compilation toolkit. It is an optimized lookup
|
|
Packit |
90a5c9 |
based on analysis of the input keywords. Postprocessing was done
|
|
Packit |
90a5c9 |
on the shilka output, but the basic structure and analysis is
|
|
Packit |
90a5c9 |
from there. Should new HTTP methods be added, then manual insertion
|
|
Packit |
90a5c9 |
into this code is fine, or simply re-running the shilka tool on
|
|
Packit |
90a5c9 |
the appropriate input. */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Note: it is also quite reasonable to just use our method_registry,
|
|
Packit |
90a5c9 |
but I'm assuming (probably incorrectly) we want more speed here
|
|
Packit |
90a5c9 |
(based on the optimizations the previous code was doing). */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
switch (len)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
case 3:
|
|
Packit |
90a5c9 |
switch (method[0])
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
case 'P':
|
|
Packit |
90a5c9 |
return (method[1] == 'U'
|
|
Packit |
90a5c9 |
&& method[2] == 'T'
|
|
Packit |
90a5c9 |
? M_PUT : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'G':
|
|
Packit |
90a5c9 |
return (method[1] == 'E'
|
|
Packit |
90a5c9 |
&& method[2] == 'T'
|
|
Packit |
90a5c9 |
? M_GET : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return UNKNOWN_METHOD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case 4:
|
|
Packit |
90a5c9 |
switch (method[0])
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
case 'H':
|
|
Packit |
90a5c9 |
return (method[1] == 'E'
|
|
Packit |
90a5c9 |
&& method[2] == 'A'
|
|
Packit |
90a5c9 |
&& method[3] == 'D'
|
|
Packit |
90a5c9 |
? M_GET : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'P':
|
|
Packit |
90a5c9 |
return (method[1] == 'O'
|
|
Packit |
90a5c9 |
&& method[2] == 'S'
|
|
Packit |
90a5c9 |
&& method[3] == 'T'
|
|
Packit |
90a5c9 |
? M_POST : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'M':
|
|
Packit |
90a5c9 |
return (method[1] == 'O'
|
|
Packit |
90a5c9 |
&& method[2] == 'V'
|
|
Packit |
90a5c9 |
&& method[3] == 'E'
|
|
Packit |
90a5c9 |
? M_MOVE : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'L':
|
|
Packit |
90a5c9 |
return (method[1] == 'O'
|
|
Packit |
90a5c9 |
&& method[2] == 'C'
|
|
Packit |
90a5c9 |
&& method[3] == 'K'
|
|
Packit |
90a5c9 |
? M_LOCK : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'C':
|
|
Packit |
90a5c9 |
return (method[1] == 'O'
|
|
Packit |
90a5c9 |
&& method[2] == 'P'
|
|
Packit |
90a5c9 |
&& method[3] == 'Y'
|
|
Packit |
90a5c9 |
? M_COPY : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return UNKNOWN_METHOD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case 5:
|
|
Packit |
90a5c9 |
switch (method[2])
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
case 'T':
|
|
Packit |
90a5c9 |
return (memcmp(method, "PATCH", 5) == 0
|
|
Packit |
90a5c9 |
? M_PATCH : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'R':
|
|
Packit |
90a5c9 |
return (memcmp(method, "MERGE", 5) == 0
|
|
Packit |
90a5c9 |
? M_MERGE : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'C':
|
|
Packit |
90a5c9 |
return (memcmp(method, "MKCOL", 5) == 0
|
|
Packit |
90a5c9 |
? M_MKCOL : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'B':
|
|
Packit |
90a5c9 |
return (memcmp(method, "LABEL", 5) == 0
|
|
Packit |
90a5c9 |
? M_LABEL : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'A':
|
|
Packit |
90a5c9 |
return (memcmp(method, "TRACE", 5) == 0
|
|
Packit |
90a5c9 |
? M_TRACE : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return UNKNOWN_METHOD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case 6:
|
|
Packit |
90a5c9 |
switch (method[0])
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
case 'U':
|
|
Packit |
90a5c9 |
switch (method[5])
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
case 'K':
|
|
Packit |
90a5c9 |
return (memcmp(method, "UNLOCK", 6) == 0
|
|
Packit |
90a5c9 |
? M_UNLOCK : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'E':
|
|
Packit |
90a5c9 |
return (memcmp(method, "UPDATE", 6) == 0
|
|
Packit |
90a5c9 |
? M_UPDATE : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return UNKNOWN_METHOD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
case 'R':
|
|
Packit |
90a5c9 |
return (memcmp(method, "REPORT", 6) == 0
|
|
Packit |
90a5c9 |
? M_REPORT : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'D':
|
|
Packit |
90a5c9 |
return (memcmp(method, "DELETE", 6) == 0
|
|
Packit |
90a5c9 |
? M_DELETE : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return UNKNOWN_METHOD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case 7:
|
|
Packit |
90a5c9 |
switch (method[1])
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
case 'P':
|
|
Packit |
90a5c9 |
return (memcmp(method, "OPTIONS", 7) == 0
|
|
Packit |
90a5c9 |
? M_OPTIONS : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'O':
|
|
Packit |
90a5c9 |
return (memcmp(method, "CONNECT", 7) == 0
|
|
Packit |
90a5c9 |
? M_CONNECT : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'H':
|
|
Packit |
90a5c9 |
return (memcmp(method, "CHECKIN", 7) == 0
|
|
Packit |
90a5c9 |
? M_CHECKIN : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return UNKNOWN_METHOD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case 8:
|
|
Packit |
90a5c9 |
switch (method[0])
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
case 'P':
|
|
Packit |
90a5c9 |
return (memcmp(method, "PROPFIND", 8) == 0
|
|
Packit |
90a5c9 |
? M_PROPFIND : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'C':
|
|
Packit |
90a5c9 |
return (memcmp(method, "CHECKOUT", 8) == 0
|
|
Packit |
90a5c9 |
? M_CHECKOUT : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return UNKNOWN_METHOD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case 9:
|
|
Packit |
90a5c9 |
return (memcmp(method, "PROPPATCH", 9) == 0
|
|
Packit |
90a5c9 |
? M_PROPPATCH : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case 10:
|
|
Packit |
90a5c9 |
switch (method[0])
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
case 'U':
|
|
Packit |
90a5c9 |
return (memcmp(method, "UNCHECKOUT", 10) == 0
|
|
Packit |
90a5c9 |
? M_UNCHECKOUT : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
case 'M':
|
|
Packit |
90a5c9 |
return (memcmp(method, "MKACTIVITY", 10) == 0
|
|
Packit |
90a5c9 |
? M_MKACTIVITY : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return UNKNOWN_METHOD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case 11:
|
|
Packit |
90a5c9 |
return (memcmp(method, "MKWORKSPACE", 11) == 0
|
|
Packit |
90a5c9 |
? M_MKWORKSPACE : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case 15:
|
|
Packit |
90a5c9 |
return (memcmp(method, "VERSION-CONTROL", 15) == 0
|
|
Packit |
90a5c9 |
? M_VERSION_CONTROL : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
case 16:
|
|
Packit |
90a5c9 |
return (memcmp(method, "BASELINE-CONTROL", 16) == 0
|
|
Packit |
90a5c9 |
? M_BASELINE_CONTROL : UNKNOWN_METHOD);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
default:
|
|
Packit |
90a5c9 |
return UNKNOWN_METHOD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* NOTREACHED */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Get the method number associated with the given string, assumed to
|
|
Packit |
90a5c9 |
* contain an HTTP method. Returns M_INVALID if not recognized.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* This is the first step toward placing method names in a configurable
|
|
Packit |
90a5c9 |
* list. Hopefully it (and other routines) can eventually be moved to
|
|
Packit |
90a5c9 |
* something like a mod_http_methods.c, complete with config stuff.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(int) ap_method_number_of(const char *method)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int len = strlen(method);
|
|
Packit |
90a5c9 |
int which = lookup_builtin_method(method, len);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (which != UNKNOWN_METHOD)
|
|
Packit |
90a5c9 |
return which;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* check if the method has been dynamically registered */
|
|
Packit |
90a5c9 |
if (methods_registry != NULL) {
|
|
Packit |
90a5c9 |
int *methnum = apr_hash_get(methods_registry, method, len);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (methnum != NULL) {
|
|
Packit |
90a5c9 |
return *methnum;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return M_INVALID;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Turn a known method number into a name.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_hash_index_t *hi = apr_hash_first(p, methods_registry);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* scan through the hash table, looking for a value that matches
|
|
Packit |
90a5c9 |
the provided method number. */
|
|
Packit |
90a5c9 |
for (; hi; hi = apr_hash_next(hi)) {
|
|
Packit |
90a5c9 |
const void *key;
|
|
Packit |
90a5c9 |
void *val;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_hash_this(hi, &key, NULL, &val;;
|
|
Packit |
90a5c9 |
if (*(int *)val == methnum)
|
|
Packit |
90a5c9 |
return key;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* it wasn't found in the hash */
|
|
Packit |
90a5c9 |
return NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* The index is found by its offset from the x00 code of each level.
|
|
Packit |
90a5c9 |
* Although this is fast, it will need to be replaced if some nutcase
|
|
Packit |
90a5c9 |
* decides to define a high-numbered code before the lower numbers.
|
|
Packit |
90a5c9 |
* If that sad event occurs, replace the code below with a linear search
|
|
Packit |
90a5c9 |
* from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
|
|
Packit |
90a5c9 |
* or use NULL to fill the gaps.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(int) ap_index_of_response(int status)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
|
|
Packit |
90a5c9 |
LEVEL_500, RESPONSE_CODES};
|
|
Packit |
90a5c9 |
int i, pos;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (status < 100) { /* Below 100 is illegal for HTTP status */
|
|
Packit |
90a5c9 |
return LEVEL_500;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (i = 0; i < 5; i++) {
|
|
Packit |
90a5c9 |
status -= 100;
|
|
Packit |
90a5c9 |
if (status < 100) {
|
|
Packit |
90a5c9 |
pos = (status + shortcut[i]);
|
|
Packit |
90a5c9 |
if (pos < shortcut[i + 1] && status_lines[pos] != NULL) {
|
|
Packit |
90a5c9 |
return pos;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return LEVEL_500; /* status unknown (falls in gap) */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
return LEVEL_500; /* 600 or above is also illegal */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(const char *) ap_get_status_line(int status)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
return status_lines[ap_index_of_response(status)];
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Build the Allow field-value from the request handler method mask.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static char *make_allow(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_int64_t mask;
|
|
Packit |
90a5c9 |
apr_array_header_t *allow = apr_array_make(r->pool, 10, sizeof(char *));
|
|
Packit |
90a5c9 |
apr_hash_index_t *hi = apr_hash_first(r->pool, methods_registry);
|
|
Packit |
90a5c9 |
/* For TRACE below */
|
|
Packit |
90a5c9 |
core_server_config *conf =
|
|
Packit |
90a5c9 |
ap_get_core_module_config(r->server->module_config);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
mask = r->allowed_methods->method_mask;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
for (; hi; hi = apr_hash_next(hi)) {
|
|
Packit |
90a5c9 |
const void *key;
|
|
Packit |
90a5c9 |
void *val;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_hash_this(hi, &key, NULL, &val;;
|
|
Packit |
90a5c9 |
if ((mask & (AP_METHOD_BIT << *(int *)val)) != 0) {
|
|
Packit |
90a5c9 |
APR_ARRAY_PUSH(allow, const char *) = key;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* TRACE is tested on a per-server basis */
|
|
Packit |
90a5c9 |
if (conf->trace_enable != AP_TRACE_DISABLE)
|
|
Packit |
90a5c9 |
*(const char **)apr_array_push(allow) = "TRACE";
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* ### this is rather annoying. we should enforce registration of
|
|
Packit |
90a5c9 |
### these methods */
|
|
Packit |
90a5c9 |
if ((mask & (AP_METHOD_BIT << M_INVALID))
|
|
Packit |
90a5c9 |
&& (r->allowed_methods->method_list != NULL)
|
|
Packit |
90a5c9 |
&& (r->allowed_methods->method_list->nelts != 0)) {
|
|
Packit |
90a5c9 |
apr_array_cat(allow, r->allowed_methods->method_list);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return apr_array_pstrcat(r->pool, allow, ',');
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(int) ap_send_http_options(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (r->assbackwards) {
|
|
Packit |
90a5c9 |
return DECLINED;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Allow", make_allow(r));
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* the request finalization will send an EOS, which will flush all
|
|
Packit |
90a5c9 |
* the headers out (including the Allow header)
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return OK;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
if (!ct) {
|
|
Packit |
90a5c9 |
r->content_type = NULL;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (!r->content_type || strcmp(r->content_type, ct)) {
|
|
Packit |
90a5c9 |
r->content_type = ct;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_set_accept_ranges(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
core_dir_config *d = ap_get_core_module_config(r->per_dir_config);
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Accept-Ranges",
|
|
Packit |
90a5c9 |
(d->max_ranges == AP_MAXRANGES_NORANGES) ? "none"
|
|
Packit |
90a5c9 |
: "bytes");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
static const char *add_optional_notes(request_rec *r,
|
|
Packit |
90a5c9 |
const char *prefix,
|
|
Packit |
90a5c9 |
const char *key,
|
|
Packit |
90a5c9 |
const char *suffix)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *notes, *result;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((notes = apr_table_get(r->notes, key)) == NULL) {
|
|
Packit |
90a5c9 |
result = apr_pstrcat(r->pool, prefix, suffix, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
result = apr_pstrcat(r->pool, prefix, notes, suffix, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return result;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* construct and return the default error message for a given
|
|
Packit |
90a5c9 |
* HTTP defined error code
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
static const char *get_canned_error_string(int status,
|
|
Packit |
90a5c9 |
request_rec *r,
|
|
Packit |
90a5c9 |
const char *location)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
apr_pool_t *p = r->pool;
|
|
Packit |
90a5c9 |
const char *error_notes, *h1, *s1;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
switch (status) {
|
|
Packit |
90a5c9 |
case HTTP_MOVED_PERMANENTLY:
|
|
Packit |
90a5c9 |
case HTTP_MOVED_TEMPORARILY:
|
|
Packit |
90a5c9 |
case HTTP_TEMPORARY_REDIRECT:
|
|
Packit |
90a5c9 |
case HTTP_PERMANENT_REDIRECT:
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"The document has moved
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, location),
|
|
Packit |
90a5c9 |
"\">here.\n",
|
|
Packit |
90a5c9 |
NULL));
|
|
Packit |
90a5c9 |
case HTTP_SEE_OTHER:
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"The answer to your request is located "
|
|
Packit |
90a5c9 |
"
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, location),
|
|
Packit |
90a5c9 |
"\">here.\n",
|
|
Packit |
90a5c9 |
NULL));
|
|
Packit |
90a5c9 |
case HTTP_USE_PROXY:
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"This resource is only accessible "
|
|
Packit |
90a5c9 |
"through the proxy\n",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, location),
|
|
Packit |
90a5c9 |
" \nYou will need to configure "
|
|
Packit |
90a5c9 |
"your client to use that proxy.\n",
|
|
Packit |
90a5c9 |
NULL));
|
|
Packit |
90a5c9 |
case HTTP_PROXY_AUTHENTICATION_REQUIRED:
|
|
Packit |
90a5c9 |
case HTTP_UNAUTHORIZED:
|
|
Packit |
90a5c9 |
return("This server could not verify that you\n"
|
|
Packit |
90a5c9 |
"are authorized to access the document\n"
|
|
Packit |
90a5c9 |
"requested. Either you supplied the wrong\n"
|
|
Packit |
90a5c9 |
"credentials (e.g., bad password), or your\n"
|
|
Packit |
90a5c9 |
"browser doesn't understand how to supply\n"
|
|
Packit |
90a5c9 |
"the credentials required.\n");
|
|
Packit |
90a5c9 |
case HTTP_BAD_REQUEST:
|
|
Packit |
90a5c9 |
return(add_optional_notes(r,
|
|
Packit |
90a5c9 |
"Your browser sent a request that "
|
|
Packit |
90a5c9 |
"this server could not understand. \n",
|
|
Packit |
90a5c9 |
"error-notes",
|
|
Packit |
90a5c9 |
"\n"));
|
|
Packit |
90a5c9 |
case HTTP_FORBIDDEN:
|
|
Packit |
90a5c9 |
s1 = apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"You don't have permission to access ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->uri),
|
|
Packit |
90a5c9 |
"\non this server. \n",
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
return(add_optional_notes(r, s1, "error-notes", "\n"));
|
|
Packit |
90a5c9 |
case HTTP_NOT_FOUND:
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"The requested URL ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->uri),
|
|
Packit |
90a5c9 |
" was not found on this server.\n",
|
|
Packit |
90a5c9 |
NULL));
|
|
Packit |
90a5c9 |
case HTTP_METHOD_NOT_ALLOWED:
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"The requested method ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->method),
|
|
Packit |
90a5c9 |
" is not allowed for the URL ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->uri),
|
|
Packit |
90a5c9 |
".\n",
|
|
Packit |
90a5c9 |
NULL));
|
|
Packit |
90a5c9 |
case HTTP_NOT_ACCEPTABLE:
|
|
Packit |
90a5c9 |
s1 = apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"An appropriate representation of the "
|
|
Packit |
90a5c9 |
"requested resource ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->uri),
|
|
Packit |
90a5c9 |
" could not be found on this server.\n",
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
return(add_optional_notes(r, s1, "variant-list", ""));
|
|
Packit |
90a5c9 |
case HTTP_MULTIPLE_CHOICES:
|
|
Packit |
90a5c9 |
return(add_optional_notes(r, "", "variant-list", ""));
|
|
Packit |
90a5c9 |
case HTTP_LENGTH_REQUIRED:
|
|
Packit |
90a5c9 |
s1 = apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"A request of the requested method ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->method),
|
|
Packit |
90a5c9 |
" requires a valid Content-length. \n",
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
return(add_optional_notes(r, s1, "error-notes", "\n"));
|
|
Packit |
90a5c9 |
case HTTP_PRECONDITION_FAILED:
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"The precondition on the request "
|
|
Packit |
90a5c9 |
"for the URL ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->uri),
|
|
Packit |
90a5c9 |
" evaluated to false.\n",
|
|
Packit |
90a5c9 |
NULL));
|
|
Packit |
90a5c9 |
case HTTP_NOT_IMPLEMENTED:
|
|
Packit |
90a5c9 |
s1 = apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->method), " to ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->uri),
|
|
Packit |
90a5c9 |
" not supported. \n",
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
return(add_optional_notes(r, s1, "error-notes", "\n"));
|
|
Packit |
90a5c9 |
case HTTP_BAD_GATEWAY:
|
|
Packit |
90a5c9 |
s1 = "The proxy server received an invalid" CRLF
|
|
Packit |
90a5c9 |
"response from an upstream server. " CRLF;
|
|
Packit |
90a5c9 |
return(add_optional_notes(r, s1, "error-notes", "\n"));
|
|
Packit |
90a5c9 |
case HTTP_VARIANT_ALSO_VARIES:
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"A variant for the requested "
|
|
Packit |
90a5c9 |
"resource\n\n",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->uri),
|
|
Packit |
90a5c9 |
"\n\nis itself a negotiable resource. "
|
|
Packit |
90a5c9 |
"This indicates a configuration error.\n",
|
|
Packit |
90a5c9 |
NULL));
|
|
Packit |
90a5c9 |
case HTTP_REQUEST_TIME_OUT:
|
|
Packit |
90a5c9 |
return("Server timeout waiting for the HTTP request from the client. \n");
|
|
Packit |
90a5c9 |
case HTTP_GONE:
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"The requested resource ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->uri),
|
|
Packit |
90a5c9 |
" \nis no longer available on this server "
|
|
Packit |
90a5c9 |
"and there is no forwarding address.\n"
|
|
Packit |
90a5c9 |
"Please remove all references to this "
|
|
Packit |
90a5c9 |
"resource.\n",
|
|
Packit |
90a5c9 |
NULL));
|
|
Packit |
90a5c9 |
case HTTP_REQUEST_ENTITY_TOO_LARGE:
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"The requested resource ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->uri), " \n",
|
|
Packit |
90a5c9 |
"does not allow request data with ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, r->method),
|
|
Packit |
90a5c9 |
" requests, or the amount of data provided in\n"
|
|
Packit |
90a5c9 |
"the request exceeds the capacity limit.\n",
|
|
Packit |
90a5c9 |
NULL));
|
|
Packit |
90a5c9 |
case HTTP_REQUEST_URI_TOO_LARGE:
|
|
Packit |
90a5c9 |
s1 = "The requested URL's length exceeds the capacity\n"
|
|
Packit |
90a5c9 |
"limit for this server. \n";
|
|
Packit |
90a5c9 |
return(add_optional_notes(r, s1, "error-notes", "\n"));
|
|
Packit |
90a5c9 |
case HTTP_UNSUPPORTED_MEDIA_TYPE:
|
|
Packit |
90a5c9 |
return("The supplied request data is not in a format\n"
|
|
Packit |
90a5c9 |
"acceptable for processing by this resource.\n");
|
|
Packit |
90a5c9 |
case HTTP_RANGE_NOT_SATISFIABLE:
|
|
Packit |
90a5c9 |
return("None of the range-specifier values in the Range\n"
|
|
Packit |
90a5c9 |
"request-header field overlap the current extent\n"
|
|
Packit |
90a5c9 |
"of the selected resource.\n");
|
|
Packit |
90a5c9 |
case HTTP_EXPECTATION_FAILED:
|
|
Packit |
90a5c9 |
s1 = apr_table_get(r->headers_in, "Expect");
|
|
Packit |
90a5c9 |
if (s1)
|
|
Packit |
90a5c9 |
s1 = apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"The expectation given in the Expect request-header\n"
|
|
Packit |
90a5c9 |
"field could not be met by this server.\n"
|
|
Packit |
90a5c9 |
"The client sent\n Expect: ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool, s1), "\n\n",
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
else
|
|
Packit |
90a5c9 |
s1 = "No expectation was seen, the Expect request-header \n"
|
|
Packit |
90a5c9 |
"field was not presented by the client.\n";
|
|
Packit |
90a5c9 |
return add_optional_notes(r, s1, "error-notes", ""
|
|
Packit |
90a5c9 |
"Only the 100-continue expectation is supported. \n");
|
|
Packit |
90a5c9 |
case HTTP_UNPROCESSABLE_ENTITY:
|
|
Packit |
90a5c9 |
return("The server understands the media type of the\n"
|
|
Packit |
90a5c9 |
"request entity, but was unable to process the\n"
|
|
Packit |
90a5c9 |
"contained instructions.\n");
|
|
Packit |
90a5c9 |
case HTTP_LOCKED:
|
|
Packit |
90a5c9 |
return("The requested resource is currently locked.\n"
|
|
Packit |
90a5c9 |
"The lock must be released or proper identification\n"
|
|
Packit |
90a5c9 |
"given before the method can be applied.\n");
|
|
Packit |
90a5c9 |
case HTTP_FAILED_DEPENDENCY:
|
|
Packit |
90a5c9 |
return("The method could not be performed on the resource\n"
|
|
Packit |
90a5c9 |
"because the requested action depended on another\n"
|
|
Packit |
90a5c9 |
"action and that other action failed.\n");
|
|
Packit |
90a5c9 |
case HTTP_UPGRADE_REQUIRED:
|
|
Packit |
90a5c9 |
return("The requested resource can only be retrieved\n"
|
|
Packit |
90a5c9 |
"using SSL. The server is willing to upgrade the current\n"
|
|
Packit |
90a5c9 |
"connection to SSL, but your client doesn't support it.\n"
|
|
Packit |
90a5c9 |
"Either upgrade your client, or try requesting the page\n"
|
|
Packit |
90a5c9 |
"using https://\n");
|
|
Packit |
90a5c9 |
case HTTP_PRECONDITION_REQUIRED:
|
|
Packit |
90a5c9 |
return("The request is required to be conditional. \n");
|
|
Packit |
90a5c9 |
case HTTP_TOO_MANY_REQUESTS:
|
|
Packit |
90a5c9 |
return("The user has sent too many requests\n"
|
|
Packit |
90a5c9 |
"in a given amount of time.\n");
|
|
Packit |
90a5c9 |
case HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE:
|
|
Packit |
90a5c9 |
return("The server refused this request because\n"
|
|
Packit |
90a5c9 |
"the request header fields are too large.\n");
|
|
Packit |
90a5c9 |
case HTTP_INSUFFICIENT_STORAGE:
|
|
Packit |
90a5c9 |
return("The method could not be performed on the resource\n"
|
|
Packit |
90a5c9 |
"because the server is unable to store the\n"
|
|
Packit |
90a5c9 |
"representation needed to successfully complete the\n"
|
|
Packit |
90a5c9 |
"request. There is insufficient free space left in\n"
|
|
Packit |
90a5c9 |
"your storage allocation.\n");
|
|
Packit |
90a5c9 |
case HTTP_SERVICE_UNAVAILABLE:
|
|
Packit |
90a5c9 |
return("The server is temporarily unable to service your\n"
|
|
Packit |
90a5c9 |
"request due to maintenance downtime or capacity\n"
|
|
Packit |
90a5c9 |
"problems. Please try again later.\n");
|
|
Packit |
90a5c9 |
case HTTP_GATEWAY_TIME_OUT:
|
|
Packit |
90a5c9 |
return("The gateway did not receive a timely response\n"
|
|
Packit |
90a5c9 |
"from the upstream server or application.\n");
|
|
Packit |
90a5c9 |
case HTTP_LOOP_DETECTED:
|
|
Packit |
90a5c9 |
return("The server terminated an operation because\n"
|
|
Packit |
90a5c9 |
"it encountered an infinite loop.\n");
|
|
Packit |
90a5c9 |
case HTTP_NOT_EXTENDED:
|
|
Packit |
90a5c9 |
return("A mandatory extension policy in the request is not\n"
|
|
Packit |
90a5c9 |
"accepted by the server for this resource.\n");
|
|
Packit |
90a5c9 |
case HTTP_NETWORK_AUTHENTICATION_REQUIRED:
|
|
Packit |
90a5c9 |
return("The client needs to authenticate to gain\n"
|
|
Packit |
90a5c9 |
"network access.\n");
|
|
Packit |
90a5c9 |
case HTTP_MISDIRECTED_REQUEST:
|
|
Packit |
90a5c9 |
return("The client needs a new connection for this\n"
|
|
Packit |
90a5c9 |
"request as the requested host name does not match\n"
|
|
Packit |
90a5c9 |
"the Server Name Indication (SNI) in use for this\n"
|
|
Packit |
90a5c9 |
"connection.\n");
|
|
Packit |
90a5c9 |
case HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
|
|
Packit |
90a5c9 |
s1 = apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"Access to ", ap_escape_html(r->pool, r->uri),
|
|
Packit |
90a5c9 |
"\nhas been denied for legal reasons. \n",
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
return(add_optional_notes(r, s1, "error-notes", "\n"));
|
|
Packit |
90a5c9 |
default: /* HTTP_INTERNAL_SERVER_ERROR */
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* This comparison to expose error-notes could be modified to
|
|
Packit |
90a5c9 |
* use a configuration directive and export based on that
|
|
Packit |
90a5c9 |
* directive. For now "*" is used to designate an error-notes
|
|
Packit |
90a5c9 |
* that is totally safe for any user to see (ie lacks paths,
|
|
Packit |
90a5c9 |
* database passwords, etc.)
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (((error_notes = apr_table_get(r->notes,
|
|
Packit |
90a5c9 |
"error-notes")) != NULL)
|
|
Packit |
90a5c9 |
&& (h1 = apr_table_get(r->notes, "verbose-error-to")) != NULL
|
|
Packit |
90a5c9 |
&& (strcmp(h1, "*") == 0)) {
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p, error_notes, "\n", NULL));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
return(apr_pstrcat(p,
|
|
Packit |
90a5c9 |
"The server encountered an internal "
|
|
Packit |
90a5c9 |
"error or\n"
|
|
Packit |
90a5c9 |
"misconfiguration and was unable to complete\n"
|
|
Packit |
90a5c9 |
"your request.\n"
|
|
Packit |
90a5c9 |
"Please contact the server "
|
|
Packit |
90a5c9 |
"administrator at \n ",
|
|
Packit |
90a5c9 |
ap_escape_html(r->pool,
|
|
Packit |
90a5c9 |
r->server->server_admin),
|
|
Packit |
90a5c9 |
" to inform them of the time this "
|
|
Packit |
90a5c9 |
"error occurred,\n"
|
|
Packit |
90a5c9 |
" and the actions you performed just before "
|
|
Packit |
90a5c9 |
"this error.\n"
|
|
Packit |
90a5c9 |
"More information about this error "
|
|
Packit |
90a5c9 |
"may be available\n"
|
|
Packit |
90a5c9 |
"in the server error log.\n",
|
|
Packit |
90a5c9 |
NULL));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* It would be nice to give the user the information they need to
|
|
Packit |
90a5c9 |
* fix the problem directly since many users don't have access to
|
|
Packit |
90a5c9 |
* the error_log (think University sites) even though they can easily
|
|
Packit |
90a5c9 |
* get this error by misconfiguring an htaccess file. However, the
|
|
Packit |
90a5c9 |
* e error notes tend to include the real file pathname in this case,
|
|
Packit |
90a5c9 |
* which some people consider to be a breach of privacy. Until we
|
|
Packit |
90a5c9 |
* can figure out a way to remove the pathname, leave this commented.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* if ((error_notes = apr_table_get(r->notes,
|
|
Packit |
90a5c9 |
* "error-notes")) != NULL) {
|
|
Packit |
90a5c9 |
* return(apr_pstrcat(p, error_notes, "\n", NULL);
|
|
Packit |
90a5c9 |
* }
|
|
Packit |
90a5c9 |
* else {
|
|
Packit |
90a5c9 |
* return "";
|
|
Packit |
90a5c9 |
* }
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We should have named this send_canned_response, since it is used for any
|
|
Packit |
90a5c9 |
* response that can be generated by the server from the request record.
|
|
Packit |
90a5c9 |
* This includes all 204 (no content), 3xx (redirect), 4xx (client error),
|
|
Packit |
90a5c9 |
* and 5xx (server error) messages that have not been redirected to another
|
|
Packit |
90a5c9 |
* handler via the ErrorDocument feature.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int status = r->status;
|
|
Packit |
90a5c9 |
int idx = ap_index_of_response(status);
|
|
Packit |
90a5c9 |
char *custom_response;
|
|
Packit |
90a5c9 |
const char *location = apr_table_get(r->headers_out, "Location");
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* At this point, we are starting the response over, so we have to reset
|
|
Packit |
90a5c9 |
* this value.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
r->eos_sent = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* and we need to get rid of any RESOURCE filters that might be lurking
|
|
Packit |
90a5c9 |
* around, thinking they are in the middle of the original request
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->output_filters = r->proto_output_filters;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_run_insert_error_filter(r);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* We need to special-case the handling of 204 and 304 responses,
|
|
Packit |
90a5c9 |
* since they have specific HTTP requirements and do not include a
|
|
Packit |
90a5c9 |
* message body. Note that being assbackwards here is not an option.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (AP_STATUS_IS_HEADER_ONLY(status)) {
|
|
Packit |
90a5c9 |
ap_finalize_request_protocol(r);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* It's possible that the Location field might be in r->err_headers_out
|
|
Packit |
90a5c9 |
* instead of r->headers_out; use the latter if possible, else the
|
|
Packit |
90a5c9 |
* former.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (location == NULL) {
|
|
Packit |
90a5c9 |
location = apr_table_get(r->err_headers_out, "Location");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!r->assbackwards) {
|
|
Packit |
90a5c9 |
apr_table_t *tmp = r->headers_out;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* For all HTTP/1.x responses for which we generate the message,
|
|
Packit |
90a5c9 |
* we need to avoid inheriting the "normal status" header fields
|
|
Packit |
90a5c9 |
* that may have been set by the request handler before the
|
|
Packit |
90a5c9 |
* error or redirect, except for Location on external redirects.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
r->headers_out = r->err_headers_out;
|
|
Packit |
90a5c9 |
r->err_headers_out = tmp;
|
|
Packit |
90a5c9 |
apr_table_clear(r->err_headers_out);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
|
|
Packit |
90a5c9 |
if ((location != NULL) && *location) {
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Location", location);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
location = ""; /* avoids coredump when printing, below */
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
r->content_languages = NULL;
|
|
Packit |
90a5c9 |
r->content_encoding = NULL;
|
|
Packit |
90a5c9 |
r->clength = 0;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (apr_table_get(r->subprocess_env,
|
|
Packit |
90a5c9 |
"suppress-error-charset") != NULL) {
|
|
Packit |
90a5c9 |
core_request_config *request_conf =
|
|
Packit |
90a5c9 |
ap_get_core_module_config(r->request_config);
|
|
Packit |
90a5c9 |
request_conf->suppress_charset = 1; /* avoid adding default
|
|
Packit |
90a5c9 |
* charset later
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
ap_set_content_type(r, "text/html");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
ap_set_content_type(r, "text/html; charset=iso-8859-1");
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((status == HTTP_METHOD_NOT_ALLOWED)
|
|
Packit |
90a5c9 |
|| (status == HTTP_NOT_IMPLEMENTED)) {
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "Allow", make_allow(r));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r->header_only) {
|
|
Packit |
90a5c9 |
ap_finalize_request_protocol(r);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if ((custom_response = ap_response_code_string(r, idx))) {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* We have a custom response output. This should only be
|
|
Packit |
90a5c9 |
* a text-string to write back. But if the ErrorDocument
|
|
Packit |
90a5c9 |
* was a local redirect and the requested resource failed
|
|
Packit |
90a5c9 |
* for any reason, the custom_response will still hold the
|
|
Packit |
90a5c9 |
* redirect URL. We don't really want to output this URL
|
|
Packit |
90a5c9 |
* as a text message, so first check the custom response
|
|
Packit |
90a5c9 |
* string to ensure that it is a text-string (using the
|
|
Packit |
90a5c9 |
* same test used in ap_die(), i.e. does it start with a ").
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If it's not a text string, we've got a recursive error or
|
|
Packit |
90a5c9 |
* an external redirect. If it's a recursive error, ap_die passes
|
|
Packit |
90a5c9 |
* us the second error code so we can write both, and has already
|
|
Packit |
90a5c9 |
* backed up to the original error. If it's an external redirect,
|
|
Packit |
90a5c9 |
* it hasn't happened yet; we may never know if it fails.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (custom_response[0] == '\"') {
|
|
Packit |
90a5c9 |
ap_rputs(custom_response + 1, r);
|
|
Packit |
90a5c9 |
ap_finalize_request_protocol(r);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
const char *title = status_lines[idx];
|
|
Packit |
90a5c9 |
const char *h1;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Accept a status_line set by a module, but only if it begins
|
|
Packit |
90a5c9 |
* with the correct 3 digit status code
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (r->status_line) {
|
|
Packit |
90a5c9 |
char *end;
|
|
Packit |
90a5c9 |
int len = strlen(r->status_line);
|
|
Packit |
90a5c9 |
if (len >= 3
|
|
Packit |
90a5c9 |
&& apr_strtoi64(r->status_line, &end, 10) == r->status
|
|
Packit |
90a5c9 |
&& (end - 3) == r->status_line
|
|
Packit |
90a5c9 |
&& (len < 4 || apr_isspace(r->status_line[3]))
|
|
Packit |
90a5c9 |
&& (len < 5 || apr_isalnum(r->status_line[4]))) {
|
|
Packit |
90a5c9 |
/* Since we passed the above check, we know that length three
|
|
Packit |
90a5c9 |
* is equivalent to only a 3 digit numeric http status.
|
|
Packit |
90a5c9 |
* RFC2616 mandates a trailing space, let's add it.
|
|
Packit |
90a5c9 |
* If we have an empty reason phrase, we also add "Unknown Reason".
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (len == 3) {
|
|
Packit |
90a5c9 |
r->status_line = apr_pstrcat(r->pool, r->status_line, " Unknown Reason", NULL);
|
|
Packit |
90a5c9 |
} else if (len == 4) {
|
|
Packit |
90a5c9 |
r->status_line = apr_pstrcat(r->pool, r->status_line, "Unknown Reason", NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
title = r->status_line;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* folks decided they didn't want the error code in the H1 text */
|
|
Packit |
90a5c9 |
h1 = &title[4];
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* can't count on a charset filter being in place here,
|
|
Packit |
90a5c9 |
* so do ebcdic->ascii translation explicitly (if needed)
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_rvputs_proto_in_ascii(r,
|
|
Packit |
90a5c9 |
DOCTYPE_HTML_2_0
|
|
Packit |
90a5c9 |
"<html><head>\n<title>", title,
|
|
Packit |
90a5c9 |
"</title>\n</head><body>\n", h1, "\n",
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ap_rvputs_proto_in_ascii(r,
|
|
Packit |
90a5c9 |
get_canned_error_string(status, r, location),
|
|
Packit |
90a5c9 |
NULL);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (recursive_error) {
|
|
Packit |
90a5c9 |
ap_rvputs_proto_in_ascii(r, "Additionally, a ",
|
|
Packit |
90a5c9 |
status_lines[ap_index_of_response(recursive_error)],
|
|
Packit |
90a5c9 |
"\nerror was encountered while trying to use an "
|
|
Packit |
90a5c9 |
"ErrorDocument to handle the request.\n", NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_rvputs_proto_in_ascii(r, ap_psignature(" \n", r), NULL);
|
|
Packit |
90a5c9 |
ap_rvputs_proto_in_ascii(r, "</body></html>\n", NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
ap_finalize_request_protocol(r);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Create a new method list with the specified number of preallocated
|
|
Packit |
90a5c9 |
* extension slots.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
ap_method_list_t *ml;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t));
|
|
Packit |
90a5c9 |
ml->method_mask = 0;
|
|
Packit |
90a5c9 |
ml->method_list = apr_array_make(p, nelts, sizeof(char *));
|
|
Packit |
90a5c9 |
return ml;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Make a copy of a method list (primarily for subrequests that may
|
|
Packit |
90a5c9 |
* subsequently change it; don't want them changing the parent's, too!).
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest,
|
|
Packit |
90a5c9 |
ap_method_list_t *src)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int i;
|
|
Packit |
90a5c9 |
char **imethods;
|
|
Packit |
90a5c9 |
char **omethods;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
dest->method_mask = src->method_mask;
|
|
Packit |
90a5c9 |
imethods = (char **) src->method_list->elts;
|
|
Packit |
90a5c9 |
for (i = 0; i < src->method_list->nelts; ++i) {
|
|
Packit |
90a5c9 |
omethods = (char **) apr_array_push(dest->method_list);
|
|
Packit |
90a5c9 |
*omethods = apr_pstrdup(dest->method_list->pool, imethods[i]);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Return true if the specified HTTP method is in the provided
|
|
Packit |
90a5c9 |
* method list.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int methnum;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* If it's one of our known methods, use the shortcut and check the
|
|
Packit |
90a5c9 |
* bitmask.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
methnum = ap_method_number_of(method);
|
|
Packit |
90a5c9 |
if (methnum != M_INVALID) {
|
|
Packit |
90a5c9 |
return !!(l->method_mask & (AP_METHOD_BIT << methnum));
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Otherwise, see if the method name is in the array of string names.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if ((l->method_list == NULL) || (l->method_list->nelts == 0)) {
|
|
Packit |
90a5c9 |
return 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return ap_array_str_contains(l->method_list, method);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Add the specified method to a method list (if it isn't already there).
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int methnum;
|
|
Packit |
90a5c9 |
const char **xmethod;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* If it's one of our known methods, use the shortcut and use the
|
|
Packit |
90a5c9 |
* bitmask.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
methnum = ap_method_number_of(method);
|
|
Packit |
90a5c9 |
if (methnum != M_INVALID) {
|
|
Packit |
90a5c9 |
l->method_mask |= (AP_METHOD_BIT << methnum);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Otherwise, see if the method name is in the array of string names.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (ap_array_str_contains(l->method_list, method)) {
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
xmethod = (const char **) apr_array_push(l->method_list);
|
|
Packit |
90a5c9 |
*xmethod = method;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Remove the specified method from a method list.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
|
|
Packit |
90a5c9 |
const char *method)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int methnum;
|
|
Packit |
90a5c9 |
char **methods;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* If it's a known methods, either builtin or registered
|
|
Packit |
90a5c9 |
* by a module, use the bitmask.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
methnum = ap_method_number_of(method);
|
|
Packit |
90a5c9 |
if (methnum != M_INVALID) {
|
|
Packit |
90a5c9 |
l->method_mask &= ~(AP_METHOD_BIT << methnum);
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Otherwise, see if the method name is in the array of string names.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (l->method_list->nelts != 0) {
|
|
Packit |
90a5c9 |
int i, j, k;
|
|
Packit |
90a5c9 |
methods = (char **)l->method_list->elts;
|
|
Packit |
90a5c9 |
for (i = 0; i < l->method_list->nelts; ) {
|
|
Packit |
90a5c9 |
if (strcmp(method, methods[i]) == 0) {
|
|
Packit |
90a5c9 |
for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) {
|
|
Packit |
90a5c9 |
methods[j] = methods[k];
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
--l->method_list->nelts;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
++i;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Reset a method list to be completely empty.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
l->method_mask = 0;
|
|
Packit |
90a5c9 |
l->method_list->nelts = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|