aboutsummaryrefslogtreecommitdiffstats
path: root/src/blogc-runserver.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/blogc-runserver.c')
-rw-r--r--src/blogc-runserver.c386
1 files changed, 0 insertions, 386 deletions
diff --git a/src/blogc-runserver.c b/src/blogc-runserver.c
deleted file mode 100644
index 2fbbbc7..0000000
--- a/src/blogc-runserver.c
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * blogc: A blog compiler.
- * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br>
- *
- * This program can be distributed under the terms of the BSD License.
- * See the file LICENSE.
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif /* HAVE_CONFIG_H */
-
-#include <event2/event.h>
-#include <event2/http.h>
-#include <event2/buffer.h>
-#include <magic.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include "utils.h"
-
-
-/**
- * this mapping is used to declare "supported" file types, that are forced over
- * whatever detected by libmagic, but we will still use the charset provided by
- * libmagic anyway. it also helps detecting index files when the client asks
- * for a directory.
- */
-static const struct content_type_map {
- const char *mimetype;
- const char *extension;
- const char *index;
-} content_types[] = {
- {"text/html", "html", "index.html"},
- {"text/html", "htm", "index.htm"},
- {"text/xml", "xml", "index.xml"},
- {"text/plain", "txt", "index.txt"},
- {"text/css", "css", NULL},
- {"application/javascript", "js", NULL},
- {NULL, NULL, NULL}
-};
-
-
-static magic_t magic_all = NULL;
-static magic_t magic_charset = NULL;
-
-
-static const char*
-get_extension(const char *filename)
-{
- const char *ext = NULL;
- unsigned int i;
- for (i = strlen(filename); i > 0; i--) {
- if (filename[i] == '.') {
- ext = filename + i + 1;
- break;
- }
- }
- if (i == 0)
- return NULL;
- return ext;
-}
-
-
-static char*
-guess_content_type(const char *filename, int fd)
-{
- int newfd;
-
- // try "supported" types first, and just use libmagic for charset
- const char *extension = get_extension(filename);
- if (extension == NULL)
- goto libmagic;
- const char *supported = NULL;
- for (unsigned int i = 0; content_types[i].extension != NULL; i++)
- if (0 == strcmp(content_types[i].extension, extension))
- supported = content_types[i].mimetype;
- if (supported != NULL) {
- newfd = dup(fd);
- if (-1 != newfd) {
- const char* charset = magic_descriptor(magic_charset, newfd);
- close(newfd);
- if (charset != NULL)
- return sb_strdup_printf("%s; charset=%s", supported, charset);
- }
- return sb_strdup(supported);
- }
-
-libmagic:
-
- // fallback to use libmagic for everything
- newfd = dup(fd);
- if (-1 != newfd) {
- const char* content_type = magic_descriptor(magic_all, newfd);
- close(newfd);
- if (content_type != NULL)
- return sb_strdup(content_type);
- }
- return sb_strdup("application/octet-stream");
-}
-
-
-static void
-handler(struct evhttp_request *request, void *ptr)
-{
- const char *root = ptr;
- const char *uri = evhttp_request_get_uri(request);
-
- struct evhttp_uri *decoded_uri = evhttp_uri_parse(uri);
- if (decoded_uri == NULL) {
- evhttp_send_error(request, 400, "Bad request");
- return;
- }
-
- const char *path = evhttp_uri_get_path(decoded_uri);
- if (path == NULL)
- path = "/";
-
- char *decoded_path = evhttp_uridecode(path, 0, NULL);
- if (decoded_path == NULL) {
- evhttp_send_error(request, 400, "Bad request");
- goto point1;
- }
-
- char *abs_path = sb_strdup_printf("%s/%s", root, decoded_path);
- char *real_path = realpath(abs_path, NULL);
- free(abs_path);
-
- if (real_path == NULL) {
- evhttp_send_error(request, 404, "Not found");
- goto point2;
- }
-
- char *real_root = realpath(root, NULL);
- if (real_root == NULL) {
- evhttp_send_error(request, 500, "Internal server error");
- goto point3;
- }
-
- if (0 != strncmp(real_root, real_path, strlen(real_root))) {
- evhttp_send_error(request, 404, "Not found");
- goto point4;
- }
-
- struct stat st;
- if (0 > stat(real_path, &st)) {
- evhttp_send_error(request, 404, "Not found");
- goto point4;
- }
-
- bool add_slash = false;
-
- if (S_ISDIR(st.st_mode)) {
- char *found = NULL;
-
- for (unsigned int i = 0; content_types[i].mimetype != NULL; i++) {
- if (content_types[i].index == NULL)
- continue;
- char *f = sb_strdup_printf("%s/%s", real_path,
- content_types[i].index);
- if (0 == access(f, F_OK)) {
- found = sb_strdup(f);
- break;
- }
- free(f);
- }
-
- if (found == NULL) {
- evhttp_send_error(request, 403, "Forbidden");
- goto point4;
- }
-
- size_t path_len = strlen(path);
- if (path_len > 0 && path[path_len - 1] != '/')
- add_slash = true;
-
- free(real_path);
- real_path = found;
- }
-
- int fd;
- if ((fd = open(real_path, O_RDONLY)) < 0) {
- evhttp_send_error(request, 500, "Internal server error");
- goto point4;
- }
-
- char *type = guess_content_type(real_path, fd);
-
- if (fstat(fd, &st) < 0) {
- evhttp_send_error(request, 500, "Internal server error");
- goto point5;
- }
-
- struct evkeyvalq *headers = evhttp_request_get_output_headers(request);
-
- if (add_slash) {
- char *tmp = sb_strdup_printf("%s/", path);
- evhttp_add_header(headers, "Location", tmp);
- free(tmp);
- // production webservers usually returns 301 in such cases, but 302 is
- // better for development/testing.
- evhttp_send_reply(request, 302, "Found", NULL);
- goto point5;
- }
-
- evhttp_add_header(headers, "Content-Type", type);
- char *content_length = sb_strdup_printf("%zu", st.st_size);
- evhttp_add_header(headers, "Content-Length", content_length);
- free(content_length);
-
- struct evbuffer *evb = evbuffer_new();
- evbuffer_add_file(evb, fd, 0, st.st_size);
- evhttp_send_reply(request, 200, "OK", evb);
-
-point5:
- free(type);
-point4:
- free(real_root);
-point3:
- free(real_path);
-point2:
- free(decoded_path);
-point1:
- evhttp_uri_free(decoded_uri);
-}
-
-
-static int
-runserver(const char *address, unsigned short port, const char *root)
-{
- struct event_base *base = event_base_new();
- if (base == NULL) {
- fprintf(stderr, "error: failed to initialize event base\n");
- return 1;
- }
-
- struct evhttp *http = evhttp_new(base);
- if (http == NULL) {
- fprintf(stderr, "error: failed to initialize HTTP server\n");
- return 1;
- }
-
- evhttp_set_gencb(http, handler, (char*) root);
-
- evhttp_set_allowed_methods(http, EVHTTP_REQ_GET | EVHTTP_REQ_HEAD);
-
- if (0 != evhttp_bind_socket(http, address, port)) {
- fprintf(stderr, "error: failed to bind socket to %s:%d\n", address,
- port);
- return 1;
- }
-
- fprintf(stderr, " * Running on http://%s:%d/\n", address, port);
-
- event_base_dispatch(base);
-
- return 0;
-}
-
-
-static void
-print_help(void)
-{
- printf(
- "usage:\n"
- " blogc-runserver [-h] [-v] [-t HOST] [-p PORT] DOCROOT\n"
- " - A simple HTTP server to test blogc websites.\n"
- "\n"
- "positional arguments:\n"
- " DOCROOT document root directory\n"
- "\n"
- "optional arguments:\n"
- " -h show this help message and exit\n"
- " -v show version and exit\n"
- " -t HOST set server listen address (default: 127.0.0.1)\n"
- " -p PORT set server listen port (default: 8080)\n");
-}
-
-
-static void
-print_usage(void)
-{
- printf("usage: blogc-runserver [-h] [-v] [-t HOST] [-p PORT] DOCROOT\n");
-}
-
-
-int
-main(int argc, char **argv)
-{
- signal(SIGPIPE, SIG_IGN);
-
- int rv = 0;
- char *host = NULL;
- char *docroot = NULL;
- unsigned short port = 8080;
-
- unsigned int args = 0;
-
- for (unsigned int i = 1; i < argc; i++) {
- if (argv[i][0] == '-') {
- switch (argv[i][1]) {
- case 'h':
- print_help();
- goto cleanup;
- case 'v':
- printf("%s\n", PACKAGE_STRING);
- goto cleanup;
- case 't':
- if (argv[i][2] != '\0')
- host = sb_strdup(argv[i] + 2);
- else
- host = sb_strdup(argv[++i]);
- break;
- case 'p':
- if (argv[i][2] != '\0')
- port = strtoul(argv[i] + 2, NULL, 10);
- else
- port = strtoul(argv[++i], NULL, 10);
- break;
- default:
- print_usage();
- fprintf(stderr, "blogc-runserver: error: invalid "
- "argument: -%c\n", argv[i][1]);
- rv = 2;
- goto cleanup;
- }
- }
- else {
- if (args > 0) {
- print_usage();
- fprintf(stderr, "blogc-runserver: error: only one positional "
- "argument allowed\n");
- rv = 2;
- goto cleanup;
- }
- args++;
- docroot = sb_strdup(argv[i]);
- }
- }
-
- if (docroot == NULL) {
- print_usage();
- fprintf(stderr, "blogc-runserver: error: document root directory "
- "required\n");
- rv = 2;
- goto cleanup;
- }
-
- if (host == NULL)
- host = sb_strdup("127.0.0.1");
-
- magic_all = magic_open(MAGIC_MIME);
- magic_charset = magic_open(MAGIC_MIME_ENCODING);
- if (magic_all == NULL || magic_charset == NULL) {
- fprintf(stderr, "error: failed to initialize libmagic\n");
- rv = 1;
- goto cleanup;
- }
-
- if ((0 != magic_load(magic_all, NULL)) ||
- (0 != magic_load(magic_charset, NULL)))
- {
- fprintf(stderr, "error: failed to load libmagic data\n");
- magic_close(magic_all);
- magic_close(magic_charset);
- rv = 1;
- goto cleanup;
- }
-
- rv = runserver(host, port, docroot);
-
- magic_close(magic_all);
- magic_close(magic_charset);
-
-cleanup:
- free(host);
- free(docroot);
-
- return rv;
-}