--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/server/daemon/http.c Sat Oct 17 22:24:38 2015 +0200 @@ -0,0 +1,230 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * THE BSD LICENSE + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of the nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "http.h" +#include "protocol.h" + +#include "../util/pblock.h" +#include "../util/pool.h" +#include "../util/util.h" + + +static int http_etag = 1; + +/* --------------------------- http_format_etag --------------------------- */ + +NSAPI_PUBLIC void http_format_etag(Session *sn, Request *rq, char *etagp, int etaglen, off_t size, time_t mtime) +{ + /* + * Heuristic for weak/strong validator: if the request was made within + * a second of the last-modified date, then we send a weak Etag. + */ + if (rq->req_start - mtime <= 1) { + /* Send a weak Etag */ + *etagp++ = 'W'; + *etagp++ = '/'; + etaglen -= 2; + } + + /* + * Create an Etag out of the metadata (size, mtime) to obtain Etag + * uniqueness. + */ + util_snprintf(etagp, etaglen, "\"%x-%x\"", (int)size, (int)mtime); +} + + +/* --------------------------- http_weaken_etag --------------------------- */ + +NSAPI_PUBLIC void http_weaken_etag(Session *sn, Request *rq) +{ + /* Replace any existing strong Etag with a weak Etag */ + pb_param *pp = pblock_findkey(pb_key_etag, rq->srvhdrs); + if (pp) { + if (pp->value[0] != 'W' || pp->value[1] != '/') { + char *weak = (char *) pool_malloc(sn->pool, 2 + strlen(pp->value) + 1); + + weak[0] = 'W'; + weak[1] = '/'; + strcpy(weak + 2, pp->value); + + pool_free(sn->pool, pp->value); + pp->value = weak; + } + } +} + + +/* ------------------------------ match_etag ------------------------------ */ + +NSAPI_PUBLIC int http_match_etag(const char *header, const char *etag, int strong) +{ + if (!etag) + return 0; /* mismatch */ + + if (header[0] == '*') + return 1; /* match */ + + if (etag[0] == 'W' && etag[1] == '/') { + /* Weak Etags never match when using the strong validator */ + if (strong) + return 0; /* mismatch */ + + etag += 2; + } + + /* Look for Etag in header */ + const char *found = strstr(header, etag); + if (!found) + return 0; /* mismatch */ + + /* Weak Etags never match when using the strong validator */ + if (strong && found >= header + 2 && found[-2] == 'W' && found[-1] == '/') + return 0; /* mismatch */ + + return 1; /* match */ +} + + +/* ----------------------- http_check_preconditions ----------------------- */ + +NSAPI_PUBLIC int http_check_preconditions(Session *sn, Request *rq, struct tm *mtm, const char *etag) +{ + char *header; + + /* If-modified-since */ + header = pblock_findkeyval(pb_key_if_modified_since, rq->headers); + if (header) { + if (mtm && util_later_than(mtm, header)) { + protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL); + return REQ_ABORTED; + } + } + + /* If-unmodified-since */ + header = pblock_findkeyval(pb_key_if_unmodified_since, rq->headers); + if (header) { + if (mtm && !util_later_than(mtm, header)) { + //PRTime temptime; + //PRStatus status = PR_ParseTimeString(header, PR_TRUE, &temptime); + //if (status == PR_SUCCESS) { + // http_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL); + // return REQ_ABORTED; + //} + } + } + + /* If-none-match */ + header = pblock_findkeyval(pb_key_if_none_match, rq->headers); + if (header) { + /* If the If-none-match header matches the current Etag... */ + if (ISMGET(rq) || ISMHEAD(rq)) { + if (http_match_etag(header, etag, PR_FALSE)) { + protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL); + return REQ_ABORTED; + } + } else { + if (http_match_etag(header, etag, PR_TRUE)) { + protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL); + return REQ_ABORTED; + } + } + } + + /* If-match */ + header = pblock_findkeyval(pb_key_if_match, rq->headers); + if (header) { + /* If the If-match header matches the current Etag... */ + if (!http_match_etag(header, etag, PR_TRUE)) { + protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL); + return REQ_ABORTED; + } + } + + return REQ_PROCEED; +} + + +/* ---------------------------- http_set_finfo ---------------------------- */ + +static inline int set_finfo(Session *sn, Request *rq, off_t size, time_t mtime) +{ + struct tm mtms; + struct tm *mtm = system_gmtime(&mtime, &mtms); + pb_param *pp; + + /* Insert Last-modified */ + if (mtm) { + pp = pblock_key_param_create(rq->srvhdrs, pb_key_last_modified, NULL, + HTTP_DATE_LEN); + if (!pp || !pp->value) + return REQ_ABORTED; + strftime(pp->value, HTTP_DATE_LEN, HTTP_DATE_FMT, mtm); + pblock_kpinsert(pb_key_last_modified, pp, rq->srvhdrs); + } + + /* Insert Content-length */ + const int content_length_size = 21; + pp = pblock_key_param_create(rq->srvhdrs, pb_key_content_length, NULL, + content_length_size); + if (!pp || !pp->value) + return REQ_ABORTED; + snprintf(pp->value, content_length_size, "%lld", (long long)size); + pblock_kpinsert(pb_key_content_length, pp, rq->srvhdrs); + + char *etag; + if (http_etag) { + /* Insert Etag */ + pp = pblock_key_param_create(rq->srvhdrs, pb_key_etag, NULL, MAX_ETAG); + if (!pp || !pp->value) + return REQ_ABORTED; + http_format_etag(sn, rq, pp->value, MAX_ETAG, size, mtime); + pblock_kpinsert(pb_key_etag, pp, rq->srvhdrs); + etag = pp->value; + } else { + etag = NULL; + } + + /* Check If-modified-since, etc. */ + return http_check_preconditions(sn, rq, mtm, etag); +} + +NSAPI_PUBLIC int http_set_finfo(Session *sn, Request *rq, struct stat *finfo) +{ + return set_finfo(sn, rq, finfo->st_size, finfo->st_mtime); +} + +