web_main.c
author Tero Marttila <terom@fixme.fi>
Sat, 31 May 2008 19:35:21 +0300
changeset 2 69f8c0acaac7
parent 0 5b010627d7ed
child 3 675be0a45157
permissions -rw-r--r--
working web_main that uses render_remote

committer: Tero Marttila <terom@fixme.fi>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

#include <event.h>
#include <evhttp.h>

#include "render.h"
#include "render_remote.h"
#include "common.h"

// what render node to use
static struct sockaddr_storage render_node;

// info on a render request
struct render_request {
    struct evhttp_request *http_request;

    int headers_sent;
};

void _render_sent (void *arg) {
    struct render_request *ctx = arg;

    // send headers
    evhttp_add_header(ctx->http_request->output_headers, "Content-Type", "image/png");
    evhttp_send_reply_start(ctx->http_request, HTTP_OK, "OK");

    ctx->headers_sent = 1;

    printf("render [%p]: sent headers\n", ctx);
}

void _render_data (struct evbuffer *buf, void *arg) {
    struct render_request *ctx = arg;

    // send chunk
    evhttp_send_reply_chunk(ctx->http_request, buf);
    
    printf("render [%p]: sent chunk\n", ctx);
}

void _render_done (void *arg) {
    struct render_request *ctx = arg;

    // send end
    evhttp_send_reply_end(ctx->http_request);

    printf("render [%p]: done\n", ctx);

    // clean up
    free(ctx);
}

void _render_fail (void *arg) {
    struct render_request *ctx = arg;

    if (ctx->headers_sent) {
        // just terminate the PNG stream where it is
        evhttp_send_reply_end(ctx->http_request);
    } else {
        evhttp_send_error(ctx->http_request, 500, "Internal Server Error");
    }
    
    printf("render [%p]: failed\n", ctx);

    // clean up
    free(ctx);
}

void _http_render_execute (struct evhttp_request *request, u_int32_t img_w, u_int32_t img_h) {
    // render request context
    struct render_request *req_ctx = calloc(1, sizeof(struct render_request));
    
    req_ctx->http_request = request;
    req_ctx->headers_sent = 0;
    
    // render context
    render_t rend_ctx;
    render_init(&rend_ctx, RENDER_PNG);
    render_set_size(&rend_ctx, img_w, img_h);
    render_region_full(&rend_ctx);
    
    // initiate the remote render operation
    if (render_remote(&rend_ctx, &render_node,
        &_render_sent,
        &_render_data,
        &_render_done,
        &_render_fail,
        req_ctx
    )) {
        free(req_ctx);
        fprintf(stderr, "ERR: render_remote\n");
        return;
    }
    
    printf("render [%p]: started\n", req_ctx);
}

/*
 * HTTP request handler
 */
void http_render (struct evhttp_request *request, void *arg) {
    // gather some info about the request
    const char *uri = evhttp_request_uri(request);
    char *peer_address;
    u_short peer_port;

    evhttp_connection_get_peer(request->evcon, &peer_address, &peer_port);
    
    // request arguments
    u_int32_t img_w = 256, img_h = 256;
    struct evkeyval *qarg;
    struct evkeyvalq qargs;

    evhttp_parse_query(uri, &qargs);

    TAILQ_FOREACH(qarg, &qargs, next) {
        if (strcmp(qarg->key, "w") == 0)
            img_w = strtol(qarg->value, NULL, 10);
        else if (strcmp(qarg->key, "h") == 0)
            img_h = strtol(qarg->value, NULL, 10);
    }

    // clean up the qargs (badly named functions :< )
    evhttp_clear_headers(&qargs);

    // request log
    printf("REQ: [%s:%d] uri=%s, img_w=%d, img_h=%d\n", peer_address, peer_port, uri, img_w, img_h);
    
    // do it
    _http_render_execute(request, img_w, img_h);
}

int main (void) {
    // libevent/http init
    struct event_base *ev_base = event_init();

    if (!ev_base)
        die("event_init");

    struct evhttp *http_server = evhttp_new(ev_base);

    if (!http_server)
        die("evhttp_new");
    
    // bind to the correct interface/port
    if (evhttp_bind_socket(http_server, "0.0.0.0", 8117))
        die("evhttp_bind_socket");
    
    // add our http request handler
    evhttp_set_cb(http_server, "/render", &http_render, NULL);

    // hack in our render node
    struct sockaddr_in *render_node_addr = (struct sockaddr_in *) &render_node;

    render_node_addr->sin_family = AF_INET;
    render_node_addr->sin_port = htons(RENDER_PORT);
    inet_aton("127.0.0.1", &render_node_addr->sin_addr);

    // we shall now run
    printf("RUN 0.0.0.0:8117\n");
    
    // run the libevent mainloop
    if (event_dispatch())
        die("event_dispatch");
    
    // clean up
    evhttp_free(http_server);
    
    // successfull exit
    return 0;
}