diff --git a/src/cache.c b/src/cache.c index ccc3014..98c13d7 100644 --- a/src/cache.c +++ b/src/cache.c @@ -21,6 +21,74 @@ int magic_init() { return 0; } +void cache_process_term() { + cache_continue = 0; +} + +int cache_process() { + signal(SIGINT, cache_process_term); + signal(SIGTERM, cache_process_term); + + int shm_id = shmget(SHM_KEY, FILE_CACHE_SIZE * sizeof(cache_entry), 0); + if (shm_id < 0) { + fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno)); + return -1; + } + shmdt(cache); + void *shm_rw = shmat(shm_id, NULL, 0); + if (shm_rw == (void *) -1) { + fprintf(stderr, ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR "\n", strerror(errno)); + return -2; + } + cache = shm_rw; + + mkdir("/var/necronda-server", 0655); + + FILE *cache_file = fopen("/var/necronda-server/cache", "rb"); + fread(cache, sizeof(cache_entry), FILE_CACHE_SIZE , cache_file); + fclose(cache_file); + + for (int i = 0; i < FILE_CACHE_SIZE; i++) { + cache[i].is_updating = 0; + } + + FILE *file; + char buf[16384]; + unsigned long read; + int compress; + SHA_CTX ctx; + unsigned char hash[SHA_DIGEST_LENGTH]; + while (cache_continue) { + for (int i = 0; i < FILE_CACHE_SIZE; i++) { + if (cache[i].filename[0] != 0 && cache[i].meta.etag[0] == 0 && !cache[i].is_updating) { + cache[i].is_updating = 1; + SHA1_Init(&ctx); + file = fopen(cache[i].filename, "rb"); + compress = strncmp(cache[i].meta.type, "text/", 5) == 0; + while ((read = fread(buf, 1, sizeof(buf), file)) > 0) { + SHA1_Update(&ctx, buf, read); + if (compress) { + // TODO compress text/* files + } + } + SHA1_Final(hash, &ctx); + memset(cache[i].meta.etag, 0, sizeof(cache[i].meta.etag)); + for (int j = 0; j < SHA_DIGEST_LENGTH; j++) { + sprintf(cache[i].meta.etag + j * 2, "%02x", hash[j]); + } + fclose(file); + cache[i].is_updating = 0; + } + } + + cache_file = fopen("/var/necronda-server/cache", "wb"); + fwrite(cache, sizeof(cache_entry), FILE_CACHE_SIZE , cache_file); + fclose(cache_file); + sleep(1); + } + return 0; +} + int cache_init() { if (magic_init() != 0) { return -1; @@ -46,10 +114,26 @@ int cache_init() { } cache = shm_rw; memset(cache, 0, FILE_CACHE_SIZE * sizeof(cache_entry)); - // TODO load cache from file shmdt(shm_rw); cache = shm; + pid_t pid = fork(); + if (pid == 0) { + // child + if (cache_process() == 0) { + return 1; + } else { + return -6; + } + } else if (pid > 0) { + // parent + fprintf(stderr, "Started child process with PID %i as cache-updater\n", pid); + children[0] = pid; + } else { + fprintf(stderr, ERR_STR "Unable to create child process: %s" CLR_STR "\n", strerror(errno)); + return -5; + } + return 0; } @@ -80,10 +164,20 @@ int cache_update_entry(int entry_num, const char *filename) { strcpy(cache[entry_num].filename, filename); magic_setflags(magic, MAGIC_MIME_TYPE); - strcpy(cache[entry_num].meta.type, magic_file(magic, filename)); + const char *type = magic_file(magic, filename); + char type_new[24]; + sprintf(type_new, "%s", type); + if (strncmp(type, "text/plain", 10) == 0) { + if (strncmp(filename + strlen(filename) - 4, ".css", 4) == 0) { + sprintf(type_new, "text/css"); + } else if (strncmp(filename + strlen(filename) - 3, ".js", 3) == 0) { + sprintf(type_new, "text/javascript"); + } + } + strcpy(cache[entry_num].meta.type, type_new); magic_setflags(magic, MAGIC_MIME_ENCODING); strcpy(cache[entry_num].meta.charset, magic_file(magic, filename)); - cache[entry_num].is_valid_etag = 0; + memset(cache[entry_num].meta.etag, 0, sizeof(cache[entry_num].meta.etag)); cache[entry_num].is_updating = 0; shmdt(shm_rw); @@ -98,7 +192,8 @@ int uri_cache_init(http_uri *uri) { int i; for (i = 0; i < FILE_CACHE_SIZE; i++) { - if (cache[i].filename[0] != 0 && strncmp(cache[i].filename, uri->filename, cache[i].filename_len) == 0) { + if (cache[i].filename[0] != 0 && strlen(cache[i].filename) == strlen(uri->filename) && + strcmp(cache[i].filename, uri->filename) == 0) { uri->meta = &cache[i].meta; if (cache[i].is_updating) { return 0; diff --git a/src/cache.h b/src/cache.h index e68e613..b1300a4 100644 --- a/src/cache.h +++ b/src/cache.h @@ -11,25 +11,32 @@ #include #include #include +#include "uri.h" magic_t magic; -typedef struct { - char etag[64]; - char type[24]; - char charset[16]; - char filename_comp[256]; - struct stat stat; -} meta_data; - typedef struct { char filename[256]; - unsigned short filename_len; - unsigned char is_valid_etag:1; unsigned char is_updating:1; meta_data meta; } cache_entry; cache_entry *cache; +int cache_continue = 1; + +int magic_init(); + +void cache_process_term(); + +int cache_process(); + +int cache_init(); + +int cache_unload(); + +int cache_update_entry(int entry_num, const char *filename); + +int uri_cache_init(http_uri *uri); + #endif //NECRONDA_SERVER_CACHE_H diff --git a/src/client.c b/src/client.c index 2d83384..69400b9 100644 --- a/src/client.c +++ b/src/client.c @@ -181,13 +181,28 @@ int client_request_handler(sock *client, int req_num) { sprintf(err_msg, "Unable to communicate with internal file cache."); goto respond; } + http_add_header_field(&res.hdr, "Last-Modified", + http_format_date(uri.meta->stat.st_mtime, buf0, sizeof(buf0))); sprintf(buf0, "%s, charset=%s", uri.meta->type, uri.meta->charset); http_add_header_field(&res.hdr, "Content-Type", buf0); + if (uri.meta->etag[0] != 0) { + http_add_header_field(&res.hdr, "ETag", uri.meta->etag); + } + if (strncmp(uri.meta->type, "text/", 5) == 0) { + http_add_header_field(&res.hdr, "Cache-Control", "public, max-age=3600"); + } else { + http_add_header_field(&res.hdr, "Cache-Control", "public, max-age=86400"); + } - file = fopen(uri.filename, "rb"); - fseek(file, 0, 2); - content_length = ftell(file); - fseek(file, 0, 0); + char *if_none_match = http_get_header_field(&req.hdr, "If-None-Match", HTTP_LOWER); + if (if_none_match != NULL && strncmp(if_none_match, uri.meta->etag, sizeof(uri.meta->etag)) == 0) { + res.status = http_get_status(304); + } else { + file = fopen(uri.filename, "rb"); + fseek(file, 0, 2); + content_length = ftell(file); + fseek(file, 0, 0); + } } respond: diff --git a/src/necronda-server.c b/src/necronda-server.c index e99d92b..3fa6db1 100644 --- a/src/necronda-server.c +++ b/src/necronda-server.c @@ -164,6 +164,7 @@ int main(int argc, const char *argv[]) { int ready_sockets_num; long client_num = 0; char buf[1024]; + int ret; int client_fd; sock client; @@ -261,8 +262,11 @@ int main(int argc, const char *argv[]) { signal(SIGINT, terminate); signal(SIGTERM, terminate); - if (cache_init() != 0) { + ret = cache_init(); + if (ret < 0) { return 1; + } else if (ret != 0) { + return 0; } // TODO init geoip database @@ -349,7 +353,6 @@ int main(int argc, const char *argv[]) { } int status = 0; - int ret; for (int i = 0; i < MAX_CHILDREN; i++) { if (children[i] != 0) { ret = waitpid(children[i], &status, WNOHANG); diff --git a/src/uri.h b/src/uri.h index d2969c6..8fe0856 100644 --- a/src/uri.h +++ b/src/uri.h @@ -9,12 +9,19 @@ #define NECRONDA_SERVER_URI_H #include -#include "cache.h" #define URI_DIR_MODE_FORBIDDEN 0 #define URI_DIR_MODE_LIST 1 #define URI_DIR_MODE_INFO 2 +typedef struct { + char etag[64]; + char type[24]; + char charset[16]; + char filename_comp[256]; + struct stat stat; +} meta_data; + typedef struct { char *webroot; // "/srv/www/www.test.org" char *req_path; // "/account/login"