#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <stdio.h>
#include <string.h>
#include <event2/http.h>
#include <event2/buffer.h>
#include "static.h"
#include "common.h"
static void _static_hit (struct evhttp_request *request, void *arg) {
struct static_file *ctx = arg;
struct evbuffer *buf = NULL;
int err = -1;
// create the new buffer
if ((buf = evbuffer_new()) == NULL)
ERROR("evbuffer_new");
// XXX: this is so very, very wasteful...
if (evbuffer_add(buf, ctx->mmap_addr, ctx->size))
ERROR("evbuffer_add %zu", ctx->size);
// send the reply...
evhttp_send_reply(request, HTTP_OK, "OK", buf);
err = 0;
error :
// free the buffer
if (buf)
evbuffer_free(buf);
if (err)
evhttp_send_error(request, 505, "Internal Server Error");
}
int static_init (struct static_file *ctx, struct evhttp *http_server, const char *url, const char *path) {
struct stat stat_buf;
// clear the ctx
memset(ctx, 0, sizeof(*ctx));
// open the file...
if ((ctx->fh = open(path, O_RDONLY)) == -1)
PERROR("open(O_RDONLY): %s", path);
// stat the file to get its size
if (fstat(ctx->fh, &stat_buf))
PERROR("fstat: %s", path);
ctx->size = stat_buf.st_size;
// then mmap the file. MAP_POPULATE so that we block now, not later...
if ((ctx->mmap_addr = mmap(NULL, ctx->size, PROT_READ, MAP_SHARED | MAP_POPULATE, ctx->fh, 0)) == MAP_FAILED)
PERROR("mmap: %s 0 -> %zu", path, ctx->size);
// finally, add the HTTP request handler
evhttp_set_cb(http_server, url, &_static_hit, ctx);
INFO("%s -> %s", url, path);
// return
return 0;
error :
static_deinit(ctx);
return -1;
}
void static_deinit (struct static_file *ctx) {
if (ctx->fh)
if (close(ctx->fh))
PWARNING("close");
if (ctx->mmap_addr && ctx->mmap_addr != MAP_FAILED)
if (munmap(ctx->mmap_addr, ctx->size))
PWARNING("munmap");
}
int static_dir_init (struct static_dir *ctx, struct evhttp *http_server, const char *url_prefix, const char *dir_path) {
DIR *dir = NULL;
struct dirent *ent;
int i = 0;
char url_buf[STATIC_PATH_MAX], path_buf[STATIC_PATH_MAX];
// init the ctx
ctx->file_count = 0;
// open the dir
if ((dir = opendir(dir_path)) == NULL)
PERROR("opendir: %s", dir_path);
// scan the files
while ((ent = readdir(dir)) != NULL) {
// skip dotfiles
if (ent->d_name[0] == '.')
continue;
// check we still have space
if (i >= STATIC_DIR_MAX) {
PWARNING("static_dir is full: %d", i);
break;
}
// format the url/path bufs
snprintf(url_buf, STATIC_PATH_MAX, "%s/%s", url_prefix, ent->d_name);
snprintf(path_buf, STATIC_PATH_MAX, "%s/%s", dir_path, ent->d_name);
// init the static_file
static_init(&ctx->files[i], http_server, url_buf, path_buf);
// remember how many files are in use and move on to the next one
ctx->file_count = ++i;
}
// ok
return 0;
error:
static_dir_deinit(ctx);
return -1;
}
void static_dir_deinit (struct static_dir *ctx) {
for (int i = 0; i < ctx->file_count; i++) {
static_deinit(&ctx->files[i]);
}
}