summaryrefslogtreecommitdiffstatshomepage
path: root/tests/t0020-validate-cache.sh
Commit message (Collapse)AuthorAge
* Redesign the caching layerLars Hjemli2008-04-28
The original caching layer in cgit has no upper bound on the number of concurrent cache entries, so when cgit is traversed by a spider (like the googlebot), the cache might end up filling your disk. Also, if any error occurs in the cache layer, no content is returned to the client. This patch redesigns the caching layer to avoid these flaws by * giving the cache a bound number of slots * disabling the cache for the current request when errors occur The cache size limit is implemented by hashing the querystring (the cache lookup key) and generating a cache filename based on this hash modulo the cache size. In order to detect hash collisions, the full lookup key (i.e. the querystring) is stored in the cache file (separated from its associated content by ascii 0). The cache filename is the reversed 8-digit hexadecimal representation of hash(key) % cache_size which should make the filesystem lookup pretty fast (if directory content is indexed/sorted); reversing the representation avoids the problem where all keys have equal prefix. There is a new config option, cache-size, which sets the upper bound for the cache. Default value for this option is 0, which has the same effect as setting nocache=1 (hence nocache is now deprecated). Included in this patch is also a new testfile which verifies that the new option works as intended. Signed-off-by: Lars Hjemli <hjemli@gmail.com>
1 92 93 94 95 96 97 98 99 100 101 102
/* ui-clone.c: functions for http cloning, based on
 * git's http-backend.c by Shawn O. Pearce
 *
 * Copyright (C) 2008 Lars Hjemli
 *
 * Licensed under GNU General Public License v2
 *   (see COPYING for full license text)
 */

#include "cgit.h"
#include "html.h"
#include "ui-shared.h"

static int print_ref_info(const char *refname, const unsigned char *sha1,
                          int flags, void *cb_data)
{
	struct object *obj;

	if (!(obj = parse_object(sha1)))
		return 0;

	if (!strcmp(refname, "HEAD") || !prefixcmp(refname, "refs/heads/"))
		htmlf("%s\t%s\n", sha1_to_hex(sha1), refname);
	else if (!prefixcmp(refname, "refs/tags") && obj->type == OBJ_TAG) {
		if (!(obj = deref_tag(obj, refname, 0)))
			return 0;
		htmlf("%s\t%s\n", sha1_to_hex(sha1), refname);
		htmlf("%s\t%s^{}\n", sha1_to_hex(obj->sha1), refname);
	}
	return 0;
}

static void print_pack_info(struct cgit_context *ctx)
{
	struct packed_git *pack;
	int ofs;

	ctx->page.mimetype = "text/plain";
	ctx->page.filename = "objects/info/packs";
	cgit_print_http_headers(ctx);
	ofs = strlen(ctx->repo->path) + strlen("/objects/pack/");
	prepare_packed_git();
	for (pack = packed_git; pack; pack = pack->next)
		if (pack->pack_local)
			htmlf("P %s\n", pack->pack_name + ofs);
}

static void send_file(struct cgit_context *ctx, char *path)
{
	struct stat st;

	if (stat(path, &st)) {
		switch (errno) {
		case ENOENT:
			html_status(404, "Not found", 0);
			break;
		case EACCES:
			html_status(403, "Forbidden", 0);
			break;
		default:
			html_status(400, "Bad request", 0);
		}
		return;
	}
	ctx->page.mimetype = "application/octet-stream";
	ctx->page.filename = path;
	if (prefixcmp(ctx->repo->path, path))
		ctx->page.filename += strlen(ctx->repo->path) + 1;
	cgit_print_http_headers(ctx);
	html_include(path);
}

void cgit_clone_info(struct cgit_context *ctx)
{
	if (!ctx->qry.path || strcmp(ctx->qry.path, "refs"))
		return;

	ctx->page.mimetype = "text/plain";
	ctx->page.filename = "info/refs";
	cgit_print_http_headers(ctx);
	for_each_ref(print_ref_info, ctx);
}

void cgit_clone_objects(struct cgit_context *ctx)
{
	if (!ctx->qry.path) {
		html_status(400, "Bad request", 0);
		return;
	}

	if (!strcmp(ctx->qry.path, "info/packs")) {
		print_pack_info(ctx);
		return;
	}

	send_file(ctx, git_path("objects/%s", ctx->qry.path));
}

void cgit_clone_head(struct cgit_context *ctx)
{
	send_file(ctx, git_path("%s", "HEAD"));
}