Added basic file transfer
This commit is contained in:
107
src/cache.c
107
src/cache.c
@ -10,22 +10,123 @@
|
|||||||
|
|
||||||
int magic_init() {
|
int magic_init() {
|
||||||
magic = magic_open(MAGIC_MIME);
|
magic = magic_open(MAGIC_MIME);
|
||||||
magic_load(magic, "/usr/share/misc/magic.mgc");
|
if (magic == NULL) {
|
||||||
|
fprintf(stderr, ERR_STR "Unable to open magic cookie: %s" CLR_STR "\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (magic_load(magic, MAGIC_FILE) != 0) {
|
||||||
|
fprintf(stderr, ERR_STR "Unable to load magic cookie: %s" CLR_STR "\n", magic_error(magic));
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cache_init() {
|
int cache_init() {
|
||||||
magic_init();
|
if (magic_init() != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int shm_id = shmget(SHM_KEY, FILE_CACHE_SIZE * sizeof(cache_entry), IPC_CREAT | IPC_EXCL);
|
||||||
|
if (shm_id < 0) {
|
||||||
|
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *shm = shmat(shm_id, NULL, SHM_RDONLY);
|
||||||
|
if (shm == (void *) -1) {
|
||||||
|
fprintf(stderr, ERR_STR "Unable to attach shared memory (ro): %s" CLR_STR "\n", strerror(errno));
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
cache = shm;
|
||||||
|
|
||||||
|
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 -4;
|
||||||
|
}
|
||||||
|
cache = shm_rw;
|
||||||
|
memset(cache, 0, FILE_CACHE_SIZE * sizeof(cache_entry));
|
||||||
|
// TODO load cache from file
|
||||||
|
shmdt(shm_rw);
|
||||||
|
cache = shm;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cache_unload() {
|
||||||
|
int shm_id = shmget(SHM_KEY, 0, 0);
|
||||||
|
if (shm_id < 0) {
|
||||||
|
fprintf(stderr, ERR_STR "Unable to create shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||||
|
} else if (shmctl(shm_id, IPC_RMID, NULL) < 0) {
|
||||||
|
fprintf(stderr, ERR_STR "Unable to configure shared memory: %s" CLR_STR "\n", strerror(errno));
|
||||||
|
}
|
||||||
|
shmdt(cache);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cache_update_entry(int entry_num, const char *filename) {
|
||||||
|
void *cache_ro = cache;
|
||||||
|
int shm_id = shmget(SHM_KEY, 0, 0);
|
||||||
|
void *shm_rw = shmat(shm_id, NULL, 0);
|
||||||
|
if (shm_rw == (void *) -1) {
|
||||||
|
print(ERR_STR "Unable to attach shared memory (rw): %s" CLR_STR, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
cache = shm_rw;
|
||||||
|
|
||||||
|
struct stat statbuf;
|
||||||
|
stat(filename, &statbuf);
|
||||||
|
memcpy(&cache[entry_num].meta.stat, &statbuf, sizeof(statbuf));
|
||||||
|
|
||||||
|
strcpy(cache[entry_num].filename, filename);
|
||||||
|
magic_setflags(magic, MAGIC_MIME_TYPE);
|
||||||
|
strcpy(cache[entry_num].meta.type, magic_file(magic, filename));
|
||||||
|
magic_setflags(magic, MAGIC_MIME_ENCODING);
|
||||||
|
strcpy(cache[entry_num].meta.charset, magic_file(magic, filename));
|
||||||
|
cache[entry_num].is_valid_etag = 0;
|
||||||
|
cache[entry_num].is_updating = 0;
|
||||||
|
|
||||||
|
shmdt(shm_rw);
|
||||||
|
cache = cache_ro;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uri_cache_init(http_uri *uri) {
|
int uri_cache_init(http_uri *uri) {
|
||||||
if (uri->filename == NULL) {
|
if (uri->filename == NULL) {
|
||||||
return -1;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
uri->meta = &cache[i].meta;
|
||||||
|
if (cache[i].is_updating) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri->meta == NULL) {
|
||||||
|
for (i = 0; i < FILE_CACHE_SIZE; i++) {
|
||||||
|
if (cache[i].filename[0] == 0) {
|
||||||
|
if (cache_update_entry(i, uri->filename) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
uri->meta = &cache[i].meta;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
struct stat statbuf;
|
||||||
|
stat(uri->filename, &statbuf);
|
||||||
|
if (uri->meta->stat.st_mtimensec != statbuf.st_mtimensec) {
|
||||||
|
if (cache_update_entry(i, uri->filename) != 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
17
src/cache.h
17
src/cache.h
@ -9,24 +9,27 @@
|
|||||||
#define NECRONDA_SERVER_CACHE_H
|
#define NECRONDA_SERVER_CACHE_H
|
||||||
|
|
||||||
#include <magic.h>
|
#include <magic.h>
|
||||||
|
#include <sys/ipc.h>
|
||||||
|
#include <sys/shm.h>
|
||||||
|
|
||||||
magic_t magic;
|
magic_t magic;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *etag;
|
char etag[64];
|
||||||
char *type;
|
char type[24];
|
||||||
char *subtype;
|
char charset[16];
|
||||||
char *filename_comp;
|
char filename_comp[256];
|
||||||
struct stat stat;
|
struct stat stat;
|
||||||
} meta_data;
|
} meta_data;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *filename;
|
char filename[256];
|
||||||
unsigned short filename_len;
|
unsigned short filename_len;
|
||||||
|
unsigned char is_valid_etag:1;
|
||||||
|
unsigned char is_updating:1;
|
||||||
meta_data meta;
|
meta_data meta;
|
||||||
} cache_entry;
|
} cache_entry;
|
||||||
|
|
||||||
cache_entry cache[FILE_CACHE_SIZE];
|
cache_entry *cache;
|
||||||
int cache_entries = 0;
|
|
||||||
|
|
||||||
#endif //NECRONDA_SERVER_CACHE_H
|
#endif //NECRONDA_SERVER_CACHE_H
|
||||||
|
85
src/client.c
85
src/client.c
@ -39,11 +39,12 @@ int client_request_handler(sock *client, int req_num) {
|
|||||||
struct timespec begin, end;
|
struct timespec begin, end;
|
||||||
int ret, client_keep_alive, dir_mode;
|
int ret, client_keep_alive, dir_mode;
|
||||||
char buf0[1024], buf1[1024];
|
char buf0[1024], buf1[1024];
|
||||||
char msg_buf[4096], msg_pre_buf[4096];
|
char msg_buf[4096], msg_pre_buf[4096], err_msg[256];
|
||||||
char err_msg[256];
|
char buffer[CHUNK_SIZE];
|
||||||
err_msg[0] = 0;
|
err_msg[0] = 0;
|
||||||
char *host, *hdr_connection, *webroot;
|
char *host, *hdr_connection, *webroot;
|
||||||
unsigned long content_length = 0;
|
unsigned long content_length = 0;
|
||||||
|
FILE *file = NULL;
|
||||||
|
|
||||||
http_res res;
|
http_res res;
|
||||||
sprintf(res.version, "1.1");
|
sprintf(res.version, "1.1");
|
||||||
@ -165,13 +166,27 @@ int client_request_handler(sock *client, int req_num) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (uri.is_static) {
|
if (uri.is_static) {
|
||||||
uri_init_cache(&uri);
|
res.status = http_get_status(200);
|
||||||
http_add_header_field(&res.hdr, "Allow", "GET, HEAD");
|
http_add_header_field(&res.hdr, "Allow", "GET, HEAD");
|
||||||
http_add_header_field(&res.hdr, "Accept-Ranges", "bytes");
|
http_add_header_field(&res.hdr, "Accept-Ranges", "bytes");
|
||||||
if (strncmp(req.method, "GET", 3) != 0 && strncmp(req.method, "HEAD", 4) != 0) {
|
if (strncmp(req.method, "GET", 3) != 0 && strncmp(req.method, "HEAD", 4) != 0) {
|
||||||
res.status = http_get_status(405);
|
res.status = http_get_status(405);
|
||||||
goto respond;
|
goto respond;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = uri_cache_init(&uri);
|
||||||
|
if (ret != 0) {
|
||||||
|
res.status = http_get_status(500);
|
||||||
|
sprintf(err_msg, "Unable to communicate with internal file cache.");
|
||||||
|
goto respond;
|
||||||
|
}
|
||||||
|
sprintf(buf0, "%s, charset=%s", uri.meta->type, uri.meta->charset);
|
||||||
|
http_add_header_field(&res.hdr, "Content-Type", buf0);
|
||||||
|
|
||||||
|
file = fopen(uri.filename, "rb");
|
||||||
|
fseek(file, 0, 2);
|
||||||
|
content_length = ftell(file);
|
||||||
|
fseek(file, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
respond:
|
respond:
|
||||||
@ -185,31 +200,63 @@ int client_request_handler(sock *client, int req_num) {
|
|||||||
if (http_get_header_field(&res.hdr, "Accept-Ranges", HTTP_PRESERVE_UPPER) == NULL) {
|
if (http_get_header_field(&res.hdr, "Accept-Ranges", HTTP_PRESERVE_UPPER) == NULL) {
|
||||||
http_add_header_field(&res.hdr, "Accept-Ranges", "none");
|
http_add_header_field(&res.hdr, "Accept-Ranges", "none");
|
||||||
}
|
}
|
||||||
unsigned long len = 0;
|
|
||||||
if (res.status->code >= 400 && res.status->code < 600) {
|
if (res.status->code >= 400 && res.status->code < 600) {
|
||||||
http_error_msg *http_msg = http_get_error_msg(res.status->code);
|
http_error_msg *http_msg = http_get_error_msg(res.status->code);
|
||||||
sprintf(msg_pre_buf, http_error_document, res.status->code, res.status->msg,
|
sprintf(msg_pre_buf, http_error_document, res.status->code, res.status->msg,
|
||||||
http_msg != NULL ? http_msg->err_msg : "", err_msg[0] != 0 ? err_msg : "");
|
http_msg != NULL ? http_msg->err_msg : "", err_msg[0] != 0 ? err_msg : "");
|
||||||
len = sprintf(msg_buf, http_default_document, res.status->code, res.status->msg,
|
content_length = sprintf(msg_buf, http_default_document, res.status->code, res.status->msg,
|
||||||
msg_pre_buf, res.status->code >= 300 && res.status->code < 400 ? "info" : "error",
|
msg_pre_buf, res.status->code >= 300 && res.status->code < 400 ? "info" : "error",
|
||||||
http_error_icon, "#C00000");
|
http_error_icon, "#C00000");
|
||||||
sprintf(buf0, "%li", len);
|
|
||||||
http_add_header_field(&res.hdr, "Content-Length", buf0);
|
|
||||||
http_add_header_field(&res.hdr, "Content-Type", "text/html; charset=UTF-8");
|
http_add_header_field(&res.hdr, "Content-Type", "text/html; charset=UTF-8");
|
||||||
} else {
|
|
||||||
sprintf(buf0, "%li", content_length);
|
|
||||||
http_add_header_field(&res.hdr, "Content-Length", buf0);
|
|
||||||
}
|
}
|
||||||
|
sprintf(buf0, "%li", content_length);
|
||||||
|
http_add_header_field(&res.hdr, "Content-Length", buf0);
|
||||||
|
|
||||||
http_send_response(client, &res);
|
http_send_response(client, &res);
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||||
|
char *location = http_get_header_field(&res.hdr, "Location", HTTP_PRESERVE_UPPER);
|
||||||
|
unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000;
|
||||||
|
print("%s%03i %s%s%s (%s)%s", http_get_status_color(res.status), res.status->code, res.status->msg,
|
||||||
|
location != NULL ? " -> " : "", location != NULL ? location : "", format_duration(micros, buf0), CLR_STR);
|
||||||
|
|
||||||
if (strncmp(req.method, "HEAD", 4) != 0) {
|
if (strncmp(req.method, "HEAD", 4) != 0) {
|
||||||
|
unsigned long snd_len = 0;
|
||||||
|
unsigned long len = 0;
|
||||||
if (res.status->code >= 400 && res.status->code < 600) {
|
if (res.status->code >= 400 && res.status->code < 600) {
|
||||||
int snd_len = 0;
|
while (snd_len < content_length) {
|
||||||
while (snd_len < len) {
|
|
||||||
if (client->enc) {
|
if (client->enc) {
|
||||||
ret = SSL_write(client->ssl, msg_buf, (int) (len - snd_len));
|
ret = SSL_write(client->ssl, msg_buf, (int) (content_length - snd_len));
|
||||||
|
if (ret <= 0) {
|
||||||
|
print(ERR_STR "Unable to send: %s" CLR_STR, ssl_get_error(client->ssl, ret));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ret = send(client->socket, msg_buf, len - snd_len, 0);
|
ret = send(client->socket, msg_buf, content_length - snd_len, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
print(ERR_STR "Unable to send: %s" CLR_STR, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
snd_len += ret;
|
||||||
|
}
|
||||||
|
} else if (file != NULL) {
|
||||||
|
while (snd_len < content_length) {
|
||||||
|
len = fread(&buffer, 1, CHUNK_SIZE, file);
|
||||||
|
if (client->enc) {
|
||||||
|
ret = SSL_write(client->ssl, buffer, (int) len);
|
||||||
|
if (ret <= 0) {
|
||||||
|
print(ERR_STR "Unable to send: %s" CLR_STR, ssl_get_error(client->ssl, ret));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = send(client->socket, buffer, len, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
print(ERR_STR "Unable to send: %s" CLR_STR, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ret <= 0) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
snd_len += ret;
|
snd_len += ret;
|
||||||
}
|
}
|
||||||
@ -217,10 +264,8 @@ int client_request_handler(sock *client, int req_num) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC, &end);
|
clock_gettime(CLOCK_MONOTONIC, &end);
|
||||||
char *location = http_get_header_field(&res.hdr, "Location", HTTP_PRESERVE_UPPER);
|
micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000;
|
||||||
unsigned long micros = (end.tv_nsec - begin.tv_nsec) / 1000 + (end.tv_sec - begin.tv_sec) * 1000000;
|
print("Transfer complete: %s", format_duration(micros, buf0));
|
||||||
print("%s%03i %s%s%s (%s)%s", http_get_status_color(res.status), res.status->code, res.status->msg,
|
|
||||||
location != NULL ? " -> " : "", location != NULL ? location : "", format_duration(micros, buf0), CLR_STR);
|
|
||||||
|
|
||||||
uri_free(&uri);
|
uri_free(&uri);
|
||||||
abort:
|
abort:
|
||||||
|
@ -80,6 +80,7 @@ void destroy() {
|
|||||||
if (kills > 0) {
|
if (kills > 0) {
|
||||||
fprintf(stderr, ERR_STR "Killed %i child process(es)" CLR_STR "\n", kills);
|
fprintf(stderr, ERR_STR "Killed %i child process(es)" CLR_STR "\n", kills);
|
||||||
}
|
}
|
||||||
|
cache_unload();
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +138,7 @@ void terminate() {
|
|||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Goodbye\n");
|
fprintf(stderr, "Goodbye\n");
|
||||||
}
|
}
|
||||||
|
cache_unload();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +241,9 @@ int main(int argc, const char *argv[]) {
|
|||||||
signal(SIGINT, terminate);
|
signal(SIGINT, terminate);
|
||||||
signal(SIGTERM, terminate);
|
signal(SIGTERM, terminate);
|
||||||
|
|
||||||
cache_init();
|
if (cache_init() != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
openssl_init();
|
openssl_init();
|
||||||
|
|
||||||
client.ctx = SSL_CTX_new(TLS_server_method());
|
client.ctx = SSL_CTX_new(TLS_server_method());
|
||||||
|
@ -33,8 +33,10 @@
|
|||||||
#define REQ_PER_CONNECTION 100
|
#define REQ_PER_CONNECTION 100
|
||||||
#define CLIENT_TIMEOUT 3600
|
#define CLIENT_TIMEOUT 3600
|
||||||
|
|
||||||
|
#define CHUNK_SIZE 4096
|
||||||
#define CLIENT_MAX_HEADER_SIZE 8192
|
#define CLIENT_MAX_HEADER_SIZE 8192
|
||||||
#define FILE_CACHE_SIZE 1024
|
#define FILE_CACHE_SIZE 1024
|
||||||
|
#define SHM_KEY 255641
|
||||||
|
|
||||||
#define ERR_STR "\x1B[1;31m"
|
#define ERR_STR "\x1B[1;31m"
|
||||||
#define CLR_STR "\x1B[0m"
|
#define CLR_STR "\x1B[0m"
|
||||||
@ -52,6 +54,8 @@
|
|||||||
#define SERVER_STR "Necronda/" NECRONDA_VERSION
|
#define SERVER_STR "Necronda/" NECRONDA_VERSION
|
||||||
#define NECRONDA_DEFAULT "www.necronda.net"
|
#define NECRONDA_DEFAULT "www.necronda.net"
|
||||||
|
|
||||||
|
#define MAGIC_FILE "/usr/share/file/misc/magic.mgc"
|
||||||
|
|
||||||
int SOCKETS[NUM_SOCKETS];
|
int SOCKETS[NUM_SOCKETS];
|
||||||
pid_t CHILDREN[MAX_CHILDREN];
|
pid_t CHILDREN[MAX_CHILDREN];
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo
|
|||||||
uri->query = NULL;
|
uri->query = NULL;
|
||||||
uri->filename = NULL;
|
uri->filename = NULL;
|
||||||
uri->uri = NULL;
|
uri->uri = NULL;
|
||||||
|
uri->meta = NULL;
|
||||||
uri->is_static = 1;
|
uri->is_static = 1;
|
||||||
uri->is_dir = 0;
|
uri->is_dir = 0;
|
||||||
if (uri_str[0] != '/') {
|
if (uri_str[0] != '/') {
|
||||||
@ -155,10 +156,6 @@ int uri_init(http_uri *uri, const char *webroot, const char *uri_str, int dir_mo
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uri_init_cache(http_uri *uri) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void uri_free(http_uri *uri) {
|
void uri_free(http_uri *uri) {
|
||||||
if (uri->webroot != NULL) free(uri->webroot);
|
if (uri->webroot != NULL) free(uri->webroot);
|
||||||
if (uri->req_path != NULL) free(uri->req_path);
|
if (uri->req_path != NULL) free(uri->req_path);
|
||||||
|
@ -23,9 +23,9 @@ typedef struct {
|
|||||||
char *query; // "username=test"
|
char *query; // "username=test"
|
||||||
char *filename; // "/account/index.php"
|
char *filename; // "/account/index.php"
|
||||||
char *uri; // "/account/login?username=test"
|
char *uri; // "/account/login?username=test"
|
||||||
meta_data meta;
|
meta_data *meta;
|
||||||
unsigned int is_static:1;
|
unsigned char is_static:1;
|
||||||
unsigned int is_dir:1;
|
unsigned char is_dir:1;
|
||||||
} http_uri;
|
} http_uri;
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user