static.c
author Tero Marttila <terom@fixme.fi>
Wed, 27 Aug 2008 21:30:32 +0300
changeset 41 540737bf6bac
parent 27 1e79b4cc8f1b
permissions -rw-r--r--
sending requests, and partial support for receiving -- incomplete, not tested
#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]);
    }
}