Add ETag calculation
This commit is contained in:
		
							
								
								
									
										103
									
								
								src/cache.c
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								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;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										27
									
								
								src/cache.h
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/cache.h
									
									
									
									
									
								
							@@ -11,25 +11,32 @@
 | 
			
		||||
#include <magic.h>
 | 
			
		||||
#include <sys/ipc.h>
 | 
			
		||||
#include <sys/shm.h>
 | 
			
		||||
#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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										23
									
								
								src/client.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								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:
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -9,12 +9,19 @@
 | 
			
		||||
#define NECRONDA_SERVER_URI_H
 | 
			
		||||
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#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"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user