terom@12: #include terom@12: #include terom@12: #include terom@0: #include terom@12: #include terom@12: #include terom@12: #include terom@12: #include terom@10: #include terom@6: #include terom@15: #include terom@0: terom@4: #include terom@4: #include terom@12: #include terom@4: #include terom@0: terom@12: #include "common.h" terom@12: #include "http.h" terom@18: #include "render_struct.h" terom@2: #include "render.h" terom@8: #include "remote_node.h" terom@8: #include "remote_pool.h" terom@13: #include "render_remote.h" terom@26: #include "config.h" terom@27: #include "tile.h" terom@27: #include "static.h" terom@13: terom@6: #define MIN_CHUNK_SIZE 4096 terom@3: #define OVERFLOW_BUFFER 4096 terom@3: terom@6: // do not do any userland socket output buffering terom@6: #define HTTP_BUFFER 0 terom@4: terom@4: // what event_base we're using terom@4: static struct event_base *ev_base; terom@4: terom@8: // our render node pool terom@8: static struct remote_pool remote_pool; terom@0: terom@2: // info on a render request terom@2: struct render_request { terom@2: struct evhttp_request *http_request; terom@0: terom@2: int headers_sent; terom@3: terom@19: struct render_remote *render_info; terom@3: terom@3: size_t bytes_sent; terom@6: terom@6: int paused; terom@2: }; terom@0: terom@6: // cb func prototypes terom@13: static void _render_http_written (struct evhttp_request *request, void *arg); terom@6: terom@13: static void _render_cleanup (struct render_request *ctx) { terom@15: if (ctx->render_info) terom@19: render_remote_free(ctx->render_info); terom@15: terom@3: free(ctx); terom@3: } terom@3: terom@13: static void _render_sent (void *arg) { terom@2: struct render_request *ctx = arg; terom@2: terom@2: // send headers terom@4: evhttp_add_header(evhttp_request_get_output_headers(ctx->http_request), "Content-Type", "image/png"); terom@2: evhttp_send_reply_start(ctx->http_request, HTTP_OK, "OK"); terom@6: terom@6: // setup flow-control terom@6: evhttp_set_reply_notify(ctx->http_request, HTTP_BUFFER, &_render_http_written, ctx); terom@2: terom@2: ctx->headers_sent = 1; terom@2: terom@25: INFO("render [%p]: sent headers", ctx); terom@2: } terom@2: terom@13: static void _render_data (struct evbuffer *buf, void *arg) { terom@2: struct render_request *ctx = arg; terom@2: terom@3: size_t buf_size = EVBUFFER_LENGTH(buf); terom@3: terom@19: assert(buf_size > 0); // shouldn't happen anymore with the new render_remote terom@6: terom@6: // check if we are paused terom@6: if (ctx->paused) { terom@6: // we are waiting for the HTTP send buffer to clear, so keep the data in the render buffer terom@25: INFO("render [%p]: delaying data: %zu:%zu bytes", ctx, buf_size, ctx->bytes_sent); terom@6: terom@6: return; terom@6: } terom@6: terom@6: // move chunk to http buffers terom@2: evhttp_send_reply_chunk(ctx->http_request, buf); terom@6: terom@25: INFO("render [%p]: enqueued chunk: %zu/%zu bytes", ctx, buf_size, ctx->bytes_sent); terom@3: terom@6: // mark ourself as paused until httpd tells us to continue terom@6: ctx->paused = 1; terom@6: terom@6: // keep a tally of total sent bytes terom@3: ctx->bytes_sent += buf_size; terom@2: } terom@2: terom@13: static void _render_done (void *arg) { terom@2: struct render_request *ctx = arg; terom@2: terom@2: // send end terom@2: evhttp_send_reply_end(ctx->http_request); terom@2: terom@25: INFO("render [%p]: done: %zu bytes", ctx, ctx->bytes_sent); terom@6: terom@6: // the request is now done, clean up terom@3: _render_cleanup(ctx); terom@2: } terom@2: terom@13: static void _render_fail (void *arg) { terom@2: struct render_request *ctx = arg; terom@2: terom@2: if (ctx->headers_sent) { terom@2: // just terminate the PNG stream where it is terom@2: evhttp_send_reply_end(ctx->http_request); terom@15: terom@2: } else { terom@2: evhttp_send_error(ctx->http_request, 500, "Internal Server Error"); terom@0: } terom@0: terom@25: INFO("render [%p]: failed", ctx); terom@2: terom@3: _render_cleanup(ctx); terom@3: } terom@3: terom@13: static void _render_http_lost (struct evhttp_request *req, void *arg) { terom@3: struct render_request *ctx = arg; terom@3: terom@25: INFO("render [%p]: lost http connection", ctx); terom@3: terom@3: // cancel terom@19: render_remote_cancel(ctx->render_info); terom@15: ctx->render_info = NULL; terom@3: terom@3: _render_cleanup(ctx); terom@2: } terom@2: terom@13: static void _render_http_written (struct evhttp_request *request, void *arg) { terom@6: struct render_request *ctx = arg; terom@6: terom@25: INFO("render [%p]: http available for write", ctx); terom@6: terom@6: // unpause ourself terom@6: ctx->paused = 0; terom@6: terom@15: // any data waiting in the buffer? terom@19: render_remote_flush(ctx->render_info); terom@6: } terom@6: terom@27: static int _http_render_execute (struct evhttp_request *request, struct render *render) { terom@2: // render request context terom@15: struct render_request *ctx = calloc(1, sizeof(struct render_request)); terom@8: terom@15: if (!ctx) terom@8: ERROR("calloc"); terom@2: terom@15: ctx->http_request = request; terom@15: ctx->headers_sent = 0; terom@15: ctx->bytes_sent = 0; terom@2: terom@2: // initiate the remote render operation terom@27: if ((ctx->render_info = render_remote(render, &remote_pool, terom@2: &_render_sent, terom@2: &_render_data, terom@2: &_render_done, terom@2: &_render_fail, terom@15: ctx terom@8: )) == NULL) terom@19: ERROR("render_remote"); terom@15: terom@15: // set chunk size terom@19: render_remote_set_recv(ctx->render_info, MIN_CHUNK_SIZE, OVERFLOW_BUFFER); terom@8: terom@3: // set close cb terom@15: evhttp_set_reply_abortcb(request, &_render_http_lost, ctx); terom@0: terom@25: INFO("render [%p]: started", ctx); terom@8: terom@27: return 0; terom@8: terom@8: error: terom@27: _render_cleanup(ctx); terom@8: terom@27: return -1; terom@0: } terom@0: terom@2: /* terom@27: * HTTP request handlers terom@2: */ terom@0: void http_render (struct evhttp_request *request, void *arg) { terom@0: // gather some info about the request terom@4: const char *uri = evhttp_request_get_uri(request); terom@0: char *peer_address; terom@27: unsigned short peer_port; terom@27: struct render render; terom@0: terom@4: evhttp_request_get_peer(request, &peer_address, &peer_port); terom@0: terom@27: u_int32_t img_w = 256, img_h = 256; terom@12: terom@12: // parse request arguments terom@10: struct http_qarg qarg_spec[] = { terom@27: { "w", QARG_UINT32, &img_w, QARG_OPTIONAL }, terom@27: { "h", QARG_UINT32, &img_h, QARG_OPTIONAL }, terom@27: { NULL, QARG_END, NULL, 0 } terom@4: }; terom@4: terom@27: if (http_qarg_parse(request, qarg_spec)) terom@27: goto error; terom@27: terom@27: // build the render op terom@27: if ( terom@27: render_init(&render) terom@27: || render_set_mode(&render, RENDER_PNG) terom@27: || render_set_size(&render, img_w, img_h) terom@27: || render_region_full(&render) terom@27: ) terom@27: ERROR("render_*"); terom@0: terom@0: // request log terom@27: INFO("REQ: [%s:%d] method=%d, uri=%s, img_w=%u, img_h=%u", peer_address, peer_port, evhttp_request_get_type(request), uri, img_w, img_h); terom@0: terom@2: // do it terom@27: if (_http_render_execute(request, &render)) terom@27: goto error; terom@27: terom@27: return; terom@27: terom@27: error: terom@27: evhttp_send_error(request, 500, "Internal Server Error"); terom@0: } terom@0: terom@27: void http_tile (struct evhttp_request *request, void *arg) { terom@27: // gather some info about the request terom@27: const char *uri = evhttp_request_get_uri(request); terom@27: char *peer_address; terom@27: unsigned short peer_port; terom@27: struct render render; terom@27: terom@27: evhttp_request_get_peer(request, &peer_address, &peer_port); terom@27: terom@27: u_int32_t x, y, sw, sh; terom@27: u_int16_t z; terom@27: terom@27: // parse request arguments terom@27: struct http_qarg qarg_spec[] = { terom@27: { "x", QARG_UINT32, &x, QARG_REQUIRED }, terom@27: { "y", QARG_UINT32, &y, QARG_REQUIRED }, terom@27: { "z", QARG_UINT16, &z, QARG_REQUIRED }, terom@27: { "sw", QARG_UINT32, &sw, QARG_REQUIRED }, terom@27: { "sh", QARG_UINT32, &sh, QARG_REQUIRED }, terom@27: { NULL, QARG_END, NULL, 0 } terom@27: }; terom@27: terom@27: if (http_qarg_parse(request, qarg_spec)) terom@27: goto error; terom@27: terom@27: // build the render op terom@27: if ( terom@27: render_init(&render) terom@27: || render_set_mode(&render, RENDER_PNG) terom@27: || render_set_tile(&render, sw, sh, x, y, z) terom@27: ) terom@27: ERROR("render_*"); terom@27: terom@27: // request log terom@27: INFO("REQ: [%s:%d] method=%d, uri=%s, pos=(%u, %u), zoom=%hu, screen=[%ux%u]", terom@27: peer_address, peer_port, evhttp_request_get_type(request), uri, x, y, z, sw, sh); terom@27: terom@27: // do it terom@27: if (_http_render_execute(request, &render)) terom@27: goto error; terom@27: terom@27: return; terom@27: terom@27: error: terom@27: evhttp_send_error(request, 500, "Internal Server Error"); terom@27: } terom@27: terom@27: /* terom@27: * Signal handling terom@27: */ terom@3: struct event ev_sigint; terom@3: terom@3: void sigint_handler (int signal, short event, void *arg) { terom@25: INFO("SIGINT: shutting down"); terom@3: terom@4: if (event_base_loopexit(ev_base, NULL)) terom@3: err_exit("event_loopexit"); terom@3: } terom@3: terom@3: void signals_init () { terom@8: // handle SIGINT terom@3: signal_set(&ev_sigint, SIGINT, &sigint_handler, NULL); terom@3: signal_add(&ev_sigint, NULL); terom@8: terom@8: // ignore SIGPIPE terom@8: struct sigaction sigpipe; terom@8: memset(&sigpipe, 0, sizeof(sigpipe)); terom@8: terom@8: sigpipe.sa_handler = SIG_IGN; terom@8: terom@8: sigaction(SIGPIPE, &sigpipe, NULL); terom@3: } terom@3: terom@3: void signals_deinit () { terom@3: signal_del(&ev_sigint); terom@3: } terom@3: terom@6: void log_null (int severity, const char *msg) { terom@6: // ignore terom@6: } terom@6: terom@6: int main (int argc, char **argv) { terom@3: // libevent init terom@4: ev_base = event_init(); terom@0: terom@0: if (!ev_base) terom@12: FATAL("event_init"); terom@3: terom@8: // set up our render node pool terom@8: remote_pool_init(&remote_pool); terom@8: terom@8: // process arguments terom@6: int opt; terom@6: int enable_debug = 0; terom@6: terom@8: while ((opt = getopt(argc, argv, "dp:r:")) != -1) { terom@6: switch (opt) { terom@8: case 'p': terom@8: // populate the pool from a file terom@8: if (remote_pool_load(&remote_pool, optarg)) terom@8: return 1; terom@8: terom@8: break; terom@8: terom@8: case 'r': terom@8: // add the given render node to the pool terom@26: if (remote_pool_add(&remote_pool, optarg)) terom@8: return 1; terom@8: terom@8: break; terom@8: terom@6: case 'd': terom@6: // enable libevent debugging terom@6: enable_debug = 1; terom@6: break; terom@6: terom@6: default: terom@8: err_exit("Usage: %s [-d] (-p pool_file | -r hostname[:port] | ...)", argv[0]); terom@6: terom@6: } terom@6: } terom@8: terom@8: int pool_size = remote_pool_size(&remote_pool); terom@8: terom@12: if (!pool_size) terom@12: FATAL("No remote render nodes given"); terom@12: terom@8: terom@25: INFO("Registered %d render nodes in our pool", pool_size); terom@6: terom@6: // per default it is enabled terom@6: if (!enable_debug) terom@6: event_set_log_callback(&log_null); terom@6: terom@3: // handle signals terom@3: signals_init(); terom@6: terom@3: // evhttp init terom@0: struct evhttp *http_server = evhttp_new(ev_base); terom@0: terom@0: if (!http_server) terom@12: FATAL("evhttp_new"); terom@0: terom@0: // bind to the correct interface/port terom@0: if (evhttp_bind_socket(http_server, "0.0.0.0", 8117)) terom@12: FATAL("evhttp_bind_socket"); terom@0: terom@27: // add our http request handlers terom@27: evhttp_set_cb(http_server, "/render", &http_render, NULL); terom@27: evhttp_set_cb(http_server, "/tile", &http_tile, NULL); terom@27: terom@27: // and then static files terom@27: struct static_file static_index; terom@27: struct static_dir static_files; terom@27: terom@27: if (static_init(&static_index, http_server, "/", "static/index.html")) terom@27: FATAL("static_init: index.html"); terom@27: terom@27: if (static_dir_init(&static_files, http_server, "/static", "static")) terom@27: FATAL("static_dir_init: static"); terom@2: terom@0: // we shall now run terom@25: INFO("RUN 0.0.0.0:8117"); terom@0: terom@0: // run the libevent mainloop terom@4: if (event_base_dispatch(ev_base)) terom@12: WARNING("event_dispatch"); terom@3: terom@25: INFO("SHUTDOWN"); terom@0: terom@0: // clean up terom@3: signals_deinit(); terom@0: evhttp_free(http_server); terom@3: event_base_free(ev_base); terom@0: terom@0: // successfull exit terom@0: return 0; terom@0: }