#define CORE_PRIVATE
#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"
#include "util_date.h"
#include <stdarg.h>
#include "http_conf_globals.h"
#define SET_BYTES_SENT(r) \
do { if (r->sent_bodyct) \
ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
} while (0)
#ifdef CHARSET_EBCDIC
#define PUSH_EBCDIC_INPUTCONVERSION_STATE(_buff, _onoff) \
int _convert_in = ap_bgetflag(_buff, B_ASCII2EBCDIC); \
ap_bsetflag(_buff, B_ASCII2EBCDIC, _onoff);
#define POP_EBCDIC_INPUTCONVERSION_STATE(_buff) \
ap_bsetflag(_buff, B_ASCII2EBCDIC, _convert_in);
#define PUSH_EBCDIC_INPUTCONVERSION_STATE_r(_req, _onoff) \
ap_bsetflag(_req->connection->client, B_ASCII2EBCDIC, _onoff);
#define POP_EBCDIC_INPUTCONVERSION_STATE_r(_req) \
ap_bsetflag(_req->connection->client, B_ASCII2EBCDIC, _req->ebcdic.conv_in);
#define PUSH_EBCDIC_OUTPUTCONVERSION_STATE_r(_req, _onoff) \
ap_bsetflag(_req->connection->client, B_EBCDIC2ASCII, _onoff);
#define POP_EBCDIC_OUTPUTCONVERSION_STATE_r(_req) \
ap_bsetflag(_req->connection->client, B_EBCDIC2ASCII, _req->ebcdic.conv_out);
#endif
static const char *make_content_type(request_rec *r, const char *type) {
char *needcset[] = {
"text/plain",
"text/html",
NULL };
char **pcset;
core_dir_config *conf;
conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
&core_module);
if (!type) {
type = ap_default_type(r);
}
if (conf->add_default_charset != ADD_DEFAULT_CHARSET_ON) {
return type;
}
if (ap_strcasestr(type, "charset=") != NULL) {
;
}
else {
for (pcset = needcset; *pcset ; pcset++) {
if (ap_strcasestr(type, *pcset) != NULL) {
type = ap_pstrcat(r->pool, type, "; charset=",
conf->add_default_charset_name, NULL);
break;
}
}
}
return type;
}
enum byterange_token {
BYTERANGE_OK,
BYTERANGE_EMPTY,
BYTERANGE_BADSYNTAX,
BYTERANGE_UNSATISFIABLE
};
static enum byterange_token
parse_byterange(request_rec *r, long *start, long *end)
{
while (ap_isspace(*r->range))
++r->range;
if (*r->range == '\0') {
return BYTERANGE_EMPTY;
}
else if (*r->range == ',') {
++r->range;
return BYTERANGE_EMPTY;
}
if (ap_isdigit(*r->range))
*start = ap_strtol(r->range, (char **)&r->range, 10);
else
*start = -1;
while (ap_isspace(*r->range))
++r->range;
if (*r->range != '-')
return BYTERANGE_BADSYNTAX;
++r->range;
while (ap_isspace(*r->range))
++r->range;
if (ap_isdigit(*r->range))
*end = ap_strtol(r->range, (char **)&r->range, 10);
else
*end = -1;
while (ap_isspace(*r->range))
++r->range;
if (*r->range == ',') {
++r->range;
}
else if (*r->range != '\0') {
return BYTERANGE_BADSYNTAX;
}
if (*start < 0) {
if (*end < 0)
return BYTERANGE_BADSYNTAX;
*start = r->clength - *end;
if (*start < 0)
*start = 0;
*end = r->clength - 1;
}
else {
if (*end >= 0 && *start > *end)
return BYTERANGE_BADSYNTAX;
if (*end < 0 || *end >= r->clength)
*end = r->clength - 1;
}
if (*start >= r->clength)
return BYTERANGE_UNSATISFIABLE;
return BYTERANGE_OK;
}
static int byterange_boundary(request_rec *r, long start, long end, int output)
{
int length = 0;
#ifdef CHARSET_EBCDIC
PUSH_EBCDIC_OUTPUTCONVERSION_STATE_r(r, 1);
#endif
if (start < 0 || end < 0) {
if (output)
ap_rvputs(r, CRLF "--", r->boundary, "--" CRLF, NULL);
else
length = 4 + strlen(r->boundary) + 4;
}
else {
const char *ct = make_content_type(r, r->content_type);
char ts[MAX_STRING_LEN];
ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", start, end, r->clength);
if (output)
ap_rvputs(r, CRLF "--", r->boundary, CRLF "Content-type: ",
ct, CRLF "Content-range: bytes ", ts, CRLF CRLF,
NULL);
else
length = 4 + strlen(r->boundary) + 16
+ strlen(ct) + 23 + strlen(ts) + 4;
}
#ifdef CHARSET_EBCDIC
POP_EBCDIC_OUTPUTCONVERSION_STATE_r(r);
#endif
return length;
}
API_EXPORT(int) ap_set_byterange(request_rec *r)
{
const char *range, *if_range, *match;
long length, start, end, one_start = 0, one_end = 0;
int ranges, empty;
if (!r->clength || r->assbackwards)
return 0;
if (!(range = ap_table_get(r->headers_in, "Range")))
range = ap_table_get(r->headers_in, "Request-Range");
if (!range || strncasecmp(range, "bytes=", 6)) {
return 0;
}
range += 6;
if ((if_range = ap_table_get(r->headers_in, "If-Range"))) {
if (if_range[0] == '"') {
if (!(match = ap_table_get(r->headers_out, "Etag")) ||
(strcmp(if_range, match) != 0))
return 0;
}
else if (!(match = ap_table_get(r->headers_out, "Last-Modified")) ||
(strcmp(if_range, match) != 0))
return 0;
}
r->range = range;
r->boundary = ap_psprintf(r->pool, "%lx%lx",
r->request_time, (long) getpid());
length = 0;
ranges = 0;
empty = 1;
do {
switch (parse_byterange(r, &start, &end)) {
case BYTERANGE_UNSATISFIABLE:
empty = 0;
break;
default:
case BYTERANGE_BADSYNTAX:
r->boundary = NULL;
r->range = NULL;
return 0;
case BYTERANGE_EMPTY:
break;
case BYTERANGE_OK:
++ranges;
length += byterange_boundary(r, start, end, 0)
+ end - start + 1;
one_start = start;
one_end = end;
break;
}
} while (*r->range != '\0');
if (ranges == 0) {
if (empty || if_range) {
r->boundary = NULL;
r->range = NULL;
return 0;
}
else {
ap_table_setn(r->headers_out, "Content-Range",
ap_psprintf(r->pool, "bytes */%ld", r->clength));
ap_set_content_length(r, 0);
r->boundary = NULL;
r->range = range;
r->header_only = 1;
r->status = HTTP_RANGE_NOT_SATISFIABLE;
return 1;
}
}
else if (ranges == 1) {
ap_table_setn(r->headers_out, "Content-Range",
ap_psprintf(r->pool, "bytes %ld-%ld/%ld",
one_start, one_end, r->clength));
ap_table_setn(r->headers_out, "Content-Length",
ap_psprintf(r->pool, "%ld", one_end - one_start + 1));
r->boundary = NULL;
r->byterange = 1;
r->range = range;
r->status = PARTIAL_CONTENT;
return 1;
}
else {
length += byterange_boundary(r, -1, -1, 0);
ap_table_setn(r->headers_out, "Content-Length",
ap_psprintf(r->pool, "%ld", length));
r->byterange = 2;
r->range = range;
r->status = PARTIAL_CONTENT;
return 1;
}
}
API_EXPORT(int) ap_each_byterange(request_rec *r, long *offset, long *length)
{
long start, end;
do {
if (parse_byterange(r, &start, &end) == BYTERANGE_OK) {
if (r->byterange > 1)
byterange_boundary(r, start, end, 1);
*offset = start;
*length = end - start + 1;
return 1;
}
} while (*r->range != '\0');
if (r->byterange > 1)
byterange_boundary(r, -1, -1, 1);
return 0;
}
API_EXPORT(int) ap_set_content_length(request_rec *r, long clength)
{
r->clength = clength;
ap_table_setn(r->headers_out, "Content-Length", ap_psprintf(r->pool, "%ld", clength));
return 0;
}
API_EXPORT(int) ap_set_keepalive(request_rec *r)
{
int ka_sent = 0;
int wimpy = ap_find_token(r->pool,
ap_table_get(r->headers_out, "Connection"), "close");
const char *conn = ap_table_get(r->headers_in, "Connection");
if ((r->connection->keepalive != -1) &&
((r->status == HTTP_NOT_MODIFIED) ||
(r->status == HTTP_NO_CONTENT) ||
r->header_only ||
ap_table_get(r->headers_out, "Content-Length") ||
ap_find_last_token(r->pool,
ap_table_get(r->headers_out, "Transfer-Encoding"),
"chunked") ||
((r->proto_num >= HTTP_VERSION(1,1)) &&
(r->chunked = 1))) &&
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") &&
(!ap_table_get(r->subprocess_env, "nokeepalive") ||
ap_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 = 1;
r->connection->keepalives++;
if (ka_sent) {
if (r->server->keep_alive_max)
ap_table_setn(r->headers_out, "Keep-Alive",
ap_psprintf(r->pool, "timeout=%d, max=%d",
r->server->keep_alive_timeout, left));
else
ap_table_setn(r->headers_out, "Keep-Alive",
ap_psprintf(r->pool, "timeout=%d",
r->server->keep_alive_timeout));
ap_table_mergen(r->headers_out, "Connection", "Keep-Alive");
}
return 1;
}
if (!wimpy)
ap_table_mergen(r->headers_out, "Connection", "close");
r->connection->keepalive = 0;
return 0;
}
API_EXPORT(time_t) ap_rationalize_mtime(request_rec *r, time_t mtime)
{
time_t now;
now = (mtime < r->request_time) ? r->request_time : time(NULL);
return (mtime > now) ? now : mtime;
}
API_EXPORT(int) ap_meets_conditions(request_rec *r)
{
const char *etag = ap_table_get(r->headers_out, "ETag");
const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
time_t mtime;
if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
return OK;
}
mtime = (r->mtime != 0) ? r->mtime : time(NULL);
if ((if_match = ap_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 {
if_unmodified = ap_table_get(r->headers_in, "If-Unmodified-Since");
if (if_unmodified != NULL) {
time_t ius = ap_parseHTTPdate(if_unmodified);
if ((ius != BAD_DATE) && (mtime > ius)) {
return HTTP_PRECONDITION_FAILED;
}
}
}
if_nonematch = ap_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 (ap_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 (strstr(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 ((r->method_number == M_GET)
&& ((if_modified_since =
ap_table_get(r->headers_in, "If-Modified-Since")) != NULL)) {
time_t ims = ap_parseHTTPdate(if_modified_since);
if ((ims >= mtime) && (ims <= r->request_time)) {
return HTTP_NOT_MODIFIED;
}
}
return OK;
}
API_EXPORT(char *) ap_make_etag(request_rec *r, int force_weak)
{
char *etag;
char *weak;
core_dir_config *cfg;
etag_components_t etag_bits;
cfg = (core_dir_config *)ap_get_module_config(r->per_dir_config,
&core_module);
etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add;
if (etag_bits == ETAG_UNSET) {
etag_bits = ETAG_BACKWARD;
}
weak = ((r->request_time - r->mtime > 1) && !force_weak) ? "" : "W/";
if (r->finfo.st_mode != 0) {
char **ent;
array_header *components;
int i;
if (etag_bits & ETAG_NONE) {
ap_table_setn(r->notes, "no-etag", "omit");
return "";
}
components = ap_make_array(r->pool, 4, sizeof(char *));
if (etag_bits & ETAG_INODE) {
ent = (char **) ap_push_array(components);
*ent = ap_psprintf(r->pool, "%lx",
(unsigned long) r->finfo.st_ino);
}
if (etag_bits & ETAG_SIZE) {
ent = (char **) ap_push_array(components);
*ent = ap_psprintf(r->pool, "%lx",
(unsigned long) r->finfo.st_size);
}
if (etag_bits & ETAG_MTIME) {
ent = (char **) ap_push_array(components);
*ent = ap_psprintf(r->pool, "%lx", (unsigned long) r->mtime);
}
ent = (char **) components->elts;
etag = ap_pstrcat(r->pool, weak, "\"", NULL);
for (i = 0; i < components->nelts; ++i) {
etag = ap_psprintf(r->pool, "%s%s%s", etag,
(i == 0 ? "" : "-"),
ent[i]);
}
etag = ap_pstrcat(r->pool, etag, "\"", NULL);
}
else {
etag = ap_psprintf(r->pool, "%s\"%lx\"", weak,
(unsigned long) r->mtime);
}
return etag;
}
API_EXPORT(void) ap_set_etag(request_rec *r)
{
char *etag;
char *variant_etag, *vlv;
int vlv_weak;
if (!r->vlist_validator) {
etag = ap_make_etag(r, 0);
if (!etag[0]) {
return;
}
}
else {
vlv = r->vlist_validator;
vlv_weak = (vlv[0] == 'W');
variant_etag = ap_make_etag(r, vlv_weak);
if (!variant_etag[0]) {
return;
}
variant_etag[strlen(variant_etag) - 1] = '\0';
if (vlv_weak)
vlv += 3;
else
vlv++;
etag = ap_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
}
ap_table_setn(r->headers_out, "ETag", etag);
}
API_EXPORT(void) ap_set_last_modified(request_rec *r)
{
time_t mod_time = ap_rationalize_mtime(r, r->mtime);
ap_table_setn(r->headers_out, "Last-Modified",
ap_gm_timestr_822(r->pool, mod_time));
}
API_EXPORT(int) ap_method_number_of(const char *method)
{
switch (*method) {
case 'H':
if (strcmp(method, "HEAD") == 0)
return M_GET;
break;
case 'G':
if (strcmp(method, "GET") == 0)
return M_GET;
break;
case 'P':
if (strcmp(method, "POST") == 0)
return M_POST;
if (strcmp(method, "PUT") == 0)
return M_PUT;
if (strcmp(method, "PATCH") == 0)
return M_PATCH;
if (strcmp(method, "PROPFIND") == 0)
return M_PROPFIND;
if (strcmp(method, "PROPPATCH") == 0)
return M_PROPPATCH;
break;
case 'D':
if (strcmp(method, "DELETE") == 0)
return M_DELETE;
break;
case 'C':
if (strcmp(method, "CONNECT") == 0)
return M_CONNECT;
if (strcmp(method, "COPY") == 0)
return M_COPY;
break;
case 'M':
if (strcmp(method, "MKCOL") == 0)
return M_MKCOL;
if (strcmp(method, "MOVE") == 0)
return M_MOVE;
break;
case 'O':
if (strcmp(method, "OPTIONS") == 0)
return M_OPTIONS;
break;
case 'T':
if (strcmp(method, "TRACE") == 0)
return M_TRACE;
break;
case 'L':
if (strcmp(method, "LOCK") == 0)
return M_LOCK;
break;
case 'U':
if (strcmp(method, "UNLOCK") == 0)
return M_UNLOCK;
break;
}
return M_INVALID;
}
API_EXPORT(int) ap_getline(char *s, int n, BUFF *in, int fold)
{
char *pos, next;
int retval;
int total = 0;
#ifdef CHARSET_EBCDIC
PUSH_EBCDIC_INPUTCONVERSION_STATE(in, 1);
#endif
pos = s;
do {
retval = ap_bgets(pos, n, in);
if (retval <= 0) {
total = ((retval < 0) && (total == 0)) ? -1 : total;
break;
}
n -= retval;
pos += (retval - 1);
total += retval;
if (*pos == '\n') {
while (pos > (s + 1) && (*(pos - 1) == ' ' || *(pos - 1) == '\t')) {
--pos;
--total;
++n;
}
*pos = '\0';
--total;
++n;
}
else
break;
} while (fold && (retval != 1) && (n > 1)
&& (ap_blookc(&next, in) == 1)
&& ((next == ' ') || (next == '\t')));
#ifdef CHARSET_EBCDIC
POP_EBCDIC_INPUTCONVERSION_STATE(in);
#endif
return total;
}
CORE_EXPORT(void) ap_parse_uri(request_rec *r, const char *uri)
{
int status = HTTP_OK;
r->unparsed_uri = ap_pstrdup(r->pool, uri);
if (r->method_number == M_CONNECT) {
status = ap_parse_hostinfo_components(r->pool, uri, &r->parsed_uri);
} else {
status = ap_parse_uri_components(r->pool, uri, &r->parsed_uri);
}
if (ap_is_HTTP_SUCCESS(status)) {
if (r->parsed_uri.scheme
&& !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))) {
r->hostname = r->parsed_uri.hostname;
} else if (r->method_number == M_CONNECT) {
r->hostname = r->parsed_uri.hostname;
}
r->args = r->parsed_uri.query;
r->uri = r->parsed_uri.path ? r->parsed_uri.path
: ap_pstrdup(r->pool, "/");
#if defined(OS2) || defined(WIN32)
{
char *x;
for (x = r->uri; (x = strchr(x, '\\')) != NULL; )
*x = '/';
}
#endif
}
else {
r->args = NULL;
r->hostname = NULL;
r->status = status;
r->uri = ap_pstrdup(r->pool, uri);
}
}
static int read_request_line(request_rec *r)
{
char l[DEFAULT_LIMIT_REQUEST_LINE + 2];
const char *ll = l;
const char *uri;
conn_rec *conn = r->connection;
unsigned int major = 1, minor = 0;
int len = 0;
int valid_protocol = 1;
ap_bsetflag(conn->client, B_SAFEREAD, 1);
while ((len = ap_getline(l, sizeof(l), conn->client, 0)) <= 0) {
if ((len < 0) || ap_bgetflag(conn->client, B_EOF)) {
ap_bsetflag(conn->client, B_SAFEREAD, 0);
r->request_time = time(0);
return 0;
}
}
#ifdef SIGUSR1
signal(SIGUSR1, SIG_IGN);
#endif
ap_bsetflag(conn->client, B_SAFEREAD, 0);
r->request_time = time(NULL);
r->the_request = ap_pstrdup(r->pool, l);
r->method = ap_getword_white(r->pool, &ll);
uri = ap_getword_white(r->pool, &ll);
r->method_number = ap_method_number_of(r->method);
if (r->method_number == M_GET && r->method[0] == 'H') {
r->header_only = 1;
}
ap_parse_uri(r, uri);
if (len > r->server->limit_req_line) {
r->status = HTTP_REQUEST_URI_TOO_LARGE;
r->proto_num = HTTP_VERSION(1,0);
r->protocol = ap_pstrdup(r->pool, "HTTP/1.0");
return 0;
}
r->assbackwards = (ll[0] == '\0');
r->protocol = ap_pstrdup(r->pool, ll[0] ? ll : "HTTP/0.9");
if (strlen(r->protocol) == 8
&& r->protocol[0] == 'H' && r->protocol[1] == 'T'
&& r->protocol[2] == 'T' && r->protocol[3] == 'P'
&& r->protocol[4] == '/' && ap_isdigit(r->protocol[5])
&& r->protocol[6] == '.' && ap_isdigit(r->protocol[7])) {
r->proto_num = HTTP_VERSION(r->protocol[5] - '0', r->protocol[7] - '0');
}
else {
char lint[2];
char http[5];
if (3 == sscanf(r->protocol, "%4s/%u.%u%1s", http, &major, &minor, lint)
&& (strcasecmp("http", http) == 0)
&& (minor < HTTP_VERSION(1,0)) )
r->proto_num = HTTP_VERSION(major, minor);
else {
r->proto_num = HTTP_VERSION(1,0);
valid_protocol = 0;
}
}
if (ap_protocol_req_check && !valid_protocol) {
int n = 0;
while (ap_isspace(r->protocol[n]))
++n;
if (r->protocol[n] != '\0') {
r->status = HTTP_BAD_REQUEST;
r->proto_num = HTTP_VERSION(1,0);
r->protocol = ap_pstrdup(r->pool, "HTTP/1.0");
ap_table_setn(r->notes, "error-notes",
"The request line contained invalid characters "
"following the protocol string.<P>\n");
return 0;
}
}
return 1;
}
static void get_mime_headers(request_rec *r)
{
char field[DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2];
conn_rec *c = r->connection;
char *value;
char *copy;
int len;
int fields_read = 0;
table *tmp_headers;
tmp_headers = ap_make_table(r->pool, 50);
while ((len = ap_getline(field, sizeof(field), c->client, 1)) > 0) {
if (r->server->limit_req_fields &&
(++fields_read > r->server->limit_req_fields)) {
r->status = HTTP_BAD_REQUEST;
ap_table_setn(r->notes, "error-notes",
"The number of request header fields exceeds "
"this server's limit.<P>\n");
return;
}
if (len > r->server->limit_req_fieldsize) {
r->status = HTTP_BAD_REQUEST;
ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
"Size of a request header field exceeds server limit.<P>\n"
"<PRE>\n", ap_escape_html(r->pool, field), "</PRE>\n", NULL));
return;
}
copy = ap_palloc(r->pool, len + 1);
memcpy(copy, field, len + 1);
if (!(value = strchr(copy, ':'))) {
r->status = HTTP_BAD_REQUEST;
ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
"Request header field is missing colon separator.<P>\n"
"<PRE>\n", ap_escape_html(r->pool, copy), "</PRE>\n", NULL));
return;
}
*value = '\0';
++value;
while (*value == ' ' || *value == '\t')
++value;
ap_table_addn(tmp_headers, copy, value);
}
ap_overlap_tables(r->headers_in, tmp_headers, AP_OVERLAP_TABLES_MERGE);
}
API_EXPORT(request_rec *) ap_read_request(conn_rec *conn)
{
request_rec *r;
pool *p;
const char *expect;
int access_status;
p = ap_make_sub_pool(conn->pool);
r = ap_pcalloc(p, sizeof(request_rec));
r->pool = p;
r->connection = conn;
conn->server = conn->base_server;
r->server = conn->server;
conn->keptalive = conn->keepalive == 1;
conn->keepalive = 0;
conn->user = NULL;
conn->ap_auth_type = NULL;
r->headers_in = ap_make_table(r->pool, 50);
r->subprocess_env = ap_make_table(r->pool, 50);
r->headers_out = ap_make_table(r->pool, 12);
r->err_headers_out = ap_make_table(r->pool, 5);
r->notes = ap_make_table(r->pool, 5);
r->request_config = ap_create_request_config(r->pool);
r->per_dir_config = r->server->lookup_defaults;
r->sent_bodyct = 0;
r->read_length = 0;
r->read_body = REQUEST_NO_BODY;
r->status = HTTP_REQUEST_TIME_OUT;
r->the_request = NULL;
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_ASCII2EBCDIC, r->ebcdic.conv_in = 1);
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, r->ebcdic.conv_out = 1);
#endif
ap_keepalive_timeout("read request line", r);
if (!read_request_line(r)) {
ap_kill_timeout(r);
if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"request failed: URI too long");
ap_send_error_response(r, 0);
ap_log_transaction(r);
return r;
}
else if (r->status == HTTP_BAD_REQUEST) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"request failed: erroneous characters after protocol string: %s",
ap_escape_logitem(r->pool, r->the_request));
ap_send_error_response(r, 0);
ap_log_transaction(r);
return r;
}
return NULL;
}
if (!r->assbackwards) {
ap_hard_timeout("read request headers", r);
get_mime_headers(r);
ap_kill_timeout(r);
if (r->status != HTTP_REQUEST_TIME_OUT) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"request failed: error reading the headers");
ap_send_error_response(r, 0);
ap_log_transaction(r);
return r;
}
}
else {
ap_kill_timeout(r);
if (r->header_only) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"client sent invalid HTTP/0.9 request: HEAD %s",
r->uri);
r->header_only = 0;
r->status = HTTP_BAD_REQUEST;
ap_send_error_response(r, 0);
ap_log_transaction(r);
return r;
}
}
r->status = HTTP_OK;
ap_update_vhost_from_headers(r);
r->per_dir_config = r->server->lookup_defaults;
conn->keptalive = 0;
if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1,1))) ||
((r->proto_num == HTTP_VERSION(1,1)) &&
!ap_table_get(r->headers_in, "Host"))) {
r->status = HTTP_BAD_REQUEST;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"client sent HTTP/1.1 request without hostname "
"(see RFC2616 section 14.23): %s", r->uri);
}
if (r->status != HTTP_OK) {
ap_send_error_response(r, 0);
ap_log_transaction(r);
return r;
}
if ((access_status = ap_run_post_read_request(r))) {
ap_die(access_status, r);
ap_log_transaction(r);
return NULL;
}
if (((expect = ap_table_get(r->headers_in, "Expect")) != NULL) &&
(expect[0] != '\0')) {
if (strcasecmp(expect, "100-continue") == 0) {
r->expecting_100 = 1;
}
else {
r->status = HTTP_EXPECTATION_FAILED;
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, r,
"client sent an unrecognized expectation value of "
"Expect: %s", expect);
ap_send_error_response(r, 0);
(void) ap_discard_request_body(r);
ap_log_transaction(r);
return r;
}
}
return r;
}
API_EXPORT(void) ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r)
{
rnew->the_request = r->the_request;
rnew->assbackwards = 1;
rnew->no_local_copy = 1;
rnew->method = "GET";
rnew->method_number = M_GET;
rnew->protocol = "INCLUDED";
rnew->status = HTTP_OK;
rnew->headers_in = r->headers_in;
rnew->subprocess_env = ap_copy_table(rnew->pool, r->subprocess_env);
rnew->headers_out = ap_make_table(rnew->pool, 5);
rnew->err_headers_out = ap_make_table(rnew->pool, 5);
rnew->notes = ap_make_table(rnew->pool, 5);
rnew->expecting_100 = r->expecting_100;
rnew->read_length = r->read_length;
rnew->read_body = REQUEST_NO_BODY;
rnew->main = (request_rec *) r;
}
API_EXPORT(void) ap_finalize_sub_req_protocol(request_rec *sub)
{
SET_BYTES_SENT(sub->main);
}
API_EXPORT(void) ap_note_auth_failure(request_rec *r)
{
if (!strcasecmp(ap_auth_type(r), "Basic"))
ap_note_basic_auth_failure(r);
else if (!strcasecmp(ap_auth_type(r), "Digest"))
ap_note_digest_auth_failure(r);
}
API_EXPORT(void) ap_note_basic_auth_failure(request_rec *r)
{
if (strcasecmp(ap_auth_type(r), "Basic"))
ap_note_auth_failure(r);
else
ap_table_setn(r->err_headers_out,
r->proxyreq == STD_PROXY ? "Proxy-Authenticate"
: "WWW-Authenticate",
ap_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"",
NULL));
}
API_EXPORT(void) ap_note_digest_auth_failure(request_rec *r)
{
ap_table_setn(r->err_headers_out,
r->proxyreq == STD_PROXY ? "Proxy-Authenticate"
: "WWW-Authenticate",
ap_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%lu\"",
ap_auth_name(r), r->request_time));
}
API_EXPORT(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
{
const char *auth_line = ap_table_get(r->headers_in,
r->proxyreq == STD_PROXY
? "Proxy-Authorization"
: "Authorization");
const char *t;
if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
return DECLINED;
if (!ap_auth_name(r)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
r, "need AuthName: %s", r->uri);
return SERVER_ERROR;
}
if (!auth_line) {
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"client used wrong authentication scheme: %s", r->uri);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
while (*auth_line== ' ' || *auth_line== '\t')
auth_line++;
t = ap_pbase64decode(r->pool, auth_line);
r->connection->user = ap_getword_nulls (r->connection->pool, &t, ':');
r->connection->ap_auth_type = "Basic";
*pw = t;
return OK;
}
#ifdef UTS21
static const char * status_lines[RESPONSE_CODES] =
#else
static 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",
#define LEVEL_500 44
"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"
};
API_EXPORT(int) ap_index_of_response(int status)
{
static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
LEVEL_500, RESPONSE_CODES};
int i, pos;
if (status < 100)
return LEVEL_500;
for (i = 0; i < 5; i++) {
status -= 100;
if (status < 100) {
pos = (status + shortcut[i]);
if (pos < shortcut[i + 1])
return pos;
else
return LEVEL_500;
}
}
return LEVEL_500;
}
API_EXPORT_NONSTD(int) ap_send_header_field(request_rec *r,
const char *fieldname,
const char *fieldval)
{
if (strcasecmp(fieldname, "ETag") == 0) {
if (ap_table_get(r->notes, "no-etag") != NULL) {
return 1;
}
}
return (0 < ap_rvputs(r, fieldname, ": ", fieldval, CRLF, NULL));
}
API_EXPORT(void) ap_basic_http_header(request_rec *r)
{
char *protocol;
if (r->assbackwards)
return;
if (!r->status_line)
r->status_line = status_lines[ap_index_of_response(r->status)];
if (r->proto_num == HTTP_VERSION(1,0)
&& ap_table_get(r->subprocess_env, "force-response-1.0")) {
protocol = "HTTP/1.0";
r->connection->keepalive = -1;
}
else
protocol = SERVER_PROTOCOL;
#ifdef CHARSET_EBCDIC
PUSH_EBCDIC_OUTPUTCONVERSION_STATE_r(r, 1);
#endif
ap_rvputs(r, protocol, " ", r->status_line, CRLF, NULL);
ap_send_header_field(r, "Date", ap_gm_timestr_822(r->pool, r->request_time));
if (r->proxyreq) {
const char *server = ap_table_get(r->headers_out, "Server");
if (server) {
ap_send_header_field(r, "Server", server);
}
}
else {
ap_send_header_field(r, "Server", ap_get_server_version());
}
ap_table_unset(r->headers_out, "Date");
ap_table_unset(r->headers_out, "Server");
#ifdef CHARSET_EBCDIC
POP_EBCDIC_OUTPUTCONVERSION_STATE_r(r);
#endif
}
static void terminate_header(BUFF *client)
{
long int bs;
ap_bgetopt(client, BO_BYTECT, &bs);
if (bs >= 255 && bs <= 257)
ap_bputs("X-Pad: avoid browser bug" CRLF, client);
ap_bputs(CRLF, client);
}
static char *make_allow(request_rec *r)
{
return 2 + ap_pstrcat(r->pool,
(r->allowed & (1 << M_GET)) ? ", GET, HEAD" : "",
(r->allowed & (1 << M_POST)) ? ", POST" : "",
(r->allowed & (1 << M_PUT)) ? ", PUT" : "",
(r->allowed & (1 << M_DELETE)) ? ", DELETE" : "",
(r->allowed & (1 << M_CONNECT)) ? ", CONNECT" : "",
(r->allowed & (1 << M_OPTIONS)) ? ", OPTIONS" : "",
(r->allowed & (1 << M_PATCH)) ? ", PATCH" : "",
(r->allowed & (1 << M_PROPFIND)) ? ", PROPFIND" : "",
(r->allowed & (1 << M_PROPPATCH)) ? ", PROPPATCH" : "",
(r->allowed & (1 << M_MKCOL)) ? ", MKCOL" : "",
(r->allowed & (1 << M_COPY)) ? ", COPY" : "",
(r->allowed & (1 << M_MOVE)) ? ", MOVE" : "",
(r->allowed & (1 << M_LOCK)) ? ", LOCK" : "",
(r->allowed & (1 << M_UNLOCK)) ? ", UNLOCK" : "",
", TRACE",
NULL);
}
API_EXPORT(int) ap_send_http_trace(request_rec *r)
{
int rv;
while (r->prev)
r = r->prev;
if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY)))
return rv;
ap_hard_timeout("send TRACE", r);
r->content_type = "message/http";
ap_send_http_header(r);
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, r->ebcdic.conv_out = 1);
#endif
ap_rvputs(r, r->the_request, CRLF, NULL);
ap_table_do((int (*) (void *, const char *, const char *))
ap_send_header_field, (void *) r, r->headers_in, NULL);
ap_rputs(CRLF, r);
ap_kill_timeout(r);
return OK;
}
API_EXPORT(int) ap_send_http_options(request_rec *r)
{
const long int zero = 0L;
if (r->assbackwards)
return DECLINED;
ap_hard_timeout("send OPTIONS", r);
ap_basic_http_header(r);
ap_table_setn(r->headers_out, "Content-Length", "0");
ap_table_setn(r->headers_out, "Allow", make_allow(r));
ap_set_keepalive(r);
ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field,
(void *) r, r->headers_out, NULL);
terminate_header(r->connection->client);
ap_kill_timeout(r);
ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
return OK;
}
static int use_range_x(request_rec *r)
{
const char *ua;
return (ap_table_get(r->headers_in, "Request-Range") ||
((ua = ap_table_get(r->headers_in, "User-Agent"))
&& strstr(ua, "MSIE 3")));
}
static int uniq_field_values(void *d, const char *key, const char *val)
{
array_header *values;
char *start;
char *e;
char **strpp;
int i;
values = (array_header *)d;
e = ap_pstrdup(values->pool, val);
do {
while (*e == ',' || ap_isspace(*e)) {
++e;
}
if (*e == '\0') {
break;
}
start = e;
while (*e != '\0' && *e != ',' && !ap_isspace(*e)) {
++e;
}
if (*e != '\0') {
*e++ = '\0';
}
for (i = 0, strpp = (char **) values->elts; i < values->nelts;
++i, ++strpp) {
if (*strpp && strcasecmp(*strpp, start) == 0) {
break;
}
}
if (i == values->nelts) {
*(char **)ap_push_array(values) = start;
}
} while (*e != '\0');
return 1;
}
static void fixup_vary(request_rec *r)
{
array_header *varies;
varies = ap_make_array(r->pool, 5, sizeof(char *));
ap_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
(void *) varies, r->headers_out, "Vary", NULL);
if (varies->nelts > 0) {
ap_table_setn(r->headers_out, "Vary",
ap_array_pstrcat(r->pool, varies, ','));
}
}
API_EXPORT(void) ap_send_http_header(request_rec *r)
{
int i;
const long int zero = 0L;
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, ap_checkconv(r));
#endif
if (r->assbackwards) {
if (!r->main)
ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
r->sent_bodyct = 1;
return;
}
if (!ap_is_empty_table(r->err_headers_out))
r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out,
r->headers_out);
if (ap_table_get(r->subprocess_env, "force-no-vary") != NULL) {
ap_table_unset(r->headers_out, "Vary");
r->proto_num = HTTP_VERSION(1,0);
ap_table_set(r->subprocess_env, "force-response-1.0", "1");
}
else {
fixup_vary(r);
}
ap_hard_timeout("send headers", r);
ap_basic_http_header(r);
#ifdef CHARSET_EBCDIC
PUSH_EBCDIC_OUTPUTCONVERSION_STATE_r(r, 1);
#endif
ap_set_keepalive(r);
if (r->chunked) {
ap_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
ap_table_unset(r->headers_out, "Content-Length");
}
if (r->byterange > 1)
ap_table_setn(r->headers_out, "Content-Type",
ap_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
"byteranges; boundary=", r->boundary, NULL));
else ap_table_setn(r->headers_out, "Content-Type", make_content_type(r,
r->content_type));
if (r->content_encoding)
ap_table_setn(r->headers_out, "Content-Encoding", r->content_encoding);
if (r->content_languages && r->content_languages->nelts) {
for (i = 0; i < r->content_languages->nelts; ++i) {
ap_table_mergen(r->headers_out, "Content-Language",
((char **) (r->content_languages->elts))[i]);
}
}
else if (r->content_language)
ap_table_setn(r->headers_out, "Content-Language", r->content_language);
if (r->no_cache && !ap_table_get(r->headers_out, "Expires"))
ap_table_addn(r->headers_out, "Expires",
ap_gm_timestr_822(r->pool, r->request_time));
ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field,
(void *) r, r->headers_out, NULL);
terminate_header(r->connection->client);
ap_kill_timeout(r);
ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
r->sent_bodyct = 1;
if (r->chunked)
ap_bsetflag(r->connection->client, B_CHUNK, 1);
#ifdef CHARSET_EBCDIC
POP_EBCDIC_OUTPUTCONVERSION_STATE_r(r);
#endif
}
API_EXPORT(void) ap_finalize_request_protocol(request_rec *r)
{
if (r->chunked && !r->connection->aborted) {
#ifdef CHARSET_EBCDIC
PUSH_EBCDIC_OUTPUTCONVERSION_STATE_r(r, 1);
#endif
r->chunked = 0;
ap_bsetflag(r->connection->client, B_CHUNK, 0);
ap_soft_timeout("send ending chunk", r);
ap_rputs("0" CRLF, r);
ap_rputs(CRLF, r);
ap_kill_timeout(r);
#ifdef CHARSET_EBCDIC
POP_EBCDIC_OUTPUTCONVERSION_STATE_r(r);
#endif
}
}
API_EXPORT(int) ap_setup_client_block(request_rec *r, int read_policy)
{
const char *tenc = ap_table_get(r->headers_in, "Transfer-Encoding");
const char *lenp = ap_table_get(r->headers_in, "Content-Length");
unsigned long max_body;
r->read_body = read_policy;
r->read_chunked = 0;
r->remaining = 0;
if (tenc) {
if (strcasecmp(tenc, "chunked")) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Unknown Transfer-Encoding %s", tenc);
return HTTP_NOT_IMPLEMENTED;
}
if (r->read_body == REQUEST_CHUNKED_ERROR) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"chunked Transfer-Encoding forbidden: %s", r->uri);
return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED;
}
r->read_chunked = 1;
}
else if (lenp) {
const char *pos = lenp;
int conversion_error = 0;
while (ap_isspace(*pos))
++pos;
if (*pos == '\0') {
r->remaining = 0;
}
else {
char *endstr;
errno = 0;
r->remaining = ap_strtol(lenp, &endstr, 10);
if (errno || (endstr && *endstr) || (r->remaining < 0)) {
conversion_error = 1;
}
}
if (conversion_error) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Invalid Content-Length");
return HTTP_BAD_REQUEST;
}
}
if ((r->read_body == REQUEST_NO_BODY) &&
(r->read_chunked || (r->remaining > 0))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"%s with body is not allowed for %s", r->method, r->uri);
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
max_body = ap_get_limit_req_body(r);
if (max_body && ((unsigned long)r->remaining > max_body)
&& (r->remaining >= 0)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Request content-length of %s is larger than the configured "
"limit of %lu", lenp, max_body);
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
#ifdef CHARSET_EBCDIC
{
ap_bsetflag(r->connection->client, B_ASCII2EBCDIC, ap_checkconv_in(r));
}
#endif
return OK;
}
API_EXPORT(int) ap_should_client_block(request_rec *r)
{
if (r->read_length || (!r->read_chunked && (r->remaining <= 0)))
return 0;
if (r->expecting_100 && r->proto_num >= HTTP_VERSION(1,1)) {
ap_rvputs(r, SERVER_PROTOCOL, " ", status_lines[0], CRLF CRLF,
NULL);
ap_rflush(r);
}
return 1;
}
API_EXPORT(long) ap_get_chunk_size(char *b)
{
long chunksize = 0;
long chunkbits = sizeof(long) * 8;
while (*b == '0') {
++b;
}
while (ap_isxdigit(*b) && (chunkbits > 0)) {
int xvalue = 0;
if (*b >= '0' && *b <= '9') {
xvalue = *b - '0';
}
else if (*b >= 'A' && *b <= 'F') {
xvalue = *b - 'A' + 0xa;
}
else if (*b >= 'a' && *b <= 'f') {
xvalue = *b - 'a' + 0xa;
}
chunksize = (chunksize << 4) | xvalue;
chunkbits -= 4;
++b;
}
if (ap_isxdigit(*b) && (chunkbits <= 0)) {
return -1;
}
return chunksize;
}
API_EXPORT(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz)
{
int c;
long len_read, len_to_read;
long chunk_start = 0;
unsigned long max_body;
if (!r->read_chunked) {
len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
len_read = ap_bread(r->connection->client, buffer, len_to_read);
if (len_read <= 0) {
if (len_read < 0)
r->connection->keepalive = -1;
return len_read;
}
r->read_length += len_read;
r->remaining -= len_read;
return len_read;
}
if (r->read_body == REQUEST_CHUNKED_PASS)
bufsiz -= 2;
if (bufsiz <= 0)
return -1;
max_body = ap_get_limit_req_body(r);
if (max_body && ((unsigned long) r->read_length > max_body)
&& (r->read_length >= 0)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
"Chunked request body is larger than the configured limit of %lu",
max_body);
r->connection->keepalive = -1;
return -1;
}
if (r->remaining == 0) {
chunk_start = ap_getline(buffer, bufsiz, r->connection->client, 0);
if ((chunk_start <= 0) || (chunk_start >= (bufsiz - 1))
|| !ap_isxdigit(*buffer)) {
r->connection->keepalive = -1;
return -1;
}
len_to_read = ap_get_chunk_size(buffer);
if (len_to_read == 0) {
if (r->read_body == REQUEST_CHUNKED_DECHUNK) {
get_mime_headers(r);
ap_snprintf(buffer, bufsiz, "%ld", r->read_length);
ap_table_unset(r->headers_in, "Transfer-Encoding");
ap_table_setn(r->headers_in, "Content-Length",
ap_pstrdup(r->pool, buffer));
return 0;
}
r->remaining = -1;
}
else if (len_to_read < 0) {
r->connection->keepalive = -1;
return -1;
}
else {
r->remaining = len_to_read;
}
if (r->read_body == REQUEST_CHUNKED_PASS) {
buffer[chunk_start++] = CR;
buffer[chunk_start++] = LF;
buffer += chunk_start;
bufsiz -= chunk_start;
}
else {
chunk_start = 0;
}
}
if (r->remaining == -1) {
len_read = chunk_start;
while ((bufsiz > 1) && ((len_read =
ap_getline(buffer, bufsiz, r->connection->client, 1)) > 0)) {
if (len_read != (bufsiz - 1)) {
buffer[len_read++] = CR;
buffer[len_read++] = LF;
}
chunk_start += len_read;
buffer += len_read;
bufsiz -= len_read;
}
if (len_read < 0) {
r->connection->keepalive = -1;
return -1;
}
if (len_read == 0) {
buffer[0] = CR;
buffer[1] = LF;
chunk_start += 2;
r->remaining = -2;
}
r->read_length += chunk_start;
return chunk_start;
}
if (r->remaining == -2) {
r->remaining = 0;
return 0;
}
len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
len_read = ap_bread(r->connection->client, buffer, len_to_read);
if (len_read <= 0) {
r->connection->keepalive = -1;
return -1;
}
r->remaining -= len_read;
if (r->remaining == 0) {
#ifdef CHARSET_EBCDIC
PUSH_EBCDIC_INPUTCONVERSION_STATE_r(r, 1);
#endif
if ((c = ap_bgetc(r->connection->client)) == CR) {
c = ap_bgetc(r->connection->client);
}
#ifdef CHARSET_EBCDIC
POP_EBCDIC_INPUTCONVERSION_STATE_r(r);
#endif
if (c != LF) {
r->connection->keepalive = -1;
return -1;
}
if (r->read_body == REQUEST_CHUNKED_PASS) {
buffer[len_read++] = CR;
buffer[len_read++] = LF;
}
}
r->read_length += (chunk_start + len_read);
return (chunk_start + len_read);
}
API_EXPORT(int) ap_discard_request_body(request_rec *r)
{
int rv;
if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_PASS)))
return rv;
if ((r->read_length == 0) && (r->read_chunked || (r->remaining > 0))) {
char dumpbuf[HUGE_STRING_LEN];
if (r->expecting_100) {
r->connection->keepalive = -1;
return OK;
}
ap_hard_timeout("reading request body", r);
while ((rv = ap_get_client_block(r, dumpbuf, HUGE_STRING_LEN)) > 0)
continue;
ap_kill_timeout(r);
if (rv < 0)
return HTTP_BAD_REQUEST;
}
return OK;
}
API_EXPORT(long) ap_send_fd(FILE *f, request_rec *r)
{
return ap_send_fd_length(f, r, -1);
}
API_EXPORT(long) ap_send_fd_length(FILE *f, request_rec *r, long length)
{
char buf[IOBUFSIZE];
long total_bytes_sent = 0;
register int n, w, o, len;
if (length == 0)
return 0;
ap_soft_timeout("send body", r);
while (!r->connection->aborted) {
if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length)
len = length - total_bytes_sent;
else
len = IOBUFSIZE;
while ((n = fread(buf, sizeof(char), len, f)) < 1
&& ferror(f) && errno == EINTR && !r->connection->aborted)
continue;
if (n < 1) {
break;
}
o = 0;
while (n && !r->connection->aborted) {
w = ap_bwrite(r->connection->client, &buf[o], n);
if (w > 0) {
ap_reset_timeout(r);
total_bytes_sent += w;
n -= w;
o += w;
}
else if (w < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before send body completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
break;
}
}
}
ap_kill_timeout(r);
SET_BYTES_SENT(r);
return total_bytes_sent;
}
API_EXPORT(long) ap_send_fb(BUFF *fb, request_rec *r)
{
return ap_send_fb_length(fb, r, -1);
}
API_EXPORT(long) ap_send_fb_length(BUFF *fb, request_rec *r, long length)
{
char buf[IOBUFSIZE];
long total_bytes_sent = 0;
register int n, w, o, len, fd;
fd_set fds;
#ifdef TPF
struct timeval tv;
#endif
if (length == 0)
return 0;
ap_bsetflag(fb, B_RD, 0);
#ifndef TPF_NO_NONSOCKET_SELECT
ap_bnonblock(fb, B_RD);
#endif
fd = ap_bfileno(fb, B_RD);
#ifdef CHECK_FD_SETSIZE
if (fd >= FD_SETSIZE) {
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
"send body: filedescriptor (%u) larger than FD_SETSIZE (%u) "
"found, you probably need to rebuild Apache with a "
"larger FD_SETSIZE", fd, FD_SETSIZE);
return 0;
}
#endif
ap_soft_timeout("send body", r);
FD_ZERO(&fds);
while (!r->connection->aborted) {
#ifdef NDELAY_PIPE_RETURNS_ZERO
int afterselect = 0;
#endif
if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length)
len = length - total_bytes_sent;
else
len = IOBUFSIZE;
do {
n = ap_bread(fb, buf, len);
#ifdef NDELAY_PIPE_RETURNS_ZERO
if ((n > 0) || (n == 0 && afterselect))
break;
#else
if (n >= 0)
break;
#endif
if (r->connection->aborted)
break;
if (n < 0 && errno != EAGAIN)
break;
if (ap_bflush(r->connection->client) < 0) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before send body completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
break;
}
FD_SET(fd, &fds);
#ifdef TPF_HAVE_NONSOCKET_SELECT
tv.tv_sec = 1;
tv.tv_usec = 0;
ap_select(fd + 1, &fds, NULL, NULL, &tv);
#else
ap_select(fd + 1, &fds, NULL, NULL, NULL);
#endif
#ifdef NDELAY_PIPE_RETURNS_ZERO
afterselect = 1;
#endif
} while (!r->connection->aborted);
if (n < 1 || r->connection->aborted) {
break;
}
o = 0;
while (n && !r->connection->aborted) {
w = ap_bwrite(r->connection->client, &buf[o], n);
if (w > 0) {
ap_reset_timeout(r);
total_bytes_sent += w;
n -= w;
o += w;
}
else if (w < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before send body completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
break;
}
}
}
ap_kill_timeout(r);
SET_BYTES_SENT(r);
return total_bytes_sent;
}
#ifndef MMAP_SEGMENT_SIZE
#define MMAP_SEGMENT_SIZE 32768
#endif
API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset,
size_t length)
{
size_t total_bytes_sent = 0;
int n, w;
if (length == 0)
return 0;
ap_soft_timeout("send mmap", r);
length += offset;
while (!r->connection->aborted && offset < length) {
if (length - offset > MMAP_SEGMENT_SIZE) {
n = MMAP_SEGMENT_SIZE;
}
else {
n = length - offset;
}
while (n && !r->connection->aborted) {
w = ap_bwrite(r->connection->client, (char *) mm + offset, n);
if (w > 0) {
ap_reset_timeout(r);
total_bytes_sent += w;
n -= w;
offset += w;
}
else if (w < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before send mmap completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
break;
}
}
}
ap_kill_timeout(r);
SET_BYTES_SENT(r);
return total_bytes_sent;
}
API_EXPORT(int) ap_rputc(int c, request_rec *r)
{
if (r->connection->aborted)
return EOF;
if (ap_bputc(c, r->connection->client) < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before rputc completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return EOF;
}
SET_BYTES_SENT(r);
return c;
}
API_EXPORT(int) ap_rputs(const char *str, request_rec *r)
{
int rcode;
if (r->connection->aborted)
return EOF;
rcode = ap_bputs(str, r->connection->client);
if (rcode < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before rputs completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return EOF;
}
SET_BYTES_SENT(r);
return rcode;
}
API_EXPORT(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
{
int n;
if (r->connection->aborted)
return -1;
n = ap_bwrite(r->connection->client, buf, nbyte);
if (n < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before rwrite completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return -1;
}
SET_BYTES_SENT(r);
return n;
}
API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list ap)
{
int n;
if (r->connection->aborted)
return -1;
n = ap_vbprintf(r->connection->client, fmt, ap);
if (n < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before vrprintf completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return -1;
}
SET_BYTES_SENT(r);
return n;
}
API_EXPORT_NONSTD(int) ap_rprintf(request_rec *r, const char *fmt,...)
{
va_list vlist;
int n;
if (r->connection->aborted)
return -1;
va_start(vlist, fmt);
n = ap_vbprintf(r->connection->client, fmt, vlist);
va_end(vlist);
if (n < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before rprintf completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return -1;
}
SET_BYTES_SENT(r);
return n;
}
API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r,...)
{
va_list args;
int i, j, k;
const char *x;
BUFF *fb = r->connection->client;
if (r->connection->aborted)
return EOF;
va_start(args, r);
for (k = 0;;) {
x = va_arg(args, const char *);
if (x == NULL)
break;
j = strlen(x);
i = ap_bwrite(fb, x, j);
if (i != j) {
va_end(args);
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before rvputs completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return EOF;
}
k += i;
}
va_end(args);
SET_BYTES_SENT(r);
return k;
}
API_EXPORT(int) ap_rflush(request_rec *r)
{
if (ap_bflush(r->connection->client) < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, r,
"client stopped connection before rflush completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return EOF;
}
return 0;
}
API_EXPORT(void) ap_send_error_response(request_rec *r, int recursive_error)
{
int status = r->status;
int idx = ap_index_of_response(status);
char *custom_response;
const char *location = ap_table_get(r->headers_out, "Location");
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, r->ebcdic.conv_out = 1);
#endif
if (location == NULL) {
location = ap_table_get(r->err_headers_out, "Location");
}
if (status == HTTP_NOT_MODIFIED) {
if (!ap_is_empty_table(r->err_headers_out))
r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out,
r->headers_out);
ap_hard_timeout("send 304", r);
ap_basic_http_header(r);
ap_set_keepalive(r);
ap_table_do((int (*)(void *, const char *, const char *)) ap_send_header_field,
(void *) r, r->headers_out,
"Connection",
"Keep-Alive",
"ETag",
"Content-Location",
"Expires",
"Cache-Control",
"Vary",
"Warning",
"WWW-Authenticate",
"Proxy-Authenticate",
NULL);
terminate_header(r->connection->client);
ap_kill_timeout(r);
return;
}
if (status == HTTP_NO_CONTENT) {
ap_send_http_header(r);
ap_finalize_request_protocol(r);
return;
}
if (!r->assbackwards) {
table *tmp = r->headers_out;
r->headers_out = r->err_headers_out;
r->err_headers_out = tmp;
ap_clear_table(r->err_headers_out);
if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
if ((location != NULL) && *location) {
ap_table_setn(r->headers_out, "Location", location);
}
else {
location = "";
}
}
r->content_language = NULL;
r->content_languages = NULL;
r->content_encoding = NULL;
r->clength = 0;
if (ap_table_get(r->subprocess_env,
"suppress-error-charset") != NULL) {
r->content_type = "text/html";
}
else {
r->content_type = "text/html; charset=iso-8859-1";
}
if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED))
ap_table_setn(r->headers_out, "Allow", make_allow(r));
ap_send_http_header(r);
if (r->header_only) {
ap_finalize_request_protocol(r);
ap_rflush(r);
return;
}
}
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, r->ebcdic.conv_out = 1);
#endif
ap_hard_timeout("send error body", r);
if ((custom_response = ap_response_code_string(r, idx))) {
if (custom_response[0] == '\"') {
ap_rputs(custom_response + 1, r);
ap_kill_timeout(r);
ap_finalize_request_protocol(r);
ap_rflush(r);
return;
}
while (r->prev && (r->prev->status != HTTP_OK))
r = r->prev;
}
{
const char *title = status_lines[idx];
const char *h1;
const char *error_notes;
if (r->status_line != NULL
&& strlen(r->status_line) > 4
&& ap_isdigit(r->status_line[0])
&& ap_isdigit(r->status_line[1])
&& ap_isdigit(r->status_line[2])
&& ap_isspace(r->status_line[3])
&& ap_isalnum(r->status_line[4])) {
title = r->status_line;
}
h1 = &title[4];
ap_rvputs(r,
DOCTYPE_HTML_2_0
"<HTML><HEAD>\n<TITLE>", title,
"</TITLE>\n</HEAD><BODY>\n<H1>", h1, "</H1>\n",
NULL);
switch (status) {
case HTTP_MOVED_PERMANENTLY:
case HTTP_MOVED_TEMPORARILY:
case HTTP_TEMPORARY_REDIRECT:
ap_rvputs(r, "The document has moved <A HREF=\"",
ap_escape_html(r->pool, location), "\">here</A>.<P>\n",
NULL);
break;
case HTTP_SEE_OTHER:
ap_rvputs(r, "The answer to your request is located <A HREF=\"",
ap_escape_html(r->pool, location), "\">here</A>.<P>\n",
NULL);
break;
case HTTP_USE_PROXY:
ap_rvputs(r, "This resource is only accessible "
"through the proxy\n",
ap_escape_html(r->pool, location),
"<BR>\nYou will need to ",
"configure your client to use that proxy.<P>\n", NULL);
break;
case HTTP_PROXY_AUTHENTICATION_REQUIRED:
case AUTH_REQUIRED:
ap_rputs("This server could not verify that you\n"
"are authorized to access the document\n"
"requested. Either you supplied the wrong\n"
"credentials (e.g., bad password), or your\n"
"browser doesn't understand how to supply\n"
"the credentials required.<P>\n", r);
break;
case BAD_REQUEST:
ap_rputs("Your browser sent a request that "
"this server could not understand.<P>\n", r);
if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
break;
case HTTP_FORBIDDEN:
ap_rvputs(r, "You don't have permission to access ",
ap_escape_html(r->pool, r->uri),
"\non this server.<P>\n", NULL);
break;
case NOT_FOUND:
ap_rvputs(r, "The requested URL ",
ap_escape_html(r->pool, r->uri),
" was not found on this server.<P>\n", NULL);
break;
case METHOD_NOT_ALLOWED:
ap_rvputs(r, "The requested method ", r->method,
" is not allowed "
"for the URL ", ap_escape_html(r->pool, r->uri),
".<P>\n", NULL);
break;
case NOT_ACCEPTABLE:
ap_rvputs(r,
"An appropriate representation of the "
"requested resource ",
ap_escape_html(r->pool, r->uri),
" could not be found on this server.<P>\n", NULL);
case MULTIPLE_CHOICES:
{
const char *list;
if ((list = ap_table_get(r->notes, "variant-list")))
ap_rputs(list, r);
}
break;
case LENGTH_REQUIRED:
ap_rvputs(r, "A request of the requested method ", r->method,
" requires a valid Content-length.<P>\n", NULL);
if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
break;
case PRECONDITION_FAILED:
ap_rvputs(r, "The precondition on the request for the URL ",
ap_escape_html(r->pool, r->uri),
" evaluated to false.<P>\n", NULL);
break;
case HTTP_NOT_IMPLEMENTED:
ap_rvputs(r, ap_escape_html(r->pool, r->method), " to ",
ap_escape_html(r->pool, r->uri),
" not supported.<P>\n", NULL);
if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
break;
case BAD_GATEWAY:
ap_rputs("The proxy server received an invalid" CRLF
"response from an upstream server.<P>" CRLF, r);
if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
break;
case VARIANT_ALSO_VARIES:
ap_rvputs(r, "A variant for the requested resource\n<PRE>\n",
ap_escape_html(r->pool, r->uri),
"\n</PRE>\nis itself a negotiable resource. "
"This indicates a configuration error.<P>\n", NULL);
break;
case HTTP_REQUEST_TIME_OUT:
ap_rputs("Server timeout waiting for the HTTP request from the client.\n", r);
break;
case HTTP_GONE:
ap_rvputs(r, "The requested resource<BR>",
ap_escape_html(r->pool, r->uri),
"<BR>\nis no longer available on this server ",
"and there is no forwarding address.\n",
"Please remove all references to this resource.\n",
NULL);
break;
case HTTP_REQUEST_ENTITY_TOO_LARGE:
ap_rvputs(r, "The requested resource<BR>",
ap_escape_html(r->pool, r->uri), "<BR>\n",
"does not allow request data with ", r->method,
" requests, or the amount of data provided in\n",
"the request exceeds the capacity limit.\n", NULL);
break;
case HTTP_REQUEST_URI_TOO_LARGE:
ap_rputs("The requested URL's length exceeds the capacity\n"
"limit for this server.<P>\n", r);
if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
break;
case HTTP_UNSUPPORTED_MEDIA_TYPE:
ap_rputs("The supplied request data is not in a format\n"
"acceptable for processing by this resource.\n", r);
break;
case HTTP_RANGE_NOT_SATISFIABLE:
ap_rputs("None of the range-specifier values in the Range\n"
"request-header field overlap the current extent\n"
"of the selected resource.\n", r);
break;
case HTTP_EXPECTATION_FAILED:
ap_rvputs(r, "The expectation given in the Expect request-header"
"\nfield could not be met by this server.<P>\n"
"The client sent<PRE>\n Expect: ",
ap_table_get(r->headers_in, "Expect"), "\n</PRE>\n"
"but we only allow the 100-continue expectation.\n",
NULL);
break;
case HTTP_UNPROCESSABLE_ENTITY:
ap_rputs("The server understands the media type of the\n"
"request entity, but was unable to process the\n"
"contained instructions.\n", r);
break;
case HTTP_LOCKED:
ap_rputs("The requested resource is currently locked.\n"
"The lock must be released or proper identification\n"
"given before the method can be applied.\n", r);
break;
case HTTP_FAILED_DEPENDENCY:
ap_rputs("The method could not be performed on the resource\n"
"because the requested action depended on another\n"
"action and that other action failed.\n", r);
break;
case HTTP_INSUFFICIENT_STORAGE:
ap_rputs("The method could not be performed on the resource\n"
"because the server is unable to store the\n"
"representation needed to successfully complete the\n"
"request. There is insufficient free space left in\n"
"your storage allocation.\n", r);
break;
case HTTP_SERVICE_UNAVAILABLE:
ap_rputs("The server is temporarily unable to service your\n"
"request due to maintenance downtime or capacity\n"
"problems. Please try again later.\n", r);
break;
case HTTP_GATEWAY_TIME_OUT:
ap_rputs("The proxy server did not receive a timely response\n"
"from the upstream server.\n", r);
break;
case HTTP_NOT_EXTENDED:
ap_rputs("A mandatory extension policy in the request is not\n"
"accepted by the server for this resource.\n", r);
break;
default:
if (((error_notes = ap_table_get(r->notes, "error-notes")) != NULL)
&& (h1 = ap_table_get(r->notes, "verbose-error-to")) != NULL
&& (strcmp(h1, "*") == 0)) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
else {
ap_rvputs(r, "The server encountered an internal error or\n"
"misconfiguration and was unable to complete\n"
"your request.<P>\n"
"Please contact the server administrator,\n ",
ap_escape_html(r->pool, r->server->server_admin),
" and inform them of the time the error occurred,\n"
"and anything you might have done that may have\n"
"caused the error.<P>\n"
"More information about this error may be available\n"
"in the server error log.<P>\n", NULL);
}
break;
}
if (recursive_error) {
ap_rvputs(r, "<P>Additionally, a ",
status_lines[ap_index_of_response(recursive_error)],
"\nerror was encountered while trying to use an "
"ErrorDocument to handle the request.\n", NULL);
}
ap_rputs(ap_psignature("<HR>\n", r), r);
ap_rputs("</BODY></HTML>\n", r);
}
ap_kill_timeout(r);
ap_finalize_request_protocol(r);
ap_rflush(r);
}