Esempio n. 1
0
static void string_strtoff(abts_case *tc, void *data)
{
    apr_off_t off;

    ABTS_ASSERT(tc, "strtoff fails on out-of-range integer",
                apr_strtoff(&off, "999999999999999999999999999999",
                            NULL, 10) != APR_SUCCESS);

    ABTS_ASSERT(tc, "strtoff failed for 1234",
                apr_strtoff(&off, "1234", NULL, 10) == APR_SUCCESS);

    ABTS_ASSERT(tc, "strtoff failed to parse 1234", off == 1234);
}
Esempio n. 2
0
static int parse_byterange(char *range, apr_off_t clength,
                           apr_off_t *start, apr_off_t *end)
{
    char *dash = strchr(range, '-');
    char *errp;
    apr_off_t number;

    if (!dash) {
        return 0;
    }

    if ((dash == range)) {
        /* In the form "-5" */
        if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
            return 0;
        }
        *start = clength - number;
        *end = clength - 1;
    }
    else {
        *dash++ = '\0';
        if (apr_strtoff(&number, range, &errp, 10) || *errp) {
            return 0;
        }
        *start = number;
        if (*dash) {
            if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
                return 0;
            }
            *end = number;
        }
        else {                  /* "5-" */
            *end = clength - 1;
        }
    }

    if (*start < 0) {
        *start = 0;
    }

    if (*end >= clength) {
        *end = clength - 1;
    }

    if (*start > *end) {
        return -1;
    }

    return (*start > 0 || *end < clength);
}
static const char *set_max_line_length(cmd_parms *cmd, void *cfg, const char *arg)
{
    subst_dir_conf *dcfg = (subst_dir_conf *)cfg;
    apr_off_t max;
    char *end;
    apr_status_t rv;

    rv = apr_strtoff(&max, arg, &end, 10);
    if (rv == APR_SUCCESS) {
        if ((*end == 'K' || *end == 'k') && !end[1]) {
            max *= KBYTE;
        }
        else if ((*end == 'M' || *end == 'm') && !end[1]) {
            max *= MBYTE;
        }
        else if ((*end == 'G' || *end == 'g') && !end[1]) {
            max *= GBYTE;
        }
        else if (*end && /* neither empty nor [Bb] */
                 ((*end != 'B' && *end != 'b') || end[1])) {
            rv = APR_EGENERAL;
        }
    }

    if (rv != APR_SUCCESS || max < 0)
    {
        return "SubstituteMaxLineLength must be a non-negative integer optionally "
               "suffixed with 'b', 'k', 'm' or 'g'.";
    }
    dcfg->max_line_length = (apr_size_t)max;
    dcfg->max_line_length_set = 1;
    return NULL;
}
Esempio n. 4
0
static const char *set_max_streaming_buffer(cmd_parms *parms, void *dummy,
                                            const char *arg)
{
    char *err;
    if (apr_strtoff(&sconf->max_streaming_buffer_size, arg, &err, 10) || *err) {
        return "MCacheMaxStreamingBuffer value must be a number";
    }

    return NULL;
}
Esempio n. 5
0
apr_status_t
svn__strtoff(apr_off_t *offset, const char *buf, char **end, int base)
{
#if !APR_VERSION_AT_LEAST(1,0,0)
  errno = 0;
  *offset = strtol(buf, end, base);
  return APR_FROM_OS_ERROR(errno);
#else
  return apr_strtoff(offset, buf, end, base);
#endif
}
Esempio n. 6
0
static const char
*set_cache_maxfs(cmd_parms *parms, void *in_struct_ptr, const char *arg)
{
    disk_cache_conf *conf = ap_get_module_config(parms->server->module_config,
                                                 &disk_cache_module);
    if (apr_strtoff(&conf->maxfs, arg, NULL, 0) != APR_SUCCESS ||
            conf->maxfs < 0) 
    {
        return "CacheMaxFileSize argument must be a non-negative integer representing the max size of a file to cache in bytes.";
    }
    return NULL;
}
Esempio n. 7
0
static apr_off_t get_start(request_rec *r)
{
    apr_off_t start = 0;
    char *p = NULL;

    if (!r->args) {
        return start;
    }

    p = strstr(r->args, "start=");

    if (p) {
        p = p + 6;
        apr_strtoff(&start, p, NULL, 10);
    }

    return start;
}
Esempio n. 8
0
static apr_off_t get_content_length(request_rec * r)
{
    apr_off_t len = 0;

    if (r->clength > 0) {
        return r->clength;
    }
    else if (r->main == NULL) {
        const char *clp = apr_table_get(r->headers_in, "Content-Length");

        if (clp) {
            char *errp;
            if (apr_strtoff(&len, clp, &errp, 10) || *errp || len < 0) {
                len = 0; /* parse error */
            }
        }
    }

    return len;
}
Esempio n. 9
0
static int
php_apache_sapi_header_handler(sapi_header_struct *sapi_header, sapi_header_op_enum op, sapi_headers_struct *sapi_headers)
{
	php_struct *ctx;
	char *val, *ptr;

	ctx = SG(server_context);

	switch (op) {
		case SAPI_HEADER_DELETE:
			apr_table_unset(ctx->r->headers_out, sapi_header->header);
			return 0;

		case SAPI_HEADER_DELETE_ALL:
			apr_table_clear(ctx->r->headers_out);
			return 0;

		case SAPI_HEADER_ADD:
		case SAPI_HEADER_REPLACE:
			val = strchr(sapi_header->header, ':');

			if (!val) {
				return 0;
			}
			ptr = val;

			*val = '\0';

			do {
				val++;
			} while (*val == ' ');

			if (!strcasecmp(sapi_header->header, "content-type")) {
				if (ctx->content_type) {
					efree(ctx->content_type);
				}
				ctx->content_type = estrdup(val);
			} else if (!strcasecmp(sapi_header->header, "content-length")) {
				apr_off_t clen = 0;

				if (APR_SUCCESS != apr_strtoff(&clen, val, (char **) NULL, 10)) {
					/* We'll fall back to strtol, since that's what we used to
					 * do anyway. */
					clen = (apr_off_t) strtol(val, (char **) NULL, 10);
				}

				ap_set_content_length(ctx->r, clen);
			} else if (op == SAPI_HEADER_REPLACE) {
				apr_table_set(ctx->r->headers_out, sapi_header->header, val);
			} else {
				apr_table_add(ctx->r->headers_out, sapi_header->header, val);
			}

			*ptr = ':';

			return SAPI_HEADER_ADD;

		default:
			return 0;
	}
}
Esempio n. 10
0
/*
 * main
 */
int main(int argc, const char * const argv[])
{
    apr_off_t max;
    apr_time_t current, repeat, delay, previous;
    apr_status_t status;
    apr_pool_t *pool, *instance;
    apr_getopt_t *o;
    apr_finfo_t info;
    int retries, isdaemon, limit_found, intelligent, dowork;
    char opt;
    const char *arg;
    char *proxypath, *path;

    interrupted = 0;
    repeat = 0;
    isdaemon = 0;
    dryrun = 0;
    limit_found = 0;
    max = 0;
    verbose = 0;
    realclean = 0;
    benice = 0;
    deldirs = 0;
    intelligent = 0;
    previous = 0; /* avoid compiler warning */
    proxypath = NULL;

    if (apr_app_initialize(&argc, &argv, NULL) != APR_SUCCESS) {
        return 1;
    }
    atexit(apr_terminate);

    if (argc) {
        shortname = apr_filepath_name_get(argv[0]);
    }

    if (apr_pool_create(&pool, NULL) != APR_SUCCESS) {
        return 1;
    }
    apr_pool_abort_set(oom, pool);
    apr_file_open_stderr(&errfile, pool);
    apr_signal(SIGINT, setterm);
    apr_signal(SIGTERM, setterm);

    apr_getopt_init(&o, pool, argc, argv);

    while (1) {
        status = apr_getopt(o, "iDnvrtd:l:L:p:", &opt, &arg);
        if (status == APR_EOF) {
            break;
        }
        else if (status != APR_SUCCESS) {
            usage();
        }
        else {
            switch (opt) {
            case 'i':
                if (intelligent) {
                    usage();
                }
                intelligent = 1;
                break;

            case 'D':
                if (dryrun) {
                    usage();
                }
                dryrun = 1;
                break;

            case 'n':
                if (benice) {
                    usage();
                }
                benice = 1;
                break;

            case 't':
                if (deldirs) {
                    usage();
                }
                deldirs = 1;
                break;

            case 'v':
                if (verbose) {
                    usage();
                }
                verbose = 1;
                break;

            case 'r':
                if (realclean) {
                    usage();
                }
                realclean = 1;
                deldirs = 1;
                break;

            case 'd':
                if (isdaemon) {
                    usage();
                }
                isdaemon = 1;
                repeat = apr_atoi64(arg);
                repeat *= SECS_PER_MIN;
                repeat *= APR_USEC_PER_SEC;
                break;

            case 'l':
                if (limit_found) {
                    usage();
                }
                limit_found = 1;

                do {
                    apr_status_t rv;
                    char *end;

                    rv = apr_strtoff(&max, arg, &end, 10);
                    if (rv == APR_SUCCESS) {
                        if ((*end == 'K' || *end == 'k') && !end[1]) {
                            max *= KBYTE;
                        }
                        else if ((*end == 'M' || *end == 'm') && !end[1]) {
                            max *= MBYTE;
                        }
                        else if ((*end == 'G' || *end == 'g') && !end[1]) {
                            max *= GBYTE;
                        }
                        else if (*end &&        /* neither empty nor [Bb] */
                                 ((*end != 'B' && *end != 'b') || end[1])) {
                            rv = APR_EGENERAL;
                        }
                    }
                    if (rv != APR_SUCCESS) {
                        apr_file_printf(errfile, "Invalid limit: %s"
                                                 APR_EOL_STR APR_EOL_STR, arg);
                        usage();
                    }
                } while(0);
                break;

            case 'p':
                if (proxypath) {
                    usage();
                }
                proxypath = apr_pstrdup(pool, arg);
                if (apr_filepath_set(proxypath, pool) != APR_SUCCESS) {
                    usage();
                }
                break;
            } /* switch */
        } /* else */
    } /* while */

    if (o->ind != argc) {
         usage();
    }

    if (isdaemon && (repeat <= 0 || verbose || realclean || dryrun)) {
         usage();
    }

    if (!isdaemon && intelligent) {
         usage();
    }

    if (!proxypath || max <= 0) {
         usage();
    }

    if (apr_filepath_get(&path, 0, pool) != APR_SUCCESS) {
        usage();
    }
    baselen = strlen(path);

#ifndef DEBUG
    if (isdaemon) {
        apr_file_close(errfile);
        apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
    }
#endif

    do {
        apr_pool_create(&instance, pool);

        now = apr_time_now();
        APR_RING_INIT(&root, _entry, link);
        delcount = 0;
        unsolicited = 0;
        dowork = 0;

        switch (intelligent) {
        case 0:
            dowork = 1;
            break;

        case 1:
            retries = STAT_ATTEMPTS;
            status = APR_SUCCESS;

            do {
                if (status != APR_SUCCESS) {
                    apr_sleep(STAT_DELAY);
                }
                status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
            } while (status != APR_SUCCESS && !interrupted && --retries);

            if (status == APR_SUCCESS) {
                previous = info.mtime;
                intelligent = 2;
            }
            dowork = 1;
            break;

        case 2:
            retries = STAT_ATTEMPTS;
            status = APR_SUCCESS;

            do {
                if (status != APR_SUCCESS) {
                    apr_sleep(STAT_DELAY);
                }
                status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
            } while (status != APR_SUCCESS && !interrupted && --retries);

            if (status == APR_SUCCESS) {
                if (previous != info.mtime) {
                    dowork = 1;
                }
                previous = info.mtime;
                break;
            }
            intelligent = 1;
            dowork = 1;
            break;
        }

        if (dowork && !interrupted) {
            if (!process_dir(path, instance) && !interrupted) {
                purge(path, instance, max);
            }
            else if (!isdaemon && !interrupted) {
                apr_file_printf(errfile, "An error occurred, cache cleaning "
                                         "aborted." APR_EOL_STR);
                return 1;
            }

            if (intelligent && !interrupted) {
                retries = STAT_ATTEMPTS;
                status = APR_SUCCESS;
                do {
                    if (status != APR_SUCCESS) {
                        apr_sleep(STAT_DELAY);
                    }
                    status = apr_stat(&info, path, APR_FINFO_MTIME, instance);
                } while (status != APR_SUCCESS && !interrupted && --retries);

                if (status == APR_SUCCESS) {
                    previous = info.mtime;
                    intelligent = 2;
                }
                else {
                    intelligent = 1;
                }
            }
        }

        apr_pool_destroy(instance);

        current = apr_time_now();
        if (current < now) {
            delay = repeat;
        }
        else if (current - now >= repeat) {
            delay = repeat;
        }
        else {
            delay = now + repeat - current;
        }

        /* we can't sleep the whole delay time here apiece as this is racy
         * with respect to interrupt delivery - think about what happens
         * if we have tested for an interrupt, then get scheduled
         * before the apr_sleep() call and while waiting for the cpu
         * we do get an interrupt
         */
        if (isdaemon) {
            while (delay && !interrupted) {
                if (delay > APR_USEC_PER_SEC) {
                    apr_sleep(APR_USEC_PER_SEC);
                    delay -= APR_USEC_PER_SEC;
                }
                else {
                    apr_sleep(delay);
                    delay = 0;
                }
            }
        }
    } while (isdaemon && !interrupted);

    if (!isdaemon && interrupted) {
        apr_file_printf(errfile, "Cache cleaning aborted due to user "
                                 "request." APR_EOL_STR);
        return 1;
    }

    return 0;
}
static int ap_set_byterange(request_rec *r, apr_off_t clength,
                            apr_array_header_t **indexes)
{
    const char *range;
    const char *if_range;
    const char *match;
    const char *ct;
    char *cur;
    int num_ranges = 0, unsatisfiable = 0;
    apr_off_t sum_lengths = 0;
    indexes_t *idx;
    int ranges = 1;
    const char *it;

    if (r->assbackwards) {
        return 0;
    }

    /*
     * Check for Range request-header (HTTP/1.1) or Request-Range for
     * backwards-compatibility with second-draft Luotonen/Franks
     * byte-ranges (e.g. Netscape Navigator 2-3).
     *
     * We support this form, with Request-Range, and (farther down) we
     * send multipart/x-byteranges instead of multipart/byteranges for
     * Request-Range based requests to work around a bug in Netscape
     * Navigator 2-3 and MSIE 3.
     */

    if (!(range = apr_table_get(r->headers_in, "Range"))) {
        range = apr_table_get(r->headers_in, "Request-Range");
    }

    if (!range || strncasecmp(range, "bytes=", 6) || r->status != HTTP_OK) {
        return 0;
    }

    /* is content already a single range? */
    if (apr_table_get(r->headers_out, "Content-Range")) {
       return 0;
    }

    /* is content already a multiple range? */
    if ((ct = apr_table_get(r->headers_out, "Content-Type"))
        && (!strncasecmp(ct, "multipart/byteranges", 20)
            || !strncasecmp(ct, "multipart/x-byteranges", 22))) {
       return 0;
    }

    /*
     * Check the If-Range header for Etag or Date.
     * Note that this check will return false (as required) if either
     * of the two etags are weak.
     */
    if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
        if (if_range[0] == '"') {
            if (!(match = apr_table_get(r->headers_out, "Etag"))
                || (strcmp(if_range, match) != 0)) {
                return 0;
            }
        }
        else if (!(match = apr_table_get(r->headers_out, "Last-Modified"))
                 || (strcmp(if_range, match) != 0)) {
            return 0;
        }
    }

    range += 6;
    it = range;
    while (*it) {
        if (*it++ == ',') {
            ranges++;
        }
    }
    it = range;
    *indexes = apr_array_make(r->pool, ranges, sizeof(indexes_t));
    while ((cur = ap_getword(r->pool, &range, ','))) {
        char *dash;
        char *errp;
        apr_off_t number, start, end;

        if (!*cur)
            break;

        /*
         * Per RFC 2616 14.35.1: If there is at least one syntactically invalid
         * byte-range-spec, we must ignore the whole header.
         */

        if (!(dash = strchr(cur, '-'))) {
            return 0;
        }

        if (dash == cur) {
            /* In the form "-5" */
            if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
                return 0;
            }
            if (number < 1) {
                return 0;
            }
            start = clength - number;
            end = clength - 1;
        }
        else {
            *dash++ = '\0';
            if (apr_strtoff(&number, cur, &errp, 10) || *errp) {
                return 0;
            }
            start = number;
            if (*dash) {
                if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
                    return 0;
                }
                end = number;
                if (start > end) {
                    return 0;
                }
            }
            else {                  /* "5-" */
                end = clength - 1;
                /*
                 * special case: 0-
                 *   ignore all other ranges provided
                 *   return as a single range: 0-
                 */
                if (start == 0) {
                    (*indexes)->nelts = 0;
                    idx = (indexes_t *)apr_array_push(*indexes);
                    idx->start = start;
                    idx->end = end;
                    sum_lengths = clength;
                    num_ranges = 1;
                    break;
                }
            }
        }

        if (start < 0) {
            start = 0;
        }
        if (start >= clength) {
            unsatisfiable = 1;
            continue;
        }
        if (end >= clength) {
            end = clength - 1;
        }

        idx = (indexes_t *)apr_array_push(*indexes);
        idx->start = start;
        idx->end = end;
        sum_lengths += end - start + 1;
        /* new set again */
        num_ranges++;
    }

    if (num_ranges == 0 && unsatisfiable) {
        /* If all ranges are unsatisfiable, we should return 416 */
        return -1;
    }
    if (sum_lengths > clength) {
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
                      "Sum of ranges larger than file, ignoring.");
        return 0;
    }

    r->status = HTTP_PARTIAL_CONTENT;
    r->range = it;

    return num_ranges;
}
Esempio n. 12
0
long long fend_to_number(char* str) {
    apr_off_t result = 0;
    apr_strtoff(&result, str, NULL, 0);
    return result;
}
Esempio n. 13
0
static int reflector_handler(request_rec * r)
{
    apr_bucket_brigade *bbin, *bbout;
    reflector_cfg *conf;
    apr_status_t status;

    if (strcmp(r->handler, "reflector")) {
        return DECLINED;
    }

    conf = (reflector_cfg *) ap_get_module_config(r->per_dir_config,
                                                  &reflector_module);

    ap_allow_methods(r, 1, "POST", "OPTIONS", NULL);

    if (r->method_number == M_OPTIONS) {
        return ap_send_http_options(r);
    }

    else if (r->method_number == M_POST) {
        const char *content_length, *content_type;
        int seen_eos;

        /*
         * Sometimes we'll get in a state where the input handling has
         * detected an error where we want to drop the connection, so if
         * that's the case, don't read the data as that is what we're trying
         * to avoid.
         *
         * This function is also a no-op on a subrequest.
         */
        if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
            ap_status_drops_connection(r->status)) {
            return OK;
        }

        /* copy headers from in to out if configured */
        apr_table_do(header_do, r, conf->headers, NULL);

        /* last modified defaults to now, unless otherwise set on the way in */
        if (!apr_table_get(r->headers_out, "Last-Modified")) {
            ap_update_mtime(r, apr_time_now());
            ap_set_last_modified(r);
        }
        ap_set_accept_ranges(r);

        /* reflect the content length, if present */
        if ((content_length = apr_table_get(r->headers_in, "Content-Length"))) {
            apr_off_t offset;

            apr_strtoff(&offset, content_length, NULL, 10);
            ap_set_content_length(r, offset);

        }

        /* reflect the content type, if present */
        if ((content_type = apr_table_get(r->headers_in, "Content-Type"))) {

            ap_set_content_type(r, content_type);

        }

        bbin = apr_brigade_create(r->pool, r->connection->bucket_alloc);
        bbout = apr_brigade_create(r->pool, r->connection->bucket_alloc);

        seen_eos = 0;
        do {
            apr_bucket *bucket;

            status = ap_get_brigade(r->input_filters, bbin, AP_MODE_READBYTES,
                                    APR_BLOCK_READ, HUGE_STRING_LEN);

            if (status != APR_SUCCESS) {
                if (status == AP_FILTER_ERROR) {
                    apr_brigade_destroy(bbin);
                    return status;
                }
                else {
                    apr_brigade_destroy(bbin);
                    return HTTP_BAD_REQUEST;
                }
            }

            for (bucket = APR_BRIGADE_FIRST(bbin);
                 bucket != APR_BRIGADE_SENTINEL(bbin);
                 bucket = APR_BUCKET_NEXT(bucket)) {
                const char *data;
                apr_size_t len;

                if (APR_BUCKET_IS_EOS(bucket)) {
                    seen_eos = 1;
                    break;
                }

                /* These are metadata buckets. */
                if (bucket->length == 0) {
                    continue;
                }

                /*
                 * We MUST read because in case we have an unknown-length
                 * bucket or one that morphs, we want to exhaust it.
                 */
                status = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
                if (status != APR_SUCCESS) {
                    apr_brigade_destroy(bbin);
                    return HTTP_BAD_REQUEST;
                }

                apr_brigade_write(bbout, NULL, NULL, data, len);

                status = ap_pass_brigade(r->output_filters, bbout);
                if (status != APR_SUCCESS) {
                    /* no way to know what type of error occurred */
                    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(01410)
                             "reflector_handler: ap_pass_brigade returned %i",
                                  status);
                    return HTTP_INTERNAL_SERVER_ERROR;
                }

            }

            apr_brigade_cleanup(bbin);

        } while (!seen_eos);

        return OK;

    }

    else {
        return HTTP_METHOD_NOT_ALLOWED;
    }

}
Esempio n. 14
0
/**
 * This is the KEEP_BODY_INPUT filter for HTTP requests, for times when the
 * body should be set aside for future use by other modules.
 */
static apr_status_t keep_body_filter(ap_filter_t *f, apr_bucket_brigade *b,
                                     ap_input_mode_t mode,
                                     apr_read_type_e block,
                                     apr_off_t readbytes)
{
    apr_bucket *e;
    keep_body_ctx_t *ctx = f->ctx;
    apr_status_t rv;
    apr_bucket *bucket;
    apr_off_t len = 0;


    if (!ctx) {
        const char *lenp;
        char *endstr = NULL;
        request_dir_conf *dconf = ap_get_module_config(f->r->per_dir_config,
                                                       &request_module);

        /* must we step out of the way? */
        if (!dconf->keep_body || f->r->kept_body) {
            ap_remove_input_filter(f);
            return ap_get_brigade(f->next, b, mode, block, readbytes);
        }

        f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));

        /* fail fast if the content length exceeds keep body */
        lenp = apr_table_get(f->r->headers_in, "Content-Length");
        if (lenp) {

            /* Protects against over/underflow, non-digit chars in the
             * string (excluding leading space) (the endstr checks)
             * and a negative number. */
            if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10)
                || endstr == lenp || *endstr || ctx->remaining < 0) {

                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01411)
                              "Invalid Content-Length");

                ap_remove_input_filter(f);
                return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
            }

            /* If we have a limit in effect and we know the C-L ahead of
             * time, stop it here if it is invalid.
             */
            if (dconf->keep_body < ctx->remaining) {
                ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01412)
                          "Requested content-length of %" APR_OFF_T_FMT
                          " is larger than the configured limit"
                          " of %" APR_OFF_T_FMT, ctx->remaining, dconf->keep_body);
                ap_remove_input_filter(f);
                return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
            }

        }

        f->r->kept_body = apr_brigade_create(f->r->pool, f->r->connection->bucket_alloc);
        ctx->remaining = dconf->keep_body;

    }

    /* get the brigade from upstream, and read it in to get its length */
    rv = ap_get_brigade(f->next, b, mode, block, readbytes);
    if (rv == APR_SUCCESS) {
        rv = apr_brigade_length(b, 1, &len);
    }

    /* does the length take us over the limit? */
    if (APR_SUCCESS == rv && len > ctx->remaining) {
        if (f->r->kept_body) {
            apr_brigade_cleanup(f->r->kept_body);
            f->r->kept_body = NULL;
        }
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r, APLOGNO(01413)
                      "Requested content-length of %" APR_OFF_T_FMT
                      " is larger than the configured limit"
                      " of %" APR_OFF_T_FMT, len, ctx->keep_body);
        return bail_out_on_error(b, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
    }
    ctx->remaining -= len;

    /* pass any errors downstream */
    if (rv != APR_SUCCESS) {
        if (f->r->kept_body) {
            apr_brigade_cleanup(f->r->kept_body);
            f->r->kept_body = NULL;
        }
        return rv;
    }

    /* all is well, set aside the buckets */
    for (bucket = APR_BRIGADE_FIRST(b);
         bucket != APR_BRIGADE_SENTINEL(b);
         bucket = APR_BUCKET_NEXT(bucket))
    {
        apr_bucket_copy(bucket, &e);
        APR_BRIGADE_INSERT_TAIL(f->r->kept_body, e);
    }

    return APR_SUCCESS;
}
Esempio n. 15
0
/**
 * Parse the Cache-Control and Pragma headers in one go, marking
 * which tokens appear within the header. Populate the structure
 * passed in.
 */
int ap_cache_control(request_rec *r, cache_control_t *cc,
        const char *cc_header, const char *pragma_header, apr_table_t *headers)
{
    char *last;

    if (cc->parsed) {
        return cc->cache_control || cc->pragma;
    }

    cc->parsed = 1;
    cc->max_age_value = -1;
    cc->max_stale_value = -1;
    cc->min_fresh_value = -1;
    cc->s_maxage_value = -1;

    if (pragma_header) {
        char *header = apr_pstrdup(r->pool, pragma_header);
        const char *token = cache_strqtok(header, CACHE_SEPARATOR, &last);
        while (token) {
            /* handle most common quickest case... */
            if (!strcmp(token, "no-cache")) {
                cc->no_cache = 1;
            }
            /* ...then try slowest case */
            else if (!strcasecmp(token, "no-cache")) {
                cc->no_cache = 1;
            }
            token = cache_strqtok(NULL, CACHE_SEPARATOR, &last);
        }
        cc->pragma = 1;
    }

    if (cc_header) {
        char *endp;
        apr_off_t offt;
        char *header = apr_pstrdup(r->pool, cc_header);
        const char *token = cache_strqtok(header, CACHE_SEPARATOR, &last);
        while (token) {
            switch (token[0]) {
            case 'n':
            case 'N': {
                /* handle most common quickest cases... */
                if (!strcmp(token, "no-cache")) {
                    cc->no_cache = 1;
                }
                else if (!strcmp(token, "no-store")) {
                    cc->no_store = 1;
                }
                /* ...then try slowest cases */
                else if (!strncasecmp(token, "no-cache", 8)) {
                    if (token[8] == '=') {
                        cc->no_cache_header = 1;
                    }
                    else if (!token[8]) {
                        cc->no_cache = 1;
                    }
                    break;
                }
                else if (!strcasecmp(token, "no-store")) {
                    cc->no_store = 1;
                }
                else if (!strcasecmp(token, "no-transform")) {
                    cc->no_transform = 1;
                }
                break;
            }
            case 'm':
            case 'M': {
                /* handle most common quickest cases... */
                if (!strcmp(token, "max-age=0")) {
                    cc->max_age = 1;
                    cc->max_age_value = 0;
                }
                else if (!strcmp(token, "must-revalidate")) {
                    cc->must_revalidate = 1;
                }
                /* ...then try slowest cases */
                else if (!strncasecmp(token, "max-age", 7)) {
                    if (token[7] == '='
                            && !apr_strtoff(&offt, token + 8, &endp, 10)
                            && endp > token + 8 && !*endp) {
                        cc->max_age = 1;
                        cc->max_age_value = offt;
                    }
                    break;
                }
                else if (!strncasecmp(token, "max-stale", 9)) {
                    if (token[9] == '='
                            && !apr_strtoff(&offt, token + 10, &endp, 10)
                            && endp > token + 10 && !*endp) {
                        cc->max_stale = 1;
                        cc->max_stale_value = offt;
                    }
                    else if (!token[9]) {
                        cc->max_stale = 1;
                        cc->max_stale_value = -1;
                    }
                    break;
                }
                else if (!strncasecmp(token, "min-fresh", 9)) {
                    if (token[9] == '='
                            && !apr_strtoff(&offt, token + 10, &endp, 10)
                            && endp > token + 10 && !*endp) {
                        cc->min_fresh = 1;
                        cc->min_fresh_value = offt;
                    }
                    break;
                }
                else if (!strcasecmp(token, "must-revalidate")) {
                    cc->must_revalidate = 1;
                }
                break;
            }
            case 'o':
            case 'O': {
                if (!strcasecmp(token, "only-if-cached")) {
                    cc->only_if_cached = 1;
                }
                break;
            }
            case 'p':
            case 'P': {
                /* handle most common quickest cases... */
                if (!strcmp(token, "private")) {
                    cc->private = 1;
                }
                /* ...then try slowest cases */
                else if (!strcasecmp(token, "public")) {
                    cc->public = 1;
                }
                else if (!strncasecmp(token, "private", 7)) {
                    if (token[7] == '=') {
                        cc->private_header = 1;
                    }
                    else if (!token[7]) {
                        cc->private = 1;
                    }
                    break;
                }
Esempio n. 16
0
int cache_check_freshness(cache_handle_t *h, cache_request_rec *cache,
        request_rec *r)
{
    apr_status_t status;
    apr_int64_t age, maxage_req, maxage_cresp, maxage, smaxage, maxstale;
    apr_int64_t minfresh;
    const char *cc_req;
    const char *pragma;
    const char *agestr = NULL;
    apr_time_t age_c = 0;
    cache_info *info = &(h->cache_obj->info);
    const char *warn_head;
    cache_server_conf *conf =
      (cache_server_conf *)ap_get_module_config(r->server->module_config,
                                                &cache_module);

    /*
     * We now want to check if our cached data is still fresh. This depends
     * on a few things, in this order:
     *
     * - RFC2616 14.9.4 End to end reload, Cache-Control: no-cache. no-cache
     * in either the request or the cached response means that we must
     * perform the request unconditionally, and ignore cached content. We
     * should never reach here, but if we do, mark the content as stale,
     * as this is the best we can do.
     *
     * - RFC2616 14.32 Pragma: no-cache This is treated the same as
     * Cache-Control: no-cache.
     *
     * - RFC2616 14.9.3 Cache-Control: max-stale, must-revalidate,
     * proxy-revalidate if the max-stale request header exists, modify the
     * stale calculations below so that an object can be at most <max-stale>
     * seconds stale before we request a revalidation, _UNLESS_ a
     * must-revalidate or proxy-revalidate cached response header exists to
     * stop us doing this.
     *
     * - RFC2616 14.9.3 Cache-Control: s-maxage the origin server specifies the
     * maximum age an object can be before it is considered stale. This
     * directive has the effect of proxy|must revalidate, which in turn means
     * simple ignore any max-stale setting.
     *
     * - RFC2616 14.9.4 Cache-Control: max-age this header can appear in both
     * requests and responses. If both are specified, the smaller of the two
     * takes priority.
     *
     * - RFC2616 14.21 Expires: if this request header exists in the cached
     * entity, and it's value is in the past, it has expired.
     *
     */

    /* This value comes from the client's initial request. */
    cc_req = apr_table_get(r->headers_in, "Cache-Control");
    pragma = apr_table_get(r->headers_in, "Pragma");

    ap_cache_control(r, &cache->control_in, cc_req, pragma, r->headers_in);

    if (cache->control_in.no_cache) {

        if (!conf->ignorecachecontrol) {
            /* Treat as stale, causing revalidation */
            return 0;
        }

        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00781)
                "Incoming request is asking for a uncached version of "
                "%s, but we have been configured to ignore it and "
                "serve a cached response anyway",
                r->unparsed_uri);
    }

    /* These come from the cached entity. */
    if (h->cache_obj->info.control.no_cache
            || h->cache_obj->info.control.invalidated) {
        /*
         * The cached entity contained Cache-Control: no-cache, or a
         * no-cache with a header present, or a private with a header
         * present, or the cached entity has been invalidated in the
         * past, so treat as stale causing revalidation.
         */
        return 0;
    }

    if ((agestr = apr_table_get(h->resp_hdrs, "Age"))) {
        char *endp;
        apr_off_t offt;
        if (!apr_strtoff(&offt, agestr, &endp, 10)
                && endp > agestr && !*endp) {
            age_c = offt;
        }
    }

    /* calculate age of object */
    age = ap_cache_current_age(info, age_c, r->request_time);

    /* extract s-maxage */
    smaxage = h->cache_obj->info.control.s_maxage_value;

    /* extract max-age from request */
    maxage_req = -1;
    if (!conf->ignorecachecontrol) {
        maxage_req = cache->control_in.max_age_value;
    }

    /*
     * extract max-age from response, if both s-maxage and max-age, s-maxage
     * takes priority
     */
    if (smaxage != -1) {
        maxage_cresp = smaxage;
    }
    else {
        maxage_cresp = h->cache_obj->info.control.max_age_value;
    }

    /*
     * if both maxage request and response, the smaller one takes priority
     */
    if (maxage_req == -1) {
        maxage = maxage_cresp;
    }
    else if (maxage_cresp == -1) {
        maxage = maxage_req;
    }
    else {
        maxage = MIN(maxage_req, maxage_cresp);
    }

    /* extract max-stale */
    if (cache->control_in.max_stale) {
        if(cache->control_in.max_stale_value != -1) {
            maxstale = cache->control_in.max_stale_value;
        }
        else {
            /*
             * If no value is assigned to max-stale, then the client is willing
             * to accept a stale response of any age (RFC2616 14.9.3). We will
             * set it to one year in this case as this situation is somewhat
             * similar to a "never expires" Expires header (RFC2616 14.21)
             * which is set to a date one year from the time the response is
             * sent in this case.
             */
            maxstale = APR_INT64_C(86400*365);
        }
    }
    else {
        maxstale = 0;
    }

    /* extract min-fresh */
    if (!conf->ignorecachecontrol && cache->control_in.min_fresh) {
        minfresh = cache->control_in.min_fresh_value;
    }
    else {
        minfresh = 0;
    }

    /* override maxstale if must-revalidate, proxy-revalidate or s-maxage */
    if (maxstale && (h->cache_obj->info.control.must_revalidate
            || h->cache_obj->info.control.proxy_revalidate || smaxage != -1)) {
        maxstale = 0;
    }

    /* handle expiration */
    if (((maxage != -1) && (age < (maxage + maxstale - minfresh))) ||
        ((smaxage == -1) && (maxage == -1) &&
         (info->expire != APR_DATE_BAD) &&
         (age < (apr_time_sec(info->expire - info->date) + maxstale - minfresh)))) {

        warn_head = apr_table_get(h->resp_hdrs, "Warning");

        /* it's fresh darlings... */
        /* set age header on response */
        apr_table_set(h->resp_hdrs, "Age",
                      apr_psprintf(r->pool, "%lu", (unsigned long)age));

        /* add warning if maxstale overrode freshness calculation */
        if (!(((maxage != -1) && age < maxage) ||
              (info->expire != APR_DATE_BAD &&
               (apr_time_sec(info->expire - info->date)) > age))) {
            /* make sure we don't stomp on a previous warning */
            if ((warn_head == NULL) ||
                ((warn_head != NULL) && (ap_strstr_c(warn_head, "110") == NULL))) {
                apr_table_mergen(h->resp_hdrs, "Warning",
                                 "110 Response is stale");
            }
        }

        /*
         * If none of Expires, Cache-Control: max-age, or Cache-Control:
         * s-maxage appears in the response, and the response header age
         * calculated is more than 24 hours add the warning 113
         */
        if ((maxage_cresp == -1) && (smaxage == -1) && (apr_table_get(
                h->resp_hdrs, "Expires") == NULL) && (age > 86400)) {

            /* Make sure we don't stomp on a previous warning, and don't dup
             * a 113 marning that is already present. Also, make sure to add
             * the new warning to the correct *headers_out location.
             */
            if ((warn_head == NULL) ||
                ((warn_head != NULL) && (ap_strstr_c(warn_head, "113") == NULL))) {
                apr_table_mergen(h->resp_hdrs, "Warning",
                                 "113 Heuristic expiration");
            }
        }
        return 1;    /* Cache object is fresh (enough) */
    }

    /*
     * At this point we are stale, but: if we are under load, we may let
     * a significant number of stale requests through before the first
     * stale request successfully revalidates itself, causing a sudden
     * unexpected thundering herd which in turn brings angst and drama.
     *
     * So.
     *
     * We want the first stale request to go through as normal. But the
     * second and subsequent request, we must pretend to be fresh until
     * the first request comes back with either new content or confirmation
     * that the stale content is still fresh.
     *
     * To achieve this, we create a very simple file based lock based on
     * the key of the cached object. We attempt to open the lock file with
     * exclusive write access. If we succeed, woohoo! we're first, and we
     * follow the stale path to the backend server. If we fail, oh well,
     * we follow the fresh path, and avoid being a thundering herd.
     *
     * The lock lives only as long as the stale request that went on ahead.
     * If the request succeeds, the lock is deleted. If the request fails,
     * the lock is deleted, and another request gets to make a new lock
     * and try again.
     *
     * At any time, a request marked "no-cache" will force a refresh,
     * ignoring the lock, ensuring an extended lockout is impossible.
     *
     * A lock that exceeds a maximum age will be deleted, and another
     * request gets to make a new lock and try again.
     */
    status = cache_try_lock(conf, cache, r);
    if (APR_SUCCESS == status) {
        /* we obtained a lock, follow the stale path */
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00782)
                "Cache lock obtained for stale cached URL, "
                "revalidating entry: %s",
                r->unparsed_uri);
        return 0;
    }
    else if (APR_STATUS_IS_EEXIST(status)) {
        /* lock already exists, return stale data anyway, with a warning */
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00783)
                "Cache already locked for stale cached URL, "
                "pretend it is fresh: %s",
                r->unparsed_uri);

        /* make sure we don't stomp on a previous warning */
        warn_head = apr_table_get(h->resp_hdrs, "Warning");
        if ((warn_head == NULL) ||
            ((warn_head != NULL) && (ap_strstr_c(warn_head, "110") == NULL))) {
            apr_table_mergen(h->resp_hdrs, "Warning",
                             "110 Response is stale");
        }

        return 1;
    }
    else {
        /* some other error occurred, just treat the object as stale */
        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(00784)
                "Attempt to obtain a cache lock for stale "
                "cached URL failed, revalidating entry anyway: %s",
                r->unparsed_uri);
        return 0;
    }

}