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)