akosut 96/07/28 12:27:58
Modified: src buff.c buff.h http_config.h http_core.c
http_protocol.c http_protocol.h http_request.c
httpd.h mod_actions.c mod_alias.c mod_cgi.c
mod_include.c mod_negotiation.c mod_proxy.c util.c
Log:
Make Apache unconditionally compliant with all HTTP/1.1 features and
requirements, as of draft -06.
Revision Changes Path
1.5 +40 -4 apache/src/buff.c
Index: buff.c
===================================================================
RCS file: /export/home/cvs/apache/src/buff.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -C3 -r1.4 -r1.5
*** buff.c 1996/07/25 19:32:26 1.4
--- buff.c 1996/07/28 19:27:41 1.5
***************
*** 1,3 ****
--- 1,4 ----
+
/* ====================================================================
* Copyright (c) 1996 The Apache Group. All rights reserved.
*
***************
*** 173,178 ****
--- 174,193 ----
}
/*
+ * Set a flag on (1) or off (0). Currently, these flags work
+ * as a function of the bcwrite() function, so we make sure to
+ * flush before setting them one way or the other; otherwise
+ * writes could end up with the wrong flag.
+ */
+ int bsetflag(BUFF *fb, int flag, int value)
+ {
+ bflush(fb);
+ if (value) fb->flags |= flag;
+ else fb->flags &= ~flag;
+ return value;
+ }
+
+ /*
* Read up to nbyte bytes into buf.
* If fewer than byte bytes are currently available, then return those.
* Returns 0 for EOF, -1 for error.
***************
*** 410,415 ****
--- 425,451 ----
}
/*
+ * A hook to write() that deals with chunking. This is really a protocol-
+ * level issue, but we deal with it here because it's simpler; this is
+ * an interim solution pending a complete rewrite of all this stuff in
+ * 2.0, using something like sfio stacked disciplines or BSD's funopen().
+ */
+ int bcwrite(BUFF *fb, const void *buf, int nbyte) {
+ int r;
+
+ if (fb->flags & B_CHUNK) {
+ char chunksize[16]; /* Big enough for practically anything */
+
+ sprintf(chunksize, "%x\015\012", nbyte);
+ write(fb->fd, chunksize, strlen(chunksize));
+ }
+ r = write(fb->fd, buf, nbyte);
+ if ((r > 0) && (fb->flags & B_CHUNK))
+ write(fb->fd, "\015\012", 2);
+ return r;
+ }
+
+ /*
* Write nbyte bytes.
* Only returns fewer than nbyte if an error ocurred.
* Returns -1 if no bytes were written before the error ocurred.
***************
*** 425,431 ****
if (!(fb->flags & B_WR))
{
/* unbuffered write */
! do i = write(fb->fd, buf, nbyte);
while (i == -1 && errno == EINTR);
if (i > 0) fb->bytes_sent += i;
if (i == 0)
--- 461,467 ----
if (!(fb->flags & B_WR))
{
/* unbuffered write */
! do i = bcwrite(fb, buf, nbyte);
while (i == -1 && errno == EINTR);
if (i > 0) fb->bytes_sent += i;
if (i == 0)
***************
*** 458,464 ****
}
/* the buffer must be full */
! do i = write(fb->fd, fb->outbase, fb->bufsiz);
while (i == -1 && errno == EINTR);
if (i > 0) fb->bytes_sent += i;
if (i == 0)
--- 494,500 ----
}
/* the buffer must be full */
! do i = bcwrite(fb, fb->outbase, fb->bufsiz);
while (i == -1 && errno == EINTR);
if (i > 0) fb->bytes_sent += i;
if (i == 0)
***************
*** 494,500 ****
*/
while (nbyte > fb->bufsiz)
{
! do i = write(fb->fd, buf, nbyte);
while (i == -1 && errno == EINTR);
if (i > 0) fb->bytes_sent += i;
if (i == 0)
--- 530,536 ----
*/
while (nbyte > fb->bufsiz)
{
! do i = bcwrite(fb, buf, nbyte);
while (i == -1 && errno == EINTR);
if (i > 0) fb->bytes_sent += i;
if (i == 0)
***************
*** 540,546 ****
{
/* the buffer must be full */
j = fb->outcnt;
! do i = write(fb->fd, fb->outbase, fb->outcnt);
while (i == -1 && errno == EINTR);
if (i > 0) fb->bytes_sent += i;
if (i == 0)
--- 576,582 ----
{
/* the buffer must be full */
j = fb->outcnt;
! do i = bcwrite(fb, fb->outbase, fb->outcnt);
while (i == -1 && errno == EINTR);
if (i > 0) fb->bytes_sent += i;
if (i == 0)
1.5 +5 -0 apache/src/buff.h
Index: buff.h
===================================================================
RCS file: /export/home/cvs/apache/src/buff.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -C3 -r1.4 -r1.5
*** buff.h 1996/05/27 21:08:27 1.4
--- buff.h 1996/07/28 19:27:42 1.5
***************
*** 66,71 ****
--- 66,73 ----
/* A write error has occurred */
#define B_WRERR (32)
#define B_ERROR (48)
+ /* Use chunked writing */
+ #define B_CHUNK (64)
typedef struct buff_struct BUFF;
***************
*** 98,104 ****
--- 100,109 ----
extern void bpushfd(BUFF *fb, int fd_in, int fd_out);
extern int bsetopt(BUFF *fb, int optname, const void *optval);
extern int bgetopt(BUFF *fb, int optname, void *optval);
+ extern int bsetflag(BUFF *fb, int flag, int value);
extern int bclose(BUFF *fb);
+
+ #define bgetflag(fb, flag) ((fb)->flags & (flag))
/* Error handling */
extern void bonerror(BUFF *fb, void (*error)(BUFF *, int, void *),
1.9 +1 -1 apache/src/http_config.h
Index: http_config.h
===================================================================
RCS file: /export/home/cvs/apache/src/http_config.h,v
retrieving revision 1.8
retrieving revision 1.9
diff -C3 -r1.8 -r1.9
*** http_config.h 1996/07/27 13:08:28 1.8
--- http_config.h 1996/07/28 19:27:43 1.9
***************
*** 201,207 ****
* handle it back-compatibly, or at least signal an error).
*/
! #define MODULE_MAGIC_NUMBER 19960526
#define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER, 0, __FILE__, NULL
/* Generic accessors for other modules to get at their own module-specific
--- 201,207 ----
* handle it back-compatibly, or at least signal an error).
*/
! #define MODULE_MAGIC_NUMBER 19960725
#define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER, 0, __FILE__, NULL
/* Generic accessors for other modules to get at their own module-specific
1.25 +32 -11 apache/src/http_core.c
Index: http_core.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_core.c,v
retrieving revision 1.24
retrieving revision 1.25
diff -C3 -r1.24 -r1.25
*** http_core.c 1996/07/27 23:00:06 1.24
--- http_core.c 1996/07/28 19:27:43 1.25
***************
*** 525,530 ****
--- 525,531 ----
else if(!strcasecmp(method,"POST")) limited |= (1 << M_POST);
else if(!strcasecmp(method,"DELETE")) limited |= (1 << M_DELETE);
else if(!strcasecmp(method,"CONNECT")) limited |= (1 << M_CONNECT);
+ else if(!strcasecmp(method,"OPTIONS")) limited |= (1 << M_OPTIONS);
else return "unknown method in <Limit>";
}
***************
*** 1063,1069 ****
core_server_config *conf = get_module_config (sconf, &core_module);
if (r->proxyreq) return NOT_IMPLEMENTED;
! if (r->uri[0] != '/') return BAD_REQUEST;
if (r->server->path &&
!strncmp(r->uri, r->server->path, r->server->pathlen))
--- 1064,1070 ----
core_server_config *conf = get_module_config (sconf, &core_module);
if (r->proxyreq) return NOT_IMPLEMENTED;
! if ((r->uri[0] != '/') && strcmp(r->uri, "*")) return BAD_REQUEST;
if (r->server->path &&
!strncmp(r->uri, r->server->path, r->server->pathlen))
***************
*** 1079,1105 ****
/*
* Default handler for MIME types without other handlers. Only GET
! * at this point... anyone who wants to write a generic handler for
! * PUT or POST is free to do so, but it seems unwise to provide any
! * defaults yet...
*/
int default_handler (request_rec *r)
{
core_dir_config *d =
(core_dir_config *)get_module_config(r->per_dir_config, &core_module);
! int errstatus;
FILE *f;
! if (r->method_number != M_GET) return DECLINED;
if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) {
log_reason("File does not exist", r->filename, r);
return NOT_FOUND;
}
! if ((errstatus = set_content_length (r, r->finfo.st_size))
! || (errstatus = set_last_modified (r, r->finfo.st_mtime)))
return errstatus;
#ifdef __EMX__
--- 1080,1113 ----
/*
* Default handler for MIME types without other handlers. Only GET
! * and OPTIONS at this point... anyone who wants to write a generic
! * handler for PUT or POST is free to do so, but it seems unwise to provide
! * any defaults yet... So, for now, we assume that this will always be
! * the last handler called and return 405 or 501.
*/
int default_handler (request_rec *r)
{
core_dir_config *d =
(core_dir_config *)get_module_config(r->per_dir_config, &core_module);
! int rangestatus, errstatus;
FILE *f;
! r->allowed |= (1 << M_GET);
! r->allowed |= (1 << M_TRACE);
! r->allowed |= (1 << M_OPTIONS);
!
! if (r->method_number == M_INVALID) return NOT_IMPLEMENTED;
! if (r->method_number == M_OPTIONS) return send_http_options(r);
! if (r->method_number != M_GET) return METHOD_NOT_ALLOWED;
if (r->finfo.st_mode == 0 || (r->path_info && *r->path_info)) {
log_reason("File does not exist", r->filename, r);
return NOT_FOUND;
}
! if ((errstatus = set_last_modified (r, r->finfo.st_mtime))
! || (errstatus = set_content_length (r, r->finfo.st_size)))
return errstatus;
#ifdef __EMX__
***************
*** 1119,1128 ****
}
soft_timeout ("send", r);
!
send_http_header (r);
! if (!r->header_only) send_fd (f, r);
! fclose (f);
return OK;
}
--- 1127,1149 ----
}
soft_timeout ("send", r);
!
! rangestatus = set_byterange(r);
send_http_header (r);
!
! if (!r->header_only) {
! if (!rangestatus)
! send_fd (f, r);
! else {
! long offset, length;
! while (each_byterange(r, &offset, &length)) {
! fseek(f, offset, SEEK_SET);
! send_fd_length(f, r, length);
! }
! }
! }
!
! fclose(f);
return OK;
}
1.30 +484 -76 apache/src/http_protocol.c
Index: http_protocol.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.c,v
retrieving revision 1.29
retrieving revision 1.30
diff -C3 -r1.29 -r1.30
*** http_protocol.c 1996/07/27 04:35:05 1.29
--- http_protocol.c 1996/07/28 19:27:44 1.30
***************
*** 1,4 ****
!
/* ====================================================================
* Copyright (c) 1995 The Apache Group. All rights reserved.
*
--- 1,4 ----
!
/* ====================================================================
* Copyright (c) 1995 The Apache Group. All rights reserved.
*
***************
*** 50,57 ****
* project, please see <http://www.apache.org/>.
*
*/
!
!
/*
* http_protocol.c --- routines which directly communicate with the
* client.
--- 50,56 ----
* project, please see <http://www.apache.org/>.
*
*/
!
/*
* http_protocol.c --- routines which directly communicate with the
* client.
***************
*** 146,213 ****
return 1;
}
int set_content_length (request_rec *r, long clength)
{
char ts[MAX_STRING_LEN];
!
sprintf (ts, "%ld", clength);
! table_set (r->headers_out, "Content-length", pstrdup (r->pool, ts));
return 0;
}
int set_keepalive(request_rec *r)
{
! char *conn = table_get (r->headers_in, "Connection");
! char *length = table_get (r->headers_out, "Content-length");
! if (conn && length && !strncasecmp(conn, "Keep-Alive", 10) &&
! r->server->keep_alive_timeout &&
! (r->server->keep_alive > r->connection->keepalives)) {
! char header[26];
! int left = r->server->keep_alive - r->connection->keepalives;
!
! r->connection->keepalive = 1;
! r->connection->keepalives++;
! sprintf(header, "timeout=%d, max=%d", r->server->keep_alive_timeout,
! left);
! table_set (r->headers_out, "Connection", "Keep-Alive");
! table_set (r->headers_out, "Keep-Alive", pstrdup(r->pool, header));
! return 1;
! }
! return 0;
}
int set_last_modified(request_rec *r, time_t mtime)
{
! char *ts;
! char *if_modified_since = table_get (r->headers_in,
"If-modified-since");
! /* Cacheing proxies use the absence of a Last-modified header
! * to indicate that a document is dynamic and shouldn't be cached.
! * For the moment, we enforce that here, though it would probably
! * work just as well to generate an Expires: header in send_http_header.
! *
! * However, even in that case, if no_cache is set, we would *not*
! * want to send USE_LOCAL_COPY, since the client isn't *supposed*
! * to have it cached.
*/
!
if (r->no_cache) return OK;
-
- ts = gm_timestr_822(r->pool, mtime);
- table_set (r->headers_out, "Last-modified", ts);
/* Check for conditional GETs --- note that we only want this check
* to succeed if the GET was successful; ErrorDocuments *always* get
sent.
*/
! if (r->status == 200 &&
! if_modified_since && later_than(gmtime(&mtime), if_modified_since))
!
return USE_LOCAL_COPY;
else
return OK;
}
--- 145,380 ----
return 1;
}
+ static int parse_byterange (char *range, long clength, long *start, long
*end)
+ {
+ char *dash = strchr(range, '-');
+
+ if (!dash)
+ return 0;
+
+ if ((dash == range)) {
+ /* In the form "-5" */
+ *start = clength - atol(dash + 1);
+ *end = clength - 1;
+ }
+ else {
+ *dash = '\0';
+ dash++;
+ *start = atol(range);
+ if (*dash)
+ *end = atol(dash);
+ else /* "5-" */
+ *end = clength -1;
+ }
+
+ if (*start > *end)
+ return 0;
+
+ if (*end >= clength)
+ *end = clength - 1;
+
+ return 1;
+ }
+
+ /* This is a string I made up. Pounded on the keyboard a couple of times.
+ * It's a good a way as any, I suppose, if you can't parse the document
+ * beforehand (which we can't).
+ */
+
+ #define BYTERANGE_BOUNDARY "13962mx38v144c9999AQdk39d2Klmx79"
+
+ int set_byterange (request_rec *r)
+ {
+ char *range = table_get (r->headers_in, "Range");
+ char *if_range = table_get (r->headers_in, "If-Range");
+ char ts[MAX_STRING_LEN], *match;
+ long range_start, range_end;
+
+ /* Reasons we won't do ranges... */
+
+ if (!r->clength || r->assbackwards) return 0;
+ if (!range || strncmp(range, "bytes=", 6)) {
+ table_set (r->headers_out, "Accept-Ranges", "bytes");
+ return 0;
+ }
+
+ /* Check the If-Range header. Golly, this is a long if statement */
+
+ if (if_range
+ && !((if_range[0] == '"') /* an entity tag */
+ && (match = table_get(r->headers_out, "Etag"))
+ && (match[0] == '"') && !strcasecmp(if_range, match))
+ && !((if_range[0] != '"') /* a date */
+ && (match = table_get(r->headers_out, "Last-Modified"))
+ && (!strcasecmp(if_range, match))))
+ return 0;
+
+ if (!strchr(range, ',')) {
+ /* A single range */
+ if (!parse_byterange(pstrdup(r->pool, range + 6), r->clength,
+ &range_start, &range_end))
+ return 0;
+
+ r->byterange = 1;
+
+ sprintf(ts, "bytes %ld-%ld/%ld", range_start, range_end,
+ r->clength);
+ table_set(r->headers_out, "Content-Range",
+ pstrdup(r->pool, ts));
+ sprintf(ts, "%ld", range_end - range_start + 1);
+ table_set(r->headers_out, "Content-Length", ts);
+ }
+ else {
+ /* a multiple range */
+ r->byterange = 2;
+ table_unset(r->headers_out, "Content-Length");
+ }
+
+ r->status = PARTIAL_CONTENT;
+ r->range = range + 6;
+
+ return 1;
+ }
+
+ int each_byterange (request_rec *r, long *offset, long *length) {
+ long range_start, range_end;
+ char *range;
+
+ if (!*r->range) {
+ if (r->byterange > 1)
+ rvputs(r, "\015\012--", BYTERANGE_BOUNDARY, "--\015\012", NULL);
+ return 0;
+ }
+
+ range = getword(r->pool, &r->range, ',');
+ if (!parse_byterange(range, r->clength, &range_start, &range_end))
+ return each_byterange(r, offset, length); /* Skip this one */
+
+ if (r->byterange > 1) {
+ char *ct = r->content_type ? r->content_type : default_type(r);
+ char ts[MAX_STRING_LEN];
+
+ sprintf(ts, "%ld-%ld/%ld", range_start, range_end, r->clength);
+ rvputs(r, "\015\012--", BYTERANGE_BOUNDARY, "\015\012Content-type: ",
+ ct, "\015\012Content-range: bytes ", ts, "\015\012\015\012",
+ NULL);
+ }
+
+ *offset = range_start;
+ *length = range_end - range_start + 1;
+ return 1;
+ }
int set_content_length (request_rec *r, long clength)
{
char ts[MAX_STRING_LEN];
!
! r->clength = clength;
!
sprintf (ts, "%ld", clength);
! table_set (r->headers_out, "Content-Length", pstrdup (r->pool, ts));
!
return 0;
}
int set_keepalive(request_rec *r)
{
! char *conn = table_get (r->headers_in, "Connection");
! char *length = table_get (r->headers_out, "Content-length");
! #ifdef FORHTTP11
! if ((((r->proto_num >= 1001) && (!find_token(r->pool, conn, "close")))
! || (find_token(r->pool, conn, "keep-alive")))
! #else
! if ((find_token(r->pool, conn, "keep-alive"))
! #endif
! && (r->header_only || length ||
! ((r->proto_num >= 1001) && (r->byterange > 1 || (r->chunked = 1))))
! && (r->server->keep_alive_timeout &&
! (r->server->keep_alive > r->connection->keepalives))) {
! char header[26];
! int left = r->server->keep_alive - r->connection->keepalives;
!
! r->connection->keepalive = 1;
! r->connection->keepalives++;
!
! #ifdef FORHTTP11
! if (r->proto_num < 1001) {
! #endif
! sprintf(header, "timeout=%d, max=%d",
! r->server->keep_alive_timeout, left);
! table_set (r->headers_out, "Connection", "Keep-Alive");
! table_set (r->headers_out, "Keep-Alive", pstrdup(r->pool, header));
! #ifdef FORHTTP11
! }
! #endif
! return 1;
! }
!
! /* We only really need to send this to HTTP/1.1 clients, but we
! * always send it anyway, because a broken proxy may identify itself
! * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
! * to a HTTP/1.1 client. Better safe than sorry.
! */
! table_set (r->headers_out, "Connection", "close");
! return 0;
}
int set_last_modified(request_rec *r, time_t mtime)
{
! char *ts, etag[MAX_STRING_LEN];
! char *if_modified_since = table_get (r->headers_in,
"If-Modified-Since");
! char *if_unmodified = table_get (r->headers_in, "If-Unmodified-Since");
! char *if_nonematch = table_get (r->headers_in, "If-None-Match");
! char *if_match = table_get (r->headers_in, "If-Match");
!
! /* Invalid, future time... just ignore it */
! if (mtime > r->request_time) return OK;
! ts = gm_timestr_822(r->pool, mtime);
! table_set (r->headers_out, "Last-Modified", ts);
!
! /* Make an ETag header out of various peices of information. We use
! * the last-modified date and, if we have a real file, the
! * length and inode number - note that this doesn't have to match
! * the content-length (i.e. includes), it just has to be unique
! * for the file.
*/
!
! if (r->finfo.st_mode != 0)
! sprintf(etag, "\"%lx-%lx-%lx\"", r->finfo.st_ino, r->finfo.st_size,
! mtime);
! else
! sprintf(etag, "\"%lx\"", mtime);
! table_set (r->headers_out, "ETag", etag);
!
! /* We now do the no_cache stuff using an Expires: header (we used to
! * withhold Last-modified). However, we still want to enforce this by
! * not allowing conditional GETs.
! */
!
if (r->no_cache) return OK;
/* Check for conditional GETs --- note that we only want this check
* to succeed if the GET was successful; ErrorDocuments *always* get
sent.
*/
! if ((r->status < 200) || (r->status >= 300))
! return OK;
!
! if (if_modified_since && later_than(gmtime(&mtime), if_modified_since))
return USE_LOCAL_COPY;
+ else if (if_unmodified && !later_than(gmtime(&mtime), if_unmodified))
+ return PRECONDITION_FAILED;
+ else if (if_nonematch && ((if_nonematch[0] == '*') ||
+ find_token(r->pool, if_nonematch, etag)))
+ return (r->method_number == M_GET) ?
+ USE_LOCAL_COPY : PRECONDITION_FAILED;
+ else if (if_match && !((if_match[0] == '*') ||
+ find_token(r->pool, if_match, etag)))
+ return PRECONDITION_FAILED;
else
return OK;
}
***************
*** 231,245 ****
void parse_uri (request_rec *r, char *uri)
{
const char *s;
- /* If we ever want to do byte-ranges a la Netscape & Franks,
- * this is the place to parse them; with proper support in
- * rprintf and rputc, and the sub-request setup and finalizers
- * here, it'll all just work, even for vile cases like
- * inclusion of byte-ranges of the output of CGI scripts, with
- * the client requesting only a byte-range of *that*!
- *
- * But for now...
- */
#ifdef __EMX__
/* Variable for OS/2 fix below. */
--- 398,403 ----
***************
*** 255,262 ****
r->proxyreq = 1;
r->uri = uri;
r->args = NULL;
! } else
! {
r->proxyreq = 0;
r->uri = getword (r->pool, &uri, '?');
--- 413,425 ----
r->proxyreq = 1;
r->uri = uri;
r->args = NULL;
! }
! else if (r->method && !strcmp(r->method, "TRACE")) {
! r->proxyreq = 0;
! r->uri = uri;
! r->args = NULL;
! }
! else {
r->proxyreq = 0;
r->uri = getword (r->pool, &uri, '?');
***************
*** 373,379 ****
void check_hostalias (request_rec *r) {
char *host = getword(r->pool, &r->hostname, ':'); /* Get rid of port */
! int port = (*r->hostname) ? atoi(r->hostname) : 0;
server_rec *s;
if (port && (port != r->server->port))
--- 536,542 ----
void check_hostalias (request_rec *r) {
char *host = getword(r->pool, &r->hostname, ':'); /* Get rid of port */
! int port = (*r->hostname) ? atoi(r->hostname) : 80;
server_rec *s;
if (port && (port != r->server->port))
***************
*** 388,395 ****
for (s = r->server->next; s; s = s->next) {
char *names = s->names;
! if ((!strcasecmp(host, s->server_hostname)) &&
! (!port || (port == s->port))) {
r->server = r->connection->server = s;
if (r->hostlen && !strncmp(r->uri, "http://", 7)) {
r->uri += r->hostlen;
--- 551,557 ----
for (s = r->server->next; s; s = s->next) {
char *names = s->names;
! if ((!strcasecmp(host, s->server_hostname)) && (port == s->port)) {
r->server = r->connection->server = s;
if (r->hostlen && !strncmp(r->uri, "http://", 7)) {
r->uri += r->hostlen;
***************
*** 431,436 ****
--- 593,600 ----
request_rec *read_request (conn_rec *conn)
{
request_rec *r = (request_rec *)pcalloc (conn->pool,
sizeof(request_rec));
+
+ r->request_time = time(NULL);
r->connection = conn;
r->server = conn->server;
***************
*** 462,468 ****
hard_timeout ("read", r);
if (!read_request_line (r)) return NULL;
! if (!r->assbackwards) get_mime_headers(r);
/* handle Host header here, to get virtual server */
--- 626,632 ----
hard_timeout ("read", r);
if (!read_request_line (r)) return NULL;
! if (!r->assbackwards) get_mime_headers (r);
/* handle Host header here, to get virtual server */
***************
*** 488,493 ****
--- 652,661 ----
r->method_number = M_DELETE;
else if(!strcmp(r->method,"CONNECT"))
r->method_number = M_CONNECT;
+ else if(!strcmp(r->method,"OPTIONS"))
+ r->method_number = M_OPTIONS;
+ else if(!strcmp(r->method,"TRACE"))
+ r->method_number = M_TRACE;
else
r->method_number = M_INVALID; /* Will eventually croak. */
***************
*** 550,556 ****
{
char nonce[10];
! sprintf(nonce, "%lu", time(NULL));
table_set (r->err_headers_out, "WWW-Authenticate",
pstrcat(r->pool, "Digest realm=\"", auth_name(r),
"\", nonce=\"", nonce, "\"", NULL));
--- 718,724 ----
{
char nonce[10];
! sprintf(nonce, "%lu", r->request_time);
table_set (r->err_headers_out, "WWW-Authenticate",
pstrcat(r->pool, "Digest realm=\"", auth_name(r),
"\", nonce=\"", nonce, "\"", NULL));
***************
*** 590,596 ****
return OK;
}
! #define RESPONSE_CODE_LIST " 200 302 304 400 401 403 404 500 503 501 502 "
/* New Apache routine to map error responses into array indicies
* e.g. 400 -> 0, 500 -> 1, 502 -> 2 ...
--- 758,764 ----
return OK;
}
! #define RESPONSE_CODE_LIST " 200 206 301 302 304 400 401 403 404 405 411
412 500 503 501 502 "
/* New Apache routine to map error responses into array indicies
* e.g. 400 -> 0, 500 -> 1, 502 -> 2 ...
***************
*** 599,610 ****
--- 767,783 ----
char *status_lines[] = {
"200 OK",
+ "206 Partial Content",
+ "301 Moved Permanently",
"302 Found",
"304 Not Modified",
"400 Bad Request",
"401 Unauthorized",
"403 Forbidden",
"404 Not found",
+ "405 Method Not Allowed",
+ "411 Length Required",
+ "412 Precondition Failed",
"500 Server error",
"503 Out of resources",
"501 Not Implemented",
***************
*** 613,624 ****
--- 786,802 ----
char *response_titles[] = {
"200 OK", /* Never actually sent, barring
die(200,...) */
+ "206 Partial Content", /* Never sent as an error (we hope) */
+ "Document moved", /* 301 Redirect */
"Document moved", /* 302 Redirect */
"304 Not Modified", /* Never sent... 304 MUST be header
only */
"Bad Request",
"Authorization Required",
"Forbidden",
"File Not found",
+ "Method Not Allowed",
+ "Length Required",
+ "Precondition Failed",
"Server Error",
"Out of resources",
"Method not implemented",
***************
*** 654,660 ****
r->status_line = status_lines[index_of_response(r->status)];
bvputs(fd, SERVER_PROTOCOL, " ", r->status_line, "\015\012", NULL);
! bvputs(fd,"Date: ",gm_timestr_822 (r->pool, time(NULL)), "\015\012",
NULL);
bvputs(fd,"Server: ", SERVER_VERSION, "\015\012", NULL);
}
--- 832,839 ----
r->status_line = status_lines[index_of_response(r->status)];
bvputs(fd, SERVER_PROTOCOL, " ", r->status_line, "\015\012", NULL);
! bvputs(fd,"Date: ",gm_timestr_822 (r->pool, r->request_time),
! "\015\012", NULL);
bvputs(fd,"Server: ", SERVER_VERSION, "\015\012", NULL);
}
***************
*** 683,688 ****
--- 862,928 ----
#endif
}
+ char *make_allow(request_rec *r)
+ {
+ int allowed = r->allowed;
+
+ return 2 + pstrcat(r->pool, (allowed & (1 << M_GET)) ? ", GET" : "",
+ (allowed & (1 << M_POST)) ? ", POST" : "",
+ (allowed & (1 << M_PUT)) ? ", PUT" : "",
+ (allowed & (1 << M_DELETE)) ? ", DELETE" : "",
+ (allowed & (1 << M_OPTIONS)) ? ", OPTIONS" : "",
+ (allowed & (1 << M_TRACE)) ? ", TRACE" : "",
+ NULL);
+
+ }
+
+ int send_http_trace (request_rec *r)
+ {
+ array_header *hdrs_arr = table_elts(r->headers_in);
+ table_entry *hdrs = (table_entry *)hdrs_arr->elts;
+ int i;
+
+ /* Get the original request */
+ while (r->prev) r = r->prev;
+
+ soft_timeout ("send", r);
+
+ r->content_type = "message/http";
+ send_http_header(r);
+
+ /* Now we recreate the request, and echo it back */
+
+ rvputs(r, r->method, " ", r->uri, " ", r->protocol, "\015\012", NULL);
+
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (!hdrs[i].key) continue;
+ rvputs(r, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL);
+ }
+
+ kill_timeout(r);
+ return OK;
+ }
+
+ int send_http_options(request_rec *r)
+ {
+ BUFF *fd = r->connection->client;
+ const long int zero=0L;
+
+ if (r->assbackwards) return DECLINED;
+
+ soft_timeout ("send", r);
+
+ basic_http_header(r);
+ bputs("Connection: close\015\012", fd);
+ bvputs(fd, "Allow: ", make_allow(r), "\015\012", NULL);
+ bputs("\015\012", fd);
+
+ bsetopt(fd, BO_BYTECT, &zero);
+ kill_timeout (r);
+
+ return OK;
+ }
+
void send_http_header(request_rec *r)
{
conn_rec *c = r->connection;
***************
*** 705,727 ****
basic_http_header (r);
set_keepalive (r);
!
! if (r->content_type)
! bvputs(fd, "Content-type: ",
nuke_mime_parms (r->pool, r->content_type), "\015\012", NULL);
! else if(default_type)
! bvputs(fd, "Content-type: ", default_type, "\015\012", NULL);
if (r->content_encoding)
! bvputs(fd,"Content-encoding: ", r->content_encoding, "\015\012",
NULL);
if (r->content_language)
! bvputs(fd,"Content-language: ", r->content_language, "\015\012",
NULL);
!
hdrs_arr = table_elts(r->headers_out);
hdrs = (table_entry *)hdrs_arr->elts;
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (!hdrs[i].key) continue;
bvputs(fd, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL);
}
--- 945,989 ----
basic_http_header (r);
set_keepalive (r);
!
! if (r->chunked)
! bputs("Transfer-Encoding: chunked\015\012", fd);
!
! if (r->byterange > 1)
! bvputs(fd, "Content-Type: multipart/byteranges; boundary=",
! BYTERANGE_BOUNDARY, "\015\012", NULL);
! else if (r->content_type)
! bvputs(fd, "Content-Type: ",
nuke_mime_parms (r->pool, r->content_type), "\015\012", NULL);
! else if (default_type)
! bvputs(fd, "Content-Type: ", default_type, "\015\012", NULL);
if (r->content_encoding)
! bvputs(fd,"Content-Encoding: ", r->content_encoding, "\015\012",
NULL);
if (r->content_language)
! bvputs(fd,"Content-Language: ", r->content_language, "\015\012",
NULL);
!
! /* If it's a TRACE, or if they sent us Via:, send one back */
! if ((r->method_number == M_TRACE) || table_get(r->headers_in, "Via"))
! bvputs(fd, "Via: ",
! SERVER_PROTOCOL + (!strncmp(SERVER_PROTOCOL, "HTTP/", 5) ? 5 : 0), " ",
! construct_server(r->pool, r->server->server_hostname, r->server->port),
! " (", SERVER_VERSION, ")\015\012", NULL);
!
! /* We now worry about this here */
!
! if (r->no_cache && (r->proto_num >= 1001))
! bputs ("Cache-Control: private\015\012", fd);
! else if (r->no_cache)
! bvputs(fd,"Expires: ", gm_timestr_822(r->pool, r->request_time),
! "\015\012", NULL);
!
hdrs_arr = table_elts(r->headers_out);
hdrs = (table_entry *)hdrs_arr->elts;
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (!hdrs[i].key) continue;
+ if (r->no_cache && !strcasecmp(hdrs[i].key, "Expires")) continue;
bvputs(fd, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL);
}
***************
*** 729,761 ****
hdrs = (table_entry *)hdrs_arr->elts;
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (!hdrs[i].key) continue;
bvputs(fd, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL);
}
bputs("\015\012",fd);
if (c->keepalive)
! bflush(r->connection->client); /* For bugs in Netscape, perhaps */
bsetopt(fd, BO_BYTECT, &zero);
r->sent_bodyct = 1; /* Whatever follows is real body
stuff... */
}
long read_client_block (request_rec *r, char *buffer, int bufsiz)
{
! return bread(r->connection->client, buffer, bufsiz);
}
! long send_fd(FILE *f, request_rec *r)
{
char buf[IOBUFSIZE];
long total_bytes_sent;
! register int n,o,w;
conn_rec *c = r->connection;
total_bytes_sent = 0;
while (!r->connection->aborted) {
! while ((n= fread(buf, sizeof(char), IOBUFSIZE, f)) < 1
&& ferror(f) && errno == EINTR)
continue;
--- 991,1134 ----
hdrs = (table_entry *)hdrs_arr->elts;
for (i = 0; i < hdrs_arr->nelts; ++i) {
if (!hdrs[i].key) continue;
+ if (r->no_cache && !strcasecmp(hdrs[i].key, "Expires")) continue;
bvputs(fd, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL);
}
bputs("\015\012",fd);
if (c->keepalive)
! bflush(fd); /* This is to work around a Netscape bug */
bsetopt(fd, BO_BYTECT, &zero);
r->sent_bodyct = 1; /* Whatever follows is real body
stuff... */
+
+ /* Set buffer flags for the body */
+ if (r->chunked) bsetflag(fd, B_CHUNK, 1);
+ }
+
+ void finalize_request_protocol (request_rec *r) {
+ BUFF *fd = r->connection->client;
+
+ /* Turn off chunked encoding */
+
+ if (r->chunked) {
+ bsetflag(fd, B_CHUNK, 0);
+ bputs("0\015\012", fd);
+ /* If we had footer "headers", we'd send them now */
+ bputs("\015\012", fd);
+ }
+
+ }
+
+ int setup_client_block (request_rec *r)
+ {
+ char *tenc = table_get (r->headers_in, "Transfer-Encoding");
+ char *lenp = table_get (r->headers_in, "Content-length");
+
+ if ((r->method_number != M_POST) && (r->method_number != M_PUT))
+ return OK;
+
+ if (tenc) {
+ if (strcasecmp(tenc, "chunked")) {
+ log_printf(r->server, "Unknown Transfer-Encoding %s", tenc);
+ return BAD_REQUEST;
+ }
+ r->read_chunked = 1;
+ }
+ else {
+ if (!lenp) {
+ log_reason("POST or PUT without Content-length:", r->filename, r);
+ return LENGTH_REQUIRED;
+ }
+ r->remaining = atol(lenp);
+ }
+
+ return OK;
+ }
+
+ int should_client_block (request_rec *r) {
+ return (r->method_number == M_POST || r->method_number == M_PUT);
+ }
+
+ static int rd_chunk_size (BUFF *b) {
+ int chunksize = 0;
+ int c;
+
+ while ((c = bgetc (b)) != EOF && isxdigit (c)) {
+ int xvalue;
+
+ if (c >= '0' && c <= '9') xvalue = c - '0';
+ else if (c >= 'A' && c <= 'F') xvalue = c - 'A' + 0xa;
+ else if (c >= 'a' && c <= 'f') xvalue = c - 'a' + 0xa;
+
+ chunksize = (chunksize << 4) | xvalue;
+ }
+
+ /* Skip to end of line, bypassing chunk options, if present */
+
+ while (c != '\n' && c != EOF)
+ c = bgetc (b);
+
+ return (c == EOF) ? -1 : chunksize;
}
long read_client_block (request_rec *r, char *buffer, int bufsiz)
{
! long c, len_read, len_to_read = r->remaining;
!
! if (!r->read_chunked) { /* Content-length read */
! if (len_to_read >= bufsiz)
! len_to_read = bufsiz - 1;
! len_read = bread(r->connection->client, buffer, len_to_read);
! r->remaining -= len_read;
! return len_read;
! }
!
! /* Handle chunked reading */
! if (len_to_read == 0) {
! len_to_read = rd_chunk_size(r->connection->client);
! if (len_to_read == 0) {
! /* Skip over any "footers" */
! do c = bgets(buffer, bufsiz, r->connection->client);
! while ((c > 0) && (*buffer != '\015') && (*buffer != '\012'));
! return 0;
! }
! }
! if (len_to_read >= bufsiz) {
! r->remaining = len_to_read - bufsiz - 1;
! len_to_read = bufsiz - 1;
! }
! else
! r->remaining = 0;
!
! len_read = bread(r->connection->client, buffer, len_to_read);
! if (r->remaining == 0) {
! do c = bgetc (r->connection->client);
! while (c != '\n' && c != EOF);
! }
!
! return len_read;
}
! long send_fd(FILE *f, request_rec *r) { return send_fd_length(f, r, -1); }
!
! long send_fd_length(FILE *f, request_rec *r, long length)
{
char buf[IOBUFSIZE];
long total_bytes_sent;
! register int n, w, o, len;
conn_rec *c = r->connection;
+ if (length == 0) return 0;
+
total_bytes_sent = 0;
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)
continue;
***************
*** 771,780 ****
break;
reset_timeout(r); /* reset timeout after successfule write */
n-=w;
! o+=w;
}
}
! bflush(c->client);
SET_BYTES_SENT(r);
return total_bytes_sent;
--- 1144,1154 ----
break;
reset_timeout(r); /* reset timeout after successfule write */
n-=w;
! o+=w;
}
}
!
! if (length > 0) bflush(c->client);
SET_BYTES_SENT(r);
return total_bytes_sent;
***************
*** 860,873 ****
*/
if (status == USE_LOCAL_COPY) {
! if (set_keepalive(r))
! bputs("Connection: Keep-Alive\015\012", c->client);
bputs("\015\012", c->client);
return;
}
! if (status == REDIRECT)
bvputs(c->client, "Location: ", location, "\015\012", NULL);
for (i = 0; i < err_hdrs_arr->nelts; ++i) {
if (!err_hdrs[i].key) continue;
--- 1234,1266 ----
*/
if (status == USE_LOCAL_COPY) {
! char *etag = table_get(r->headers_out, "ETag");
! char *cloc = table_get(r->headers_out, "Content-Location");
! if (etag) bvputs(c->client, "ETag: ", etag, "\015\012", NULL);
! if (cloc) bvputs(c->client, "Content-Location: ", cloc,
! "\015\012", NULL);
! if (set_keepalive(r)) {
! #ifdef FORHTTP11
! if (r->proto_num < 1001)
! #endif
! bputs("Connection: Keep-Alive\015\012", c->client);
! }
! else bputs("Connection: close", c->client);
bputs("\015\012", c->client);
return;
}
+
+ /* We don't want persistent connections here, for several reasons.
+ * Most importantly, if there's been an error, we don't want
+ * it screwing up the next request.
+ */
+ bputs("Connection: close\015\012", c->client);
! if ((status == REDIRECT) || (status == MOVED))
bvputs(c->client, "Location: ", location, "\015\012", NULL);
+
+ if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED))
+ bvputs(c->client, "Allow: ", make_allow(r), "\015\012", NULL);
for (i = 0; i < err_hdrs_arr->nelts; ++i) {
if (!err_hdrs[i].key) continue;
***************
*** 911,916 ****
--- 1304,1310 ----
switch (r->status) {
case REDIRECT:
+ case MOVED:
bvputs(fd, "The document has moved <A HREF=\"",
escape_html(r->pool, location), "\">here</A>.<P>\n", NULL);
break;
***************
*** 932,939 ****
NULL);
break;
case NOT_FOUND:
! bvputs(fd, "The requested URL ", escape_html(r->pool, r->uri),
" was not found on this server.<P>\n", NULL);
break;
case SERVER_ERROR:
bputs("The server encountered an internal error or\n", fd);
--- 1326,1347 ----
NULL);
break;
case NOT_FOUND:
! bvputs(fd, "The requested URL ", escape_html(r->pool, r->uri),
" was not found on this server.<P>\n", NULL);
+ break;
+ case METHOD_NOT_ALLOWED:
+ bvputs(fd, "The requested method ", r->method, " is not allowed "
+ "for the URL ", escape_html(r->pool, r->uri),
+ ".<P>\n", NULL);
+ break;
+ case LENGTH_REQUIRED:
+ bvputs(fd, "A request of the requested method ", r->method,
+ " requires a valid Content-length.<P>\n", NULL);
+ break;
+ case PRECONDITION_FAILED:
+ bvputs(fd, "The requested precondition for serving the URL ",
+ escape_html(r->pool, r->uri), " evaluated to false.<P>\n",
+ NULL);
break;
case SERVER_ERROR:
bputs("The server encountered an internal error or\n", fd);
1.7 +17 -0 apache/src/http_protocol.h
Index: http_protocol.h
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.h,v
retrieving revision 1.6
retrieving revision 1.7
diff -C3 -r1.6 -r1.7
*** http_protocol.h 1996/05/27 21:08:28 1.6
--- http_protocol.h 1996/07/28 19:27:45 1.7
***************
*** 64,69 ****
--- 64,78 ----
/* Send header for http response */
void send_http_header (request_rec *l);
+
+ /* Send the response to special method requests */
+
+ int send_http_trace (request_rec *r);
+ int send_http_options (request_rec *r);
+
+ /* Finish up stuff after a request */
+
+ void finalize_request_protocol (request_rec *r);
/* Send error back to client... last arg indicates error status in case
* we get an error in the process of trying to deal with an ErrorDocument
***************
*** 99,104 ****
--- 108,114 ----
*/
long send_fd(FILE *f, request_rec *r);
+ long send_fd_length(FILE *f, request_rec *r, long length);
/* Hmmm... could macrofy these for now, and maybe forever, though the
* definitions of the macros would get a whole lot hairier.
***************
*** 121,127 ****
--- 131,144 ----
/* Reading a block of data from the client connection (e.g., POST arg) */
+ int setup_client_block (request_rec *r);
+ int should_client_block (request_rec *r);
long read_client_block (request_rec *r, char *buffer, int bufsiz);
+
+ /* Sending a byterange */
+
+ int set_byterange (request_rec *r);
+ int each_byterange (request_rec *r, long *offset, long *length);
/* Finally, this charming little number is here to encapsulate the
* degree to which nph- scripts completely escape from any discipline
1.13 +17 -2 apache/src/http_request.c
Index: http_request.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_request.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -C3 -r1.12 -r1.13
*** http_request.c 1996/07/25 19:32:29 1.12
--- http_request.c 1996/07/28 19:27:45 1.13
***************
*** 782,788 ****
return;
}
! if (!r->hostname && (r->proto_num >= 1001)) {
/* Client sent us a HTTP/1.1 or later request without telling
* us the hostname, either with a full URL or a Host: header.
* We therefore need to (as per the 1.1 spec) send an error
--- 782,789 ----
return;
}
! if ((!r->hostname && (r->proto_num >= 1001)) ||
! ((r->proto_num == 1001) && !table_get(r->headers_in, "Host"))) {
/* Client sent us a HTTP/1.1 or later request without telling
* us the hostname, either with a full URL or a Host: header.
* We therefore need to (as per the 1.1 spec) send an error
***************
*** 852,859 ****
return;
}
! if ((access_status = invoke_handler (r)) != 0)
die (access_status, r);
}
void process_request (request_rec *r)
--- 853,869 ----
return;
}
! /* We don't want TRACE to run through the normal handler set,
! * we handle it specially.
! */
! if (r->method_number == M_TRACE) send_http_trace (r);
! else if ((access_status = invoke_handler (r)) != 0) {
die (access_status, r);
+ return;
+ }
+
+ /* Take care of little things that need to happen when we're done */
+ finalize_request_protocol (r);
}
void process_request (request_rec *r)
***************
*** 913,923 ****
--- 923,938 ----
new->method = r->method;
new->method_number = r->method_number;
+ new->allowed = r->allowed;
new->status = r->status;
new->assbackwards = r->assbackwards;
new->header_only = r->header_only;
new->protocol = r->protocol;
+ new->proto_num = r->proto_num;
+ new->hostname = r->hostname;
+ new->hostlen = r->hostlen;
+ new->request_time = r->request_time;
new->main = r->main;
new->headers_in = r->headers_in;
1.40 +26 -6 apache/src/httpd.h
Index: httpd.h
===================================================================
RCS file: /export/home/cvs/apache/src/httpd.h,v
retrieving revision 1.39
retrieving revision 1.40
diff -C3 -r1.39 -r1.40
*** httpd.h 1996/07/25 19:49:02 1.39
--- httpd.h 1996/07/28 19:27:46 1.40
***************
*** 226,232 ****
#define DEFAULT_MAX_REQUESTS_PER_CHILD 0
/* If you have altered Apache and wish to change the SERVER_VERSION define
! * below, please keep to the HTTP/1.0 specification. This states that
* the identification string should consist of product tokens with an
optional
* slash and version designator. Sub-products which form a significant
part
* of the application can be listed, separated by whitespace. The tokens
--- 226,232 ----
#define DEFAULT_MAX_REQUESTS_PER_CHILD 0
/* If you have altered Apache and wish to change the SERVER_VERSION define
! * below, please keep to the HTTP specification. This states that
* the identification string should consist of product tokens with an
optional
* slash and version designator. Sub-products which form a significant
part
* of the application can be listed, separated by whitespace. The tokens
***************
*** 235,241 ****
* "Product tokens should be short and to the point -- use of them for
* advertizing or other non-essential information is explicitly forbidden."
*
! * Example: "Apache/1.1b3 MrWidget/0.1-alpha"
*/
#define SERVER_VERSION "Apache/1.2-dev" /* SEE COMMENTS ABOVE */
--- 235,241 ----
* "Product tokens should be short and to the point -- use of them for
* advertizing or other non-essential information is explicitly forbidden."
*
! * Example: "Apache/1.1.0 MrWidget/0.1-alpha"
*/
#define SERVER_VERSION "Apache/1.2-dev" /* SEE COMMENTS ABOVE */
***************
*** 249,273 ****
/* ------------------------------ error types
------------------------------ */
#define DOCUMENT_FOLLOWS 200
#define REDIRECT 302
#define USE_LOCAL_COPY 304
#define BAD_REQUEST 400
#define AUTH_REQUIRED 401
#define FORBIDDEN 403
#define NOT_FOUND 404
#define SERVER_ERROR 500
#define NOT_IMPLEMENTED 501
#define BAD_GATEWAY 502
#define HTTP_SERVICE_UNAVAILABLE 503
! #define RESPONSE_CODES 10
! #define METHODS 6
#define M_GET 0
#define M_PUT 1
#define M_POST 2
#define M_DELETE 3
#define M_CONNECT 4
! #define M_INVALID 5
#define CGI_MAGIC_TYPE "application/x-httpd-cgi"
#define INCLUDES_MAGIC_TYPE "text/x-server-parsed-html"
--- 249,280 ----
/* ------------------------------ error types
------------------------------ */
#define DOCUMENT_FOLLOWS 200
+ #define PARTIAL_CONTENT 206
+ #define MOVED 301
#define REDIRECT 302
#define USE_LOCAL_COPY 304
#define BAD_REQUEST 400
#define AUTH_REQUIRED 401
#define FORBIDDEN 403
#define NOT_FOUND 404
+ #define METHOD_NOT_ALLOWED 405
+ #define LENGTH_REQUIRED 411
+ #define PRECONDITION_FAILED 412
#define SERVER_ERROR 500
#define NOT_IMPLEMENTED 501
#define BAD_GATEWAY 502
#define HTTP_SERVICE_UNAVAILABLE 503
! #define RESPONSE_CODES 15
! #define METHODS 8
#define M_GET 0
#define M_PUT 1
#define M_POST 2
#define M_DELETE 3
#define M_CONNECT 4
! #define M_OPTIONS 5
! #define M_TRACE 6
! #define M_INVALID 7
#define CGI_MAGIC_TYPE "application/x-httpd-cgi"
#define INCLUDES_MAGIC_TYPE "text/x-server-parsed-html"
***************
*** 342,347 ****
--- 349,356 ----
char *hostname; /* Host, as set by full URI or Host: */
int hostlen; /* Length of http://host:port in full
URI */
+ time_t request_time; /* When the request started */
+
char *status_line; /* Status line, if set by script */
int status; /* In any case */
***************
*** 351,360 ****
char *method; /* GET, HEAD, POST, etc. */
int method_number; /* M_GET, M_POST, etc. */
int sent_bodyct; /* byte count in stream is for body */
long bytes_sent; /* body byte count, for easy access */
!
/* MIME header environments, in and out. Also, an array containing
* environment variables to be passed to subprocesses, so people can
* write modules to add to that environment.
--- 360,378 ----
char *method; /* GET, HEAD, POST, etc. */
int method_number; /* M_GET, M_POST, etc. */
+ int allowed; /* Allowed methods - for 405, OPTIONS,
etc */
int sent_bodyct; /* byte count in stream is for body */
long bytes_sent; /* body byte count, for easy access */
!
! int chunked; /* sending chunked transfer-coding */
! int byterange; /* number of byte ranges */
! char *range; /* The Range: header */
! long clength; /* The "real" content length */
!
! long int remaining; /* bytes left to read */
! int read_chunked; /* reading chunked transfer-coding */
!
/* MIME header environments, in and out. Also, an array containing
* environment variables to be passed to subprocesses, so people can
* write modules to add to that environment.
***************
*** 521,526 ****
--- 539,545 ----
char *getword_conf (pool *p, char **line);
char *get_token (pool *p, char **accept_line, int accept_white);
+ int find_token (pool *p, char *line, char *tok);
int is_url(char *u);
extern int unescape_url(char *url);
***************
*** 530,535 ****
--- 549,555 ----
char *os_escape_path(pool *p,const char *path,int partial);
char *escape_uri (pool *p, char *s);
extern char *escape_html(pool *p, const char *s);
+ char *construct_server(pool *p, char *hostname, int port);
char *construct_url (pool *p, char *path, server_rec *s);
char *escape_shell_cmd (pool *p, char *s);
1.5 +6 -0 apache/src/mod_actions.c
Index: mod_actions.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_actions.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -C3 -r1.4 -r1.5
*** mod_actions.c 1996/06/16 02:25:06 1.4
--- mod_actions.c 1996/07/28 19:27:46 1.5
***************
*** 156,161 ****
--- 156,167 ----
char *t, *action = r->handler ? r->handler : r->content_type;
char *script = NULL;
+ /* Set allowed stuff */
+ if (conf->get) r->allowed |= (1 << M_GET);
+ if (conf->post) r->allowed |= (1 << M_POST);
+ if (conf->put) r->allowed |= (1 << M_PUT);
+ if (conf->delete) r->allowed |= (1 << M_DELETE);
+
/* First, check for the method-handling scripts */
if ((r->method_number == M_GET) && r->args && conf->get)
script = conf->get;
1.6 +1 -1 apache/src/mod_alias.c
Index: mod_alias.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_alias.c,v
retrieving revision 1.5
retrieving revision 1.6
diff -C3 -r1.5 -r1.6
*** mod_alias.c 1996/07/08 18:58:58 1.5
--- mod_alias.c 1996/07/28 19:27:47 1.6
***************
*** 234,240 ****
#else
if (r->uri[0] != '/' && r->uri[0] != '\0')
#endif
! return BAD_REQUEST;
if ((ret = try_alias_list (r, serverconf->redirects, 1)) != NULL) {
table_set (r->headers_out, "Location", ret);
--- 234,240 ----
#else
if (r->uri[0] != '/' && r->uri[0] != '\0')
#endif
! return DECLINED;
if ((ret = try_alias_list (r, serverconf->redirects, 1)) != NULL) {
table_set (r->headers_out, "Location", ret);
1.12 +16 -37 apache/src/mod_cgi.c
Index: mod_cgi.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_cgi.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -C3 -r1.11 -r1.12
*** mod_cgi.c 1996/07/21 20:03:42 1.11
--- mod_cgi.c 1996/07/28 19:27:48 1.12
***************
*** 165,179 ****
int cgi_handler (request_rec *r)
{
! int nph;
char *argv0;
FILE *script_out, *script_in;
char argsbuffer[HUGE_STRING_LEN];
int is_included = !strcmp (r->protocol, "INCLUDED");
- char *lenp = table_get (r->headers_in, "Content-length");
struct cgi_child_stuff cld;
if((argv0 = strrchr(r->filename,'/')) != NULL)
argv0++;
else argv0 = r->filename;
--- 165,185 ----
int cgi_handler (request_rec *r)
{
! int retval, nph;
char *argv0;
FILE *script_out, *script_in;
char argsbuffer[HUGE_STRING_LEN];
int is_included = !strcmp (r->protocol, "INCLUDED");
struct cgi_child_stuff cld;
+ if (r->method_number == M_OPTIONS) {
+ /* 99 out of 100 CGI scripts, this is all they support */
+ r->allowed |= (1 << M_GET);
+ r->allowed |= (1 << M_POST);
+ return DECLINED;
+ }
+
if((argv0 = strrchr(r->filename,'/')) != NULL)
argv0++;
else argv0 = r->filename;
***************
*** 201,221 ****
log_reason("file permissions deny server execution", r->filename,
r);
return FORBIDDEN;
}
! if ((r->method_number == M_POST || r->method_number == M_PUT)
! && !lenp) {
! log_reason("POST or PUT without Content-length:", r->filename, r);
! return BAD_REQUEST;
! }
add_common_vars (r);
cld.argv0 = argv0; cld.r = r; cld.nph = nph;
#ifdef __EMX__
! if (r->method_number == M_POST || r->method_number == M_PUT) {
! int len_to_read = atoi (lenp);
!
! if (len_to_read > HUGE_STRING_LEN) len_to_read = HUGE_STRING_LEN;
! read_client_block (r, argsbuffer, len_to_read);
if (!spawn_child_os2 (r->connection->pool, cgi_child, (void *)&cld,
nph ? just_wait : kill_after_timeout,
--- 207,222 ----
log_reason("file permissions deny server execution", r->filename,
r);
return FORBIDDEN;
}
! if ((retval = setup_client_block(r)))
! return retval;
add_common_vars (r);
cld.argv0 = argv0; cld.r = r; cld.nph = nph;
#ifdef __EMX__
! if (should_client_block (r)) {
!
! read_client_block (r, argsbuffer, HUGE_STRING_LEN);
if (!spawn_child_os2 (r->connection->pool, cgi_child, (void *)&cld,
nph ? just_wait : kill_after_timeout,
***************
*** 251,289 ****
*/
#ifndef __EMX__
! if (r->method_number == M_POST || r->method_number == M_PUT) {
void (*handler)();
! int remaining = atoi (lenp);
hard_timeout ("copy script args", r);
handler = signal (SIGPIPE, SIG_IGN);
! while ((remaining > 0))
! {
! int len_read, len_to_read = remaining;
!
! if (len_to_read > HUGE_STRING_LEN) len_to_read = HUGE_STRING_LEN;
!
! len_read = read_client_block (r, argsbuffer, len_to_read);
! if (len_read == 0)
! break;
if (fwrite (argsbuffer, 1, len_read, script_out) == 0)
break;
- remaining -= len_read;
- }
- /* If script stopped reading early, soak up remaining stuff from
- * client...
- */
-
- while (remaining > 0) {
- int len_read, len_to_read = remaining;
- if (len_to_read > HUGE_STRING_LEN) len_to_read = HUGE_STRING_LEN;
-
- len_read = read_client_block (r, argsbuffer, len_to_read);
- if (len_read == 0) break;
- }
-
fflush (script_out);
signal (SIGPIPE, handler);
--- 252,268 ----
*/
#ifndef __EMX__
! if (should_client_block(r)) {
void (*handler)();
! int len_read;
hard_timeout ("copy script args", r);
handler = signal (SIGPIPE, SIG_IGN);
! while ((len_read = read_client_block (r, argsbuffer, HUGE_STRING_LEN)))
if (fwrite (argsbuffer, 1, len_read, script_out) == 0)
break;
fflush (script_out);
signal (SIGPIPE, handler);
1.12 +2 -2 apache/src/mod_include.c
Index: mod_include.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_include.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -C3 -r1.11 -r1.12
*** mod_include.c 1996/07/27 04:43:22 1.11
--- mod_include.c 1996/07/28 19:27:48 1.12
***************
*** 87,93 ****
struct passwd *pw;
table *e = r->subprocess_env;
char *t;
! time_t date = time(NULL);
table_set(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0));
table_set(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1));
--- 87,93 ----
struct passwd *pw;
table *e = r->subprocess_env;
char *t;
! time_t date = r->request_time;
table_set(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0));
table_set(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1));
***************
*** 584,590 ****
if(!strcmp(tag,"errmsg"))
strcpy(error,tag_val);
else if(!strcmp(tag,"timefmt")) {
! time_t date = time(NULL);
strcpy(tf,tag_val);
table_set (env, "DATE_LOCAL", ht_time(r->pool,date,tf,0));
table_set (env, "DATE_GMT", ht_time(r->pool,date,tf,1));
--- 584,590 ----
if(!strcmp(tag,"errmsg"))
strcpy(error,tag_val);
else if(!strcmp(tag,"timefmt")) {
! time_t date = r->request_time;
strcpy(tf,tag_val);
table_set (env, "DATE_LOCAL", ht_time(r->pool,date,tf,0));
table_set (env, "DATE_GMT", ht_time(r->pool,date,tf,1));
1.10 +38 -3 apache/src/mod_negotiation.c
Index: mod_negotiation.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_negotiation.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -C3 -r1.9 -r1.10
*** mod_negotiation.c 1996/07/08 18:59:02 1.9
--- mod_negotiation.c 1996/07/28 19:27:49 1.10
***************
*** 347,353 ****
negotiation_state *parse_accept_headers (request_rec *r)
{
negotiation_state *new =
! (negotiation_state *)palloc (r->pool, sizeof (negotiation_state));
table *hdrs = r->headers_in;
new->pool = r->pool;
--- 347,353 ----
negotiation_state *parse_accept_headers (request_rec *r)
{
negotiation_state *new =
! (negotiation_state *)pcalloc (r->pool, sizeof (negotiation_state));
table *hdrs = r->headers_in;
new->pool = r->pool;
***************
*** 1016,1021 ****
--- 1016,1046 ----
return best;
}
+ char *set_vary (pool *p, negotiation_state *neg)
+ {
+ var_rec *var_recs = (var_rec*)neg->avail_vars->elts;
+ int i, accept_encoding = 0;
+ int accept_language = 0;
+ char *enc, *lang;
+
+ /* Go through each variant and check for an encoding
+ * or language (we always set "Accept", so no need to check).
+ */
+
+ for (i = 0; i < neg->avail_vars->nelts; ++i) {
+ enc = var_recs[i].content_encoding;
+ lang = var_recs[i].content_language;
+
+ if (!accept_encoding && !(!enc || !strcmp(enc, "")))
+ accept_encoding = 1;
+ if (!accept_language && !(!lang || !strcmp(lang, "")))
+ accept_language = 1;
+ }
+
+ return pstrcat(p, "Accept", accept_encoding ? ", Accept-Encoding" : "",
+ accept_language ? ", Accept-Language" : "", NULL);
+ }
+
/****************************************************************
*
* Executive...
***************
*** 1039,1045 ****
return NOT_FOUND;
}
! if (!do_cache_negotiated_docs(r->server)) r->no_cache = 1;
udir = make_dirstr (r->pool, r->uri, count_dirs (r->uri));
udir = escape_uri(r->pool, udir);
internal_redirect (make_full_path (r->pool, udir, best->file_name), r);
--- 1064,1077 ----
return NOT_FOUND;
}
! /* Make sure caching works - Vary should handle HTTP/1.1, but for
! * HTTP/1.0, we can't allow caching at all. NB that we merge the
! * header in case some other module negotiates on something else.
! */
! if (!do_cache_negotiated_docs(r->server) && (r->proto_num < 1001))
! r->no_cache = 1;
! table_merge(r->err_headers_out, "Vary", set_vary(r->pool, neg));
!
udir = make_dirstr (r->pool, r->uri, count_dirs (r->uri));
udir = escape_uri(r->pool, udir);
internal_redirect (make_full_path (r->pool, udir, best->file_name), r);
***************
*** 1087,1093 ****
/* Otherwise, use it. */
! if (!do_cache_negotiated_docs(r->server)) r->no_cache = 1;
r->filename = sub_req->filename;
r->handler = sub_req->handler;
r->content_type = sub_req->content_type;
--- 1119,1128 ----
/* Otherwise, use it. */
! if (!do_cache_negotiated_docs(r->server) && (r->proto_num < 1001))
! r->no_cache = 1;
! table_merge(r->err_headers_out, "Vary", set_vary(r->pool, neg));
!
r->filename = sub_req->filename;
r->handler = sub_req->handler;
r->content_type = sub_req->content_type;
1.34 +6 -18 apache/src/mod_proxy.c
Index: mod_proxy.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_proxy.c,v
retrieving revision 1.33
retrieving revision 1.34
diff -C3 -r1.33 -r1.34
*** mod_proxy.c 1996/07/25 19:49:04 1.33
--- mod_proxy.c 1996/07/28 19:27:49 1.34
***************
*** 2117,2123 ****
static int
proxy_handler(request_rec *r)
{
! char *url, *scheme, *lenp, *p;
void *sconf = r->server->module_config;
proxy_server_conf *conf =
(proxy_server_conf *)get_module_config(sconf, &proxy_module);
--- 2117,2123 ----
static int
proxy_handler(request_rec *r)
{
! char *url, *scheme, *p;
void *sconf = r->server->module_config;
proxy_server_conf *conf =
(proxy_server_conf *)get_module_config(sconf, &proxy_module);
***************
*** 2128,2137 ****
if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED;
! lenp = table_get (r->headers_in, "Content-length");
! if ((r->method_number == M_POST || r->method_number == M_PUT)
! && lenp == NULL)
! return BAD_REQUEST;
url = r->filename + 6;
p = strchr(url, ':');
--- 2128,2135 ----
if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED;
! if ((rc = setup_client_block(r)))
! return rc;
url = r->filename + 6;
p = strchr(url, ':');
***************
*** 2849,2868 ****
bputs("\015\012", f);
/* send the request data, if any. N.B. should we trap SIGPIPE ? */
! if (r->method_number == M_POST || r->method_number == M_PUT)
{
! len = atoi(table_get (r->headers_in, "Content-length"));
!
! while (len > 0)
! {
! i = len;
! if (i > HUGE_STRING_LEN) i = HUGE_STRING_LEN;
!
! i = read_client_block(r, buffer, i);
! bwrite(f, buffer, i);
!
! len -= i;
! }
}
bflush(f);
kill_timeout(r);
--- 2847,2856 ----
bputs("\015\012", f);
/* send the request data, if any. N.B. should we trap SIGPIPE ? */
! if (should_client_block(r))
{
! while ((i = read_client_block (r, buffer, HUGE_STRING_LEN)))
! bwrite(f, buffer, i);
}
bflush(f);
kill_timeout(r);
1.14 +60 -7 apache/src/util.c
Index: util.c
===================================================================
RCS file: /export/home/cvs/apache/src/util.c,v
retrieving revision 1.13
retrieving revision 1.14
diff -C3 -r1.13 -r1.14
*** util.c 1996/07/09 21:40:14 1.13
--- util.c 1996/07/28 19:27:50 1.14
***************
*** 528,533 ****
--- 528,581 ----
return token;
}
+ static char* tspecials = " \t()<>@,;:\\/[]?={}";
+
+ /* Next HTTP token from a header line. Warning --- destructive!
+ * Use only with a copy!
+ */
+
+ static char *next_token (char **toks) {
+ char *cp = *toks;
+ char *ret;
+
+ while (*cp && (iscntrl (*cp) || strchr (tspecials, *cp))) {
+ if (*cp == '"')
+ while (*cp && (*cp != '"')) ++cp;
+ else
+ ++cp;
+ }
+
+ if (!*cp) ret = NULL;
+ else {
+ ret = cp;
+
+ while (*cp && !iscntrl(*cp) && !strchr (tspecials, *cp))
+ ++cp;
+
+ if (*cp) {
+ *toks = cp + 1;
+ *cp = '\0';
+ }
+ else *toks = cp;
+ }
+
+ return ret;
+ }
+
+ int find_token (pool *p, char *line, char *tok) {
+ char *ltok;
+
+ if (!line) return 0;
+
+ line = pstrdup (p, line);
+ while ((ltok = next_token (&line)))
+ if (!strcasecmp (ltok, tok))
+ return 1;
+
+ return 0;
+ }
+
+
char *escape_shell_cmd(pool *p, char *s) {
register int x,y,l;
char *cmd;
***************
*** 617,632 ****
else return OK;
}
! char *construct_url(pool *p, char *uri, server_rec *s) {
char portnum[10]; /* Long enough. Really! */
! if (s->port == 80) {
! return pstrcat (p, "http://", s->server_hostname, uri, NULL);
! } else {
! sprintf (portnum, "%d", s->port);
! return pstrcat (p, "http://", s->server_hostname, ":", portnum, uri,
! NULL);
}
}
#define c2x(what,where) sprintf(where,"%%%02x",what)
--- 665,685 ----
else return OK;
}
! char *construct_server(pool *p, char *hostname, int port) {
char portnum[10]; /* Long enough. Really! */
! if (port == 80)
! return hostname;
! else {
! sprintf (portnum, "%d", port);
! return pstrcat (p, hostname, ":", portnum, NULL);
}
+ }
+
+ char *construct_url(pool *p, char *uri, server_rec *s) {
+ return pstrcat (p, "http://",
+ construct_server(p, s->server_hostname, s->port),
+ uri, NULL);
}
#define c2x(what,where) sprintf(where,"%%%02x",what)