# HG changeset patch # User John Tsiombikas # Date 1429386477 -10800 # Node ID 0dd50a23f3dd1cb7f8a160feb9cd86e88c7e44f0 # Parent 0244b08cc9d3ffb76a12ecb3e6f0c9b8beb0293b separated all the tinyweb functionality out as a library diff -r 0244b08cc9d3 -r 0dd50a23f3dd .hgignore --- a/.hgignore Sat Apr 18 21:36:07 2015 +0300 +++ b/.hgignore Sat Apr 18 22:47:57 2015 +0300 @@ -1,4 +1,7 @@ \.o$ \.d$ \.swp$ +\.a$ +\.so$ +\.so\. ^tinywebd$ diff -r 0244b08cc9d3 -r 0dd50a23f3dd Makefile --- a/Makefile Sat Apr 18 21:36:07 2015 +0300 +++ b/Makefile Sat Apr 18 22:47:57 2015 +0300 @@ -2,12 +2,17 @@ obj = $(src:.c=.o) dep = $(obj:.o=.d) bin = tinywebd +weblib = libtinyweb/libtinyweb.so -CFLAGS = -pedantic -Wall -g +CFLAGS = -pedantic -Wall -g -Ilibtinyweb/src +LDFLAGS = -Llibtinyweb -Wl,-rpath=libtinyweb -ltinyweb -$(bin): $(obj) +$(bin): $(obj) $(weblib) $(CC) -o $@ $(obj) $(LDFLAGS) +$(weblib): + $(MAKE) -C libtinyweb + -include $(dep) %.d: %.c @@ -16,3 +21,12 @@ .PHONY: clean clean: rm -f $(obj) $(bin) + +.PHONY: install +install: $(bin) + mkdir -p $(PREFIX)/bin + cp $(bin) $(DESTDIR)$(PREFIX)/bin/$(bin) + +.PHONY: uninstall +uninstall: + rm -f $(DESTDIR)$(PREFIX)/bin/$(bin) diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/Makefile Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,73 @@ +PREFIX ?= /usr/local + +src = $(wildcard src/*.c) +obj = $(src:.c=.o) +dep = $(obj:.o=.d) +name = tinyweb + +CFLAGS = -pedantic -Wall -g $(pic) + +sys = $(shell uname -s) + +so_major = 0 +so_minor = 1 +alib = lib$(name).a + +ifeq ($(sys), Darwin) + solib = lib$(name).dylib + shared = -dynamiclib +else + ldname = lib$(name).so + soname = lib$(name).so.$(so_major) + solib = lib$(name).so.$(so_major).$(so_minor) + shared = -shared -Wl,-soname=$(soname) + pic = -fPIC +endif + +.PHONY: all +all: $(alib) $(solib) + +$(solib): $(obj) + $(CC) -o $@ $(shared) $(obj) $(LDFLAGS) + [ -n "$(soname)" ] && \ + ln -s $(solib) $(soname) && \ + ln -s $(soname) $(ldname) || true + +$(alib): $(obj) + $(AR) rcs $@ $(obj) + +-include $(dep) + +%.d: %.c + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +.PHONY: clean +clean: + rm -f $(obj) $(alib) $(solib) + +.PHONY: cleandep +cleandep: + rm -f $(dep) + +.PHONY: install +install: $(alib) $(solib) + mkdir -p $(DESTDIR)$(PREFIX)/lib $(DESTDIR)$(PREFIX)/include + cp src/tinyweb.h $(DESTDIR)$(PREFIX)/include/tinyweb.h + cp $(alib) $(DESTDIR)$(PREFIX)/lib/$(alib) + cp $(solib) $(DESTDIR)$(PREFIX)/lib/$(solib) + [ -n "$(soname)" ] && \ + cd $(DESTDIR)$(PREFIX)/lib && \ + ln -s $(solib) $(soname) && \ + ln -s $(soname) $(ldname) || \ + true + +.PHONY: uninstall +uninstall: + rm -f $(DESTDIR)$(PREFIX)/include/tinyweb.h + rm -f $(DESTDIR)$(PREFIX)/lib/$(alib) + rm -f $(DESTDIR)$(PREFIX)/lib/$(solib) + [ -n "$(soname)" ] && \ + rm -f $(DESTDIR)$(PREFIX)/lib/$(soname) && \ + rm -f $(DESTDIR)$(PREFIX)/lib/$(ldname) || \ + true + diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/src/http.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/src/http.c Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,324 @@ +#include +#include +#include +#include +#include +#include +#include "http.h" +#include "logger.h" + + +static const char *http_method_str[] = { + "", + "OPTIONS", + "GET", + "HEAD", + "POST", + "PUT", + "DELETE", + "TRACE", + "CONNECT", + 0 +}; + + +/* HTTP 1xx message strings */ +static const char *http_msg1xx[] = { + "Continue", /* 100 */ + "Switching Protocols" /* 101 */ +}; + +/* HTTP 2xx message strings */ +static const char *http_msg2xx[] = { + "OK", /* 200 */ + "Created", /* 201 */ + "Accepted", /* 202 */ + "Non-Authoritative Information", /* 203 */ + "No Content", /* 204 */ + "Reset Content", /* 205 */ + "Partial Content" /* 206 */ +}; + +/* HTTP 3xx message strings */ +static const char *http_msg3xx[] = { + "Multiple Choices", /* 300 */ + "Moved Permanently", /* 301 */ + "Found", /* 302 */ + "See Other", /* 303 */ + "Not Modified", /* 304 */ + "Use Proxy", /* 305 */ + "", /* 306 is undefined? */ + "Temporary Redirect" /* 307 */ +}; + +/* HTTP 4xx error strings */ +static const char *http_msg4xx[] = { + "Bad Request", /* 400 */ + "Unauthorized", /* 401 */ + "What the Fuck?", /* 402 */ + "Forbidden", /* 403 */ + "Not Found", /* 404 */ + "Method Not Allowed", /* 405 */ + "Not Acceptable", /* 406 */ + "Proxy Authentication Required", /* 407 */ + "Request Time-out", /* 408 */ + "Conflict", /* 409 */ + "Gone", /* 410 */ + "Length Required", /* 411 */ + "Precondition Failed", /* 412 */ + "Request Entity Too Large", /* 413 */ + "Request-URI Too Large", /* 414 */ + "Unsupported Media Type", /* 415 */ + "Request range not satisfiable", /* 416 */ + "Expectation Failed" /* 417 */ +}; + +/* HTTP 5xx error strings */ +static const char *http_msg5xx[] = { + "Internal Server Error", /* 500 */ + "Not Implemented", /* 501 */ + "Bad Gateway", /* 502 */ + "Service Unavailable", /* 503 */ + "Gateway Time-out", /* 504 */ + "HTTP Version not supported" /* 505 */ +}; + + +static enum http_method parse_method(const char *s); + + +int http_parse_request(struct http_req_header *hdr, const char *buf, int bufsz) +{ + int i, nlines = 0; + char *rqline = 0; + char *method, *uri, *version, *ptr; + const char *startln, *endln; + + memset(hdr, 0, sizeof *hdr); + + for(i=1; i 4 && buf[i - 2] == '\n' && buf[i - 3] == '\r') { + hdr->body_offset = i + 1; + break; + } + } + } + + if(!rqline) { + return HTTP_HDR_PARTIAL; + } + + ptr = rqline; + while(*ptr && isspace(*ptr)) ++ptr; + method = ptr; + + /* parse the request line */ + while(*ptr && !isspace(*ptr)) ++ptr; + while(*ptr && isspace(*ptr)) *ptr++ = 0; + + uri = ptr; + while(*ptr && !isspace(*ptr)) ++ptr; + while(*ptr && isspace(*ptr)) *ptr++ = 0; + + version = ptr; + while(*ptr && !isspace(*ptr)) ++ptr; + while(*ptr && isspace(*ptr)) *ptr++ = 0; + + hdr->method = parse_method(method); + hdr->uri = strdup(uri); + if(sscanf(version, "HTTP/%d.%d", &hdr->ver_major, &hdr->ver_minor) != 2) { + fprintf(stderr, "warning: failed to parse HTTP version \"%s\"\n", version); + hdr->ver_major = 1; + hdr->ver_minor = 1; + } + + if(!(hdr->hdrfields = malloc(nlines * sizeof *hdr->hdrfields))) { + perror("failed to allocate memory for the header fields"); + return HTTP_HDR_NOMEM; + } + hdr->num_hdrfields = 0; + + startln = buf; + endln = buf; + for(i=1; ibody_offset - 2; i++) { + if(buf[i] == '\n' && buf[i - 1] == '\r') { + int linesz; + + endln = buf + i - 1; + linesz = endln - startln; + + if(startln > buf) { /* skip first line */ + int idx = hdr->num_hdrfields++; + hdr->hdrfields[idx] = malloc(linesz + 1); + memcpy(hdr->hdrfields[idx], startln, linesz); + hdr->hdrfields[idx][linesz] = 0; + } + startln = endln = buf + i + 1; + } + } + + return HTTP_HDR_OK; +} + +void http_log_request(struct http_req_header *hdr) +{ + int i; + + logmsg("HTTP request header\n"); + logmsg(" method: %s\n", http_method_str[hdr->method]); + logmsg(" uri: %s\n", hdr->uri); + logmsg(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor); + logmsg(" fields (%d):\n", hdr->num_hdrfields); + + for(i=0; inum_hdrfields; i++) { + logmsg(" %s\n", hdr->hdrfields[i]); + } + logmsg("\n"); +} + +void http_destroy_request(struct http_req_header *hdr) +{ + int i; + + if(hdr->hdrfields) { + for(i=0; inum_hdrfields; i++) { + free(hdr->hdrfields[i]); + } + free(hdr->hdrfields); + } + free(hdr->uri); +} + +int http_init_resp(struct http_resp_header *resp) +{ + memset(resp, 0, sizeof *resp); + resp->status = 200; + resp->ver_major = resp->ver_minor = 1; + resp->fields = 0; + resp->num_fields = 0; + + return 0; +} + +int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...) +{ + int sz; + va_list ap; + char *field, **newarr, tmp; + + va_start(ap, fmt); + sz = vsnprintf(&tmp, 0, fmt, ap); + va_end(ap); + + if(sz <= 0) sz = 1023; + if(!(field = malloc(sz + 1))) { + return -1; + } + va_start(ap, fmt); + vsnprintf(field, sz + 1, fmt, ap); + va_end(ap); + + if(!(newarr = realloc(resp->fields, (resp->num_fields + 1) * sizeof *resp->fields))) { + free(field); + return -1; + } + resp->fields = newarr; + + resp->fields[resp->num_fields++] = field; + return 0; +} + +void http_destroy_resp(struct http_resp_header *resp) +{ + int i; + if(resp->fields) { + for(i=0; inum_fields; i++) { + free(resp->fields[i]); + } + free(resp->fields); + resp->fields = 0; + } + resp->num_fields = 0; +} + +int http_serialize_resp(struct http_resp_header *resp, char *buf) +{ + int i, stsize, size, *fsize; + char *ptr, tmp; + + stsize = snprintf(&tmp, 0, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor, + resp->status, http_strmsg(resp->status)); + + fsize = alloca(resp->num_fields * sizeof *fsize); + + size = stsize; + for(i=0; inum_fields; i++) { + int len = strlen(resp->fields[i]) + 2; + fsize[i] = len; + size += len; + } + size += 2; /* CRLF and null */ + + if(buf) { + sprintf(buf, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor, + resp->status, http_strmsg(resp->status)); + + ptr = buf + stsize; + for(i=0; inum_fields; i++) { + sprintf(ptr, "%s\r\n", resp->fields[i]); + ptr += fsize[i]; + } + *ptr++ = '\r'; + *ptr++ = '\n'; + *ptr++ = 0; + } + + return size; +} + +const char *http_strmsg(int code) +{ + static const char **msgxxx[] = { + 0, http_msg1xx, http_msg2xx, http_msg3xx, http_msg4xx, http_msg5xx + }; + static int msgcount[] = { + 0, + sizeof http_msg1xx / sizeof *http_msg1xx, + sizeof http_msg2xx / sizeof *http_msg2xx, + sizeof http_msg3xx / sizeof *http_msg3xx, + sizeof http_msg4xx / sizeof *http_msg4xx, + sizeof http_msg5xx / sizeof *http_msg5xx + }; + + int type = code / 100; + int idx = code % 100; + + if(type < 1 || type >= sizeof msgxxx / sizeof *msgxxx) { + return "Invalid HTTP Status"; + } + + if(idx < 0 || idx >= msgcount[type]) { + return "Unknown HTTP Status"; + } + + return msgxxx[type][idx]; +} + +static enum http_method parse_method(const char *s) +{ + int i; + for(i=0; http_method_str[i]; i++) { + if(strcmp(s, http_method_str[i]) == 0) { + return (enum http_method)i; + } + } + return HTTP_UNKNOWN; +} diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/src/http.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/src/http.h Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,50 @@ +#ifndef HTTP_H_ +#define HTTP_H_ + +enum http_method { + HTTP_UNKNOWN, + HTTP_OPTIONS, + HTTP_GET, + HTTP_HEAD, + HTTP_POST, + HTTP_PUT, + HTTP_DELETE, + HTTP_TRACE, + HTTP_CONNECT, + + NUM_HTTP_METHODS +}; + +struct http_req_header { + enum http_method method; + char *uri; + int ver_major, ver_minor; /* http version */ + char **hdrfields; + int num_hdrfields; + int body_offset; +}; + +struct http_resp_header { + int status; + int ver_major, ver_minor; + char **fields; + int num_fields; +}; + +#define HTTP_HDR_OK 0 +#define HTTP_HDR_INVALID -1 +#define HTTP_HDR_NOMEM -2 +#define HTTP_HDR_PARTIAL -3 + +int http_parse_request(struct http_req_header *hdr, const char *buf, int bufsz); +void http_log_request(struct http_req_header *hdr); +void http_destroy_request(struct http_req_header *hdr); + +int http_init_resp(struct http_resp_header *resp); +int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...); +void http_destroy_resp(struct http_resp_header *resp); +int http_serialize_resp(struct http_resp_header *resp, char *buf); + +const char *http_strmsg(int code); + +#endif /* HTTP_H_ */ diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/src/logger.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/src/logger.c Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include "logger.h" + +static FILE *logfile; + +int set_log_file(const char *fname) +{ + FILE *fp; + + if(!(fp = fopen(fname, "w"))) { + fprintf(stderr, "failed to open logfile: %s: %s\n", fname, strerror(errno)); + return -1; + } + setvbuf(fp, 0, _IONBF, 0); + logfile = fp; + return 0; +} + +void logmsg(const char *fmt, ...) +{ + va_list ap; + + if(!logfile) { + logfile = stderr; + } + + va_start(ap, fmt); + vfprintf(logfile, fmt, ap); + va_end(ap); +} diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/src/logger.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/src/logger.h Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,8 @@ +#ifndef LOGGER_H_ +#define LOGGER_H_ + +int set_log_file(const char *fname); + +void logmsg(const char *fname, ...); + +#endif /* LOGGER_H_ */ diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/src/mime.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/src/mime.c Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,72 @@ +#include +#include +#include "mime.h" +#include "rbtree.h" + +/* TODO: do proper content detection */ +struct mime_type { + const char *suffix, *type; +}; + +static struct mime_type def_types[] = { + {"txt", "text/plain"}, + {"htm", "text/html"}, + {"html", "text/html"}, + {"png", "image/png"}, + {"jpg", "image/jpeg"}, + {"jpeg", "image/jpeg"}, + {"gif", "image/gif"}, + {"bmp", "image/bmp"}, + {"cgi", 0}, + {0, 0} +}; + +static int init_types(void); +static void del_func(struct rbnode *node, void *cls); + +static struct rbtree *types; + +static int init_types(void) +{ + int i; + + if(types) return 0; + + if(!(types = rb_create(RB_KEY_STRING))) { + return -1; + } + rb_set_delete_func(types, del_func, 0); + + for(i=0; def_types[i].suffix; i++) { + add_mime_type(def_types[i].suffix, def_types[i].type); + } + return 0; +} + +static void del_func(struct rbnode *node, void *cls) +{ + free(node->key); + free(node->data); +} + +int add_mime_type(const char *suffix, const char *type) +{ + init_types(); + + return rb_insert(types, strdup(suffix), type ? strdup(type) : 0); +} + +const char *mime_type(const char *path) +{ + const char *suffix; + + init_types(); + + if((suffix = strrchr(path, '.'))) { + struct rbnode *node = rb_find(types, (void*)(suffix + 1)); + if(node) { + return node->data; + } + } + return "text/plain"; +} diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/src/mime.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/src/mime.h Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,7 @@ +#ifndef MIME_H_ +#define MIME_H_ + +int add_mime_type(const char *suffix, const char *type); +const char *mime_type(const char *path); + +#endif /* MIME_H_ */ diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/src/rbtree.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/src/rbtree.c Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,501 @@ +/* +rbtree - simple balanced binary search tree (red-black tree) library. +Copyright (C) 2011-2014 John Tsiombikas + +rbtree is free software, feel free to use, modify, and redistribute it, under +the terms of the 3-clause BSD license. See COPYING for details. + */ +#include +#include +#include +#include +#include "rbtree.h" + +#define INT2PTR(x) ((void*)(intptr_t)(x)) +#define PTR2INT(x) ((int)(intptr_t)(x)) + +struct rbtree { + struct rbnode *root; + + rb_alloc_func_t alloc; + rb_free_func_t free; + + rb_cmp_func_t cmp; + rb_del_func_t del; + void *del_cls; + + struct rbnode *rstack, *iter; +}; + +static int cmpaddr(const void *ap, const void *bp); +static int cmpint(const void *ap, const void *bp); + +static int count_nodes(struct rbnode *node); +static void del_tree(struct rbnode *node, void (*delfunc)(struct rbnode*, void*), void *cls); +static struct rbnode *insert(struct rbtree *rb, struct rbnode *tree, void *key, void *data); +static struct rbnode *delete(struct rbtree *rb, struct rbnode *tree, void *key); +/*static struct rbnode *find(struct rbtree *rb, struct rbnode *node, void *key);*/ +static void traverse(struct rbnode *node, void (*func)(struct rbnode*, void*), void *cls); + +struct rbtree *rb_create(rb_cmp_func_t cmp_func) +{ + struct rbtree *rb; + + if(!(rb = malloc(sizeof *rb))) { + return 0; + } + if(rb_init(rb, cmp_func) == -1) { + free(rb); + return 0; + } + return rb; +} + +void rb_free(struct rbtree *rb) +{ + rb_destroy(rb); + free(rb); +} + + +int rb_init(struct rbtree *rb, rb_cmp_func_t cmp_func) +{ + memset(rb, 0, sizeof *rb); + + if(!cmp_func) { + rb->cmp = cmpaddr; + } else if(cmp_func == RB_KEY_INT) { + rb->cmp = cmpint; + } else if(cmp_func == RB_KEY_STRING) { + rb->cmp = (rb_cmp_func_t)strcmp; + } else { + rb->cmp = cmp_func; + } + + rb->alloc = malloc; + rb->free = free; + return 0; +} + +void rb_destroy(struct rbtree *rb) +{ + del_tree(rb->root, rb->del, rb->del_cls); +} + +void rb_set_allocator(struct rbtree *rb, rb_alloc_func_t alloc, rb_free_func_t free) +{ + rb->alloc = alloc; + rb->free = free; +} + + +void rb_set_compare_func(struct rbtree *rb, rb_cmp_func_t func) +{ + rb->cmp = func; +} + +void rb_set_delete_func(struct rbtree *rb, rb_del_func_t func, void *cls) +{ + rb->del = func; + rb->del_cls = cls; +} + + +void rb_clear(struct rbtree *rb) +{ + del_tree(rb->root, rb->del, rb->del_cls); + rb->root = 0; +} + +int rb_copy(struct rbtree *dest, struct rbtree *src) +{ + struct rbnode *node; + + rb_clear(dest); + rb_begin(src); + while((node = rb_next(src))) { + if(rb_insert(dest, node->key, node->data) == -1) { + return -1; + } + } + return 0; +} + +int rb_size(struct rbtree *rb) +{ + return count_nodes(rb->root); +} + +int rb_insert(struct rbtree *rb, void *key, void *data) +{ + rb->root = insert(rb, rb->root, key, data); + rb->root->red = 0; + return 0; +} + +int rb_inserti(struct rbtree *rb, int key, void *data) +{ + rb->root = insert(rb, rb->root, INT2PTR(key), data); + rb->root->red = 0; + return 0; +} + + +int rb_delete(struct rbtree *rb, void *key) +{ + rb->root = delete(rb, rb->root, key); + rb->root->red = 0; + return 0; +} + +int rb_deletei(struct rbtree *rb, int key) +{ + rb->root = delete(rb, rb->root, INT2PTR(key)); + rb->root->red = 0; + return 0; +} + + +struct rbnode *rb_find(struct rbtree *rb, void *key) +{ + struct rbnode *node = rb->root; + + while(node) { + int cmp = rb->cmp(key, node->key); + if(cmp == 0) { + return node; + } + node = cmp < 0 ? node->left : node->right; + } + return 0; +} + +struct rbnode *rb_findi(struct rbtree *rb, int key) +{ + return rb_find(rb, INT2PTR(key)); +} + + +void rb_foreach(struct rbtree *rb, void (*func)(struct rbnode*, void*), void *cls) +{ + traverse(rb->root, func, cls); +} + + +struct rbnode *rb_root(struct rbtree *rb) +{ + return rb->root; +} + +void rb_begin(struct rbtree *rb) +{ + rb->rstack = 0; + rb->iter = rb->root; +} + +#define push(sp, x) ((x)->next = (sp), (sp) = (x)) +#define pop(sp) ((sp) = (sp)->next) +#define top(sp) (sp) + +struct rbnode *rb_next(struct rbtree *rb) +{ + struct rbnode *res = 0; + + while(rb->rstack || rb->iter) { + if(rb->iter) { + push(rb->rstack, rb->iter); + rb->iter = rb->iter->left; + } else { + rb->iter = top(rb->rstack); + pop(rb->rstack); + res = rb->iter; + rb->iter = rb->iter->right; + break; + } + } + return res; +} + +void *rb_node_key(struct rbnode *node) +{ + return node ? node->key : 0; +} + +int rb_node_keyi(struct rbnode *node) +{ + return node ? PTR2INT(node->key) : 0; +} + +void *rb_node_data(struct rbnode *node) +{ + return node ? node->data : 0; +} + +static int cmpaddr(const void *ap, const void *bp) +{ + return ap < bp ? -1 : (ap > bp ? 1 : 0); +} + +static int cmpint(const void *ap, const void *bp) +{ + return PTR2INT(ap) - PTR2INT(bp); +} + + +/* ---- left-leaning 2-3 red-black implementation ---- */ + +/* helper prototypes */ +static int is_red(struct rbnode *tree); +static void color_flip(struct rbnode *tree); +static struct rbnode *rot_left(struct rbnode *a); +static struct rbnode *rot_right(struct rbnode *a); +static struct rbnode *find_min(struct rbnode *tree); +static struct rbnode *del_min(struct rbtree *rb, struct rbnode *tree); +/*static struct rbnode *move_red_right(struct rbnode *tree);*/ +static struct rbnode *move_red_left(struct rbnode *tree); +static struct rbnode *fix_up(struct rbnode *tree); + +static int count_nodes(struct rbnode *node) +{ + if(!node) + return 0; + + return 1 + count_nodes(node->left) + count_nodes(node->right); +} + +static void del_tree(struct rbnode *node, rb_del_func_t delfunc, void *cls) +{ + if(!node) + return; + + del_tree(node->left, delfunc, cls); + del_tree(node->right, delfunc, cls); + + if(delfunc) { + delfunc(node, cls); + } + free(node); +} + +static struct rbnode *insert(struct rbtree *rb, struct rbnode *tree, void *key, void *data) +{ + int cmp; + + if(!tree) { + struct rbnode *node = rb->alloc(sizeof *node); + node->red = 1; + node->key = key; + node->data = data; + node->left = node->right = 0; + return node; + } + + cmp = rb->cmp(key, tree->key); + + if(cmp < 0) { + tree->left = insert(rb, tree->left, key, data); + } else if(cmp > 0) { + tree->right = insert(rb, tree->right, key, data); + } else { + tree->data = data; + } + + /* fix right-leaning reds */ + if(is_red(tree->right)) { + tree = rot_left(tree); + } + /* fix two reds in a row */ + if(is_red(tree->left) && is_red(tree->left->left)) { + tree = rot_right(tree); + } + + /* if 4-node, split it by color inversion */ + if(is_red(tree->left) && is_red(tree->right)) { + color_flip(tree); + } + + return tree; +} + +static struct rbnode *delete(struct rbtree *rb, struct rbnode *tree, void *key) +{ + int cmp; + + if(!tree) { + return 0; + } + + cmp = rb->cmp(key, tree->key); + + if(cmp < 0) { + if(!is_red(tree->left) && !is_red(tree->left->left)) { + tree = move_red_left(tree); + } + tree->left = delete(rb, tree->left, key); + } else { + /* need reds on the right */ + if(is_red(tree->left)) { + tree = rot_right(tree); + } + + /* found it at the bottom (XXX what certifies left is null?) */ + if(cmp == 0 && !tree->right) { + if(rb->del) { + rb->del(tree, rb->del_cls); + } + rb->free(tree); + return 0; + } + + if(!is_red(tree->right) && !is_red(tree->right->left)) { + tree = move_red_left(tree); + } + + if(key == tree->key) { + struct rbnode *rmin = find_min(tree->right); + tree->key = rmin->key; + tree->data = rmin->data; + tree->right = del_min(rb, tree->right); + } else { + tree->right = delete(rb, tree->right, key); + } + } + + return fix_up(tree); +} + +/*static struct rbnode *find(struct rbtree *rb, struct rbnode *node, void *key) +{ + int cmp; + + if(!node) + return 0; + + if((cmp = rb->cmp(key, node->key)) == 0) { + return node; + } + return find(rb, cmp < 0 ? node->left : node->right, key); +}*/ + +static void traverse(struct rbnode *node, void (*func)(struct rbnode*, void*), void *cls) +{ + if(!node) + return; + + traverse(node->left, func, cls); + func(node, cls); + traverse(node->right, func, cls); +} + +/* helpers */ + +static int is_red(struct rbnode *tree) +{ + return tree && tree->red; +} + +static void color_flip(struct rbnode *tree) +{ + tree->red = !tree->red; + tree->left->red = !tree->left->red; + tree->right->red = !tree->right->red; +} + +static struct rbnode *rot_left(struct rbnode *a) +{ + struct rbnode *b = a->right; + a->right = b->left; + b->left = a; + b->red = a->red; + a->red = 1; + return b; +} + +static struct rbnode *rot_right(struct rbnode *a) +{ + struct rbnode *b = a->left; + a->left = b->right; + b->right = a; + b->red = a->red; + a->red = 1; + return b; +} + +static struct rbnode *find_min(struct rbnode *tree) +{ + if(!tree) + return 0; + + while(tree->left) { + tree = tree->left; + } + return tree; +} + +static struct rbnode *del_min(struct rbtree *rb, struct rbnode *tree) +{ + if(!tree->left) { + if(rb->del) { + rb->del(tree->left, rb->del_cls); + } + rb->free(tree->left); + return 0; + } + + /* make sure we've got red (3/4-nodes) at the left side so we can delete at the bottom */ + if(!is_red(tree->left) && !is_red(tree->left->left)) { + tree = move_red_left(tree); + } + tree->left = del_min(rb, tree->left); + + /* fix right-reds, red-reds, and split 4-nodes on the way up */ + return fix_up(tree); +} + +#if 0 +/* push a red link on this node to the right */ +static struct rbnode *move_red_right(struct rbnode *tree) +{ + /* flipping it makes both children go red, so we have a red to the right */ + color_flip(tree); + + /* if after the flip we've got a red-red situation to the left, fix it */ + if(is_red(tree->left->left)) { + tree = rot_right(tree); + color_flip(tree); + } + return tree; +} +#endif + +/* push a red link on this node to the left */ +static struct rbnode *move_red_left(struct rbnode *tree) +{ + /* flipping it makes both children go red, so we have a red to the left */ + color_flip(tree); + + /* if after the flip we've got a red-red on the right-left, fix it */ + if(is_red(tree->right->left)) { + tree->right = rot_right(tree->right); + tree = rot_left(tree); + color_flip(tree); + } + return tree; +} + +static struct rbnode *fix_up(struct rbnode *tree) +{ + /* fix right-leaning */ + if(is_red(tree->right)) { + tree = rot_left(tree); + } + /* change invalid red-red pairs into a proper 4-node */ + if(is_red(tree->left) && is_red(tree->left->left)) { + tree = rot_right(tree); + } + /* split 4-nodes */ + if(is_red(tree->left) && is_red(tree->right)) { + color_flip(tree); + } + return tree; +} diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/src/rbtree.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/src/rbtree.h Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,78 @@ +/* +rbtree - simple balanced binary search tree (red-black tree) library. +Copyright (C) 2011-2014 John Tsiombikas + +rbtree is free software, feel free to use, modify, and redistribute it, under +the terms of the 3-clause BSD license. See COPYING for details. + */ +#ifndef RBTREE_H_ +#define RBTREE_H_ + +struct rbtree; + + +struct rbnode { + void *key, *data; + int red; + struct rbnode *left, *right; + struct rbnode *next; /* for iterator stack */ +}; + + +typedef void *(*rb_alloc_func_t)(size_t); +typedef void (*rb_free_func_t)(void*); + +typedef int (*rb_cmp_func_t)(const void*, const void*); +typedef void (*rb_del_func_t)(struct rbnode*, void*); + +#define RB_KEY_ADDR (rb_cmp_func_t)(0) +#define RB_KEY_INT (rb_cmp_func_t)(1) +#define RB_KEY_STRING (rb_cmp_func_t)(3) + + +#ifdef __cplusplus +extern "C" { +#endif + +struct rbtree *rb_create(rb_cmp_func_t cmp_func); +void rb_free(struct rbtree *rb); + +int rb_init(struct rbtree *rb, rb_cmp_func_t cmp_func); +void rb_destroy(struct rbtree *rb); + +void rb_set_allocator(struct rbtree *rb, rb_alloc_func_t alloc, rb_free_func_t free); +void rb_set_compare_func(struct rbtree *rb, rb_cmp_func_t func); +void rb_set_delete_func(struct rbtree *rb, rb_del_func_t func, void *cls); +/* TODO add user deep copy function */ + +void rb_clear(struct rbtree *rb); +int rb_copy(struct rbtree *dest, struct rbtree *src); + +int rb_size(struct rbtree *rb); + +int rb_insert(struct rbtree *rb, void *key, void *data); +int rb_inserti(struct rbtree *rb, int key, void *data); + +int rb_delete(struct rbtree *rb, void *key); +int rb_deletei(struct rbtree *rb, int key); + +struct rbnode *rb_find(struct rbtree *rb, void *key); +struct rbnode *rb_findi(struct rbtree *rb, int key); + +void rb_foreach(struct rbtree *rb, void (*func)(struct rbnode*, void*), void *cls); + +struct rbnode *rb_root(struct rbtree *rb); + +void rb_begin(struct rbtree *rb); +struct rbnode *rb_next(struct rbtree *rb); + +void *rb_node_key(struct rbnode *node); +int rb_node_keyi(struct rbnode *node); +void *rb_node_data(struct rbnode *node); + +#ifdef __cplusplus +} +#endif + + +#endif /* RBTREE_H_ */ diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/src/tinyweb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/src/tinyweb.c Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,391 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tinyweb.h" +#include "http.h" +#include "mime.h" +#include "logger.h" + +/* HTTP version */ +#define HTTP_VER_MAJOR 1 +#define HTTP_VER_MINOR 1 +#define HTTP_VER_STR "1.1" + +/* maximum request length: 64mb */ +#define MAX_REQ_LENGTH (65536 * 1024) + +struct client { + int s; + char *rcvbuf; + int bufsz; + struct client *next; +}; + +static int accept_conn(int lis); +static void close_conn(struct client *c); +static int handle_client(struct client *c); +static int do_get(struct client *c, const char *uri, int with_body); +static void respond_error(struct client *c, int errcode); + +static int lis = -1; +static int maxfd; +static int port = 8080; +static struct client *clist; +static int num_clients; + +static const char *indexfiles[] = { + "index.cgi", + "index.html", + "index.htm", + 0 +}; + +void tw_set_port(int p) +{ + port = p; +} + +int tw_set_root(const char *path) +{ + return chdir(path); +} + +int tw_set_logfile(const char *fname) +{ + return set_log_file(fname); +} + +int tw_start(void) +{ + int s; + struct sockaddr_in sa; + + logmsg("starting server ...\n"); + + if(lis != -1) { + logmsg("can't start tinyweb server: already running!\n"); + return -1; + } + + if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) { + logmsg("failed to create listening socket: %s\n", strerror(errno)); + return -1; + } + fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK); + + memset(&sa, 0, sizeof sa); + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = INADDR_ANY; + sa.sin_port = htons(port); + + if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) { + logmsg("failed to bind socket to port %d: %s\n", port, strerror(errno)); + return -1; + } + listen(s, 16); + + lis = s; + return s; +} + +int tw_stop(void) +{ + if(lis == -1) { + return -1; + } + + logmsg("stopping server...\n"); + + close(lis); + lis = -1; + + while(clist) { + struct client *c = clist; + clist = clist->next; + close_conn(c); + free(c); + } + clist = 0; + + return 0; +} + +int tw_get_sockets(int *socks) +{ + struct client *c, dummy; + + /* first cleanup the clients marked for removal */ + dummy.next = clist; + c = &dummy; + + while(c->next) { + struct client *n = c->next; + + if(n->s == -1) { + /* marked for removal */ + c->next = n->next; + free(n); + --num_clients; + } else { + c = c->next; + } + } + clist = dummy.next; + + + if(!socks) { + /* just return the count */ + return num_clients + 1; /* +1 for the listening socket */ + } + + /* go through the client list and populate the array */ + maxfd = lis; + *socks++ = lis; + + c = clist; + while(c) { + *socks++ = c->s; + if(c->s > maxfd) { + maxfd = c->s; + } + c = c->next; + } + return num_clients + 1; /* +1 for the listening socket */ +} + +int tw_get_maxfd(void) +{ + return maxfd; +} + +int tw_handle_socket(int s) +{ + struct client *c; + + if(s == lis) { + return accept_conn(s); + } + + /* find which client corresponds to this socket */ + c = clist; + while(c) { + if(c->s == s) { + return handle_client(c); + } + c = c->next; + } + + logmsg("socket %d doesn't correspond to any client\n"); + return -1; +} + +static int accept_conn(int lis) +{ + int s; + struct client *c; + struct sockaddr_in addr; + socklen_t addr_sz = sizeof addr; + + if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) { + logmsg("failed to accept incoming connection: %s\n", strerror(errno)); + return -1; + } + fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK); + + if(!(c = malloc(sizeof *c))) { + logmsg("failed to allocate memory while accepting connection: %s\n", strerror(errno)); + return -1; + } + c->s = s; + c->rcvbuf = 0; + c->bufsz = 0; + c->next = clist; + clist = c; + ++num_clients; + return 0; +} + +static void close_conn(struct client *c) +{ + close(c->s); + c->s = -1; /* mark it for removal */ + free(c->rcvbuf); + c->rcvbuf = 0; +} + +static int handle_client(struct client *c) +{ + struct http_req_header hdr; + static char buf[2048]; + int rdsz, status; + + while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) { + char *newbuf; + int newsz = c->bufsz + rdsz; + if(newsz > MAX_REQ_LENGTH) { + respond_error(c, 413); + return -1; + } + + if(!(newbuf = realloc(c->rcvbuf, newsz + 1))) { + logmsg("failed to allocate %d byte buffer\n", newsz); + respond_error(c, 503); + return -1; + } + + memcpy(newbuf + c->bufsz, buf, rdsz); + newbuf[newsz] = 0; + + c->rcvbuf = newbuf; + c->bufsz = newsz; + } + + if((status = http_parse_request(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) { + http_log_request(&hdr); + switch(status) { + case HTTP_HDR_INVALID: + respond_error(c, 400); + return -1; + + case HTTP_HDR_NOMEM: + respond_error(c, 503); + return -1; + + case HTTP_HDR_PARTIAL: + return 0; /* partial header, continue reading */ + } + } + http_log_request(&hdr); + + /* we only support GET and HEAD at this point, so freak out on anything else */ + switch(hdr.method) { + case HTTP_GET: + if(do_get(c, hdr.uri, 1) == -1) { + return -1; + } + break; + + case HTTP_HEAD: + if(do_get(c, hdr.uri, 0) == -1) { + return -1; + } + break; + + default: + respond_error(c, 501); + return -1; + } + + close_conn(c); + return 0; +} + +static int do_get(struct client *c, const char *uri, int with_body) +{ + const char *ptr; + struct http_resp_header resp; + + if((ptr = strstr(uri, "://"))) { + uri = ptr + 3; + } + + /* skip the host part and the first slash if it exists */ + if((ptr = strchr(uri, '/'))) { + uri = ptr + 1; + } + + if(*uri) { + struct stat st; + char *path = 0; + char *rsphdr; + const char *type; + int fd, rspsize; + + if(stat(uri, &st) == -1) { + respond_error(c, 404); + return -1; + } + + if(S_ISDIR(st.st_mode)) { + int i; + path = alloca(strlen(uri) + 64); + + for(i=0; indexfiles[i]; i++) { + sprintf(path, "%s/%s", uri, indexfiles[i]); + if(stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) { + break; + } + } + + if(indexfiles[i] == 0) { + respond_error(c, 404); + return -1; + } + } else { + path = (char*)uri; + } + + if((fd = open(path, O_RDONLY)) == -1) { + respond_error(c, 403); + return -1; + } + + /* construct response header */ + http_init_resp(&resp); + http_add_resp_field(&resp, "Content-Length: %d", st.st_size); + if((type = mime_type(path))) { + http_add_resp_field(&resp, "Content-Type: %s", type); + } + + rspsize = http_serialize_resp(&resp, 0); + rsphdr = alloca(rspsize); + http_serialize_resp(&resp, rsphdr); + + if(with_body) { + int cont_left = st.st_size; + char *cont = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if(cont == (void*)-1) { + respond_error(c, 503); + close(fd); + return -1; + } + ptr = cont; + + send(c->s, rsphdr, rspsize, 0); + while(cont_left > 0) { + int sz = cont_left < 4096 ? cont_left : 4096; + send(c->s, ptr, sz, 0); + ptr += sz; + cont_left -= sz; + } + + munmap(cont, st.st_size); + } else { + send(c->s, rsphdr, rspsize, 0); + } + + close(fd); + } + return 0; +} + +static void respond_error(struct client *c, int errcode) +{ + char buf[512]; + + sprintf(buf, "HTTP/" HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode)); + + send(c->s, buf, strlen(buf), 0); + close_conn(c); +} + diff -r 0244b08cc9d3 -r 0dd50a23f3dd libtinyweb/src/tinyweb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libtinyweb/src/tinyweb.h Sat Apr 18 22:47:57 2015 +0300 @@ -0,0 +1,16 @@ +#ifndef TINYWEB_H_ +#define TINYWEB_H_ + +void tw_set_port(int port); +int tw_set_root(const char *path); +int tw_set_logfile(const char *fname); + +int tw_start(void); +int tw_stop(void); + +int tw_get_sockets(int *socks); +int tw_get_maxfd(void); +int tw_handle_socket(int s); + + +#endif /* TINYWEB_H_ */ diff -r 0244b08cc9d3 -r 0dd50a23f3dd src/http.c --- a/src/http.c Sat Apr 18 21:36:07 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,324 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "http.h" -#include "logger.h" - - -static const char *http_method_str[] = { - "", - "OPTIONS", - "GET", - "HEAD", - "POST", - "PUT", - "DELETE", - "TRACE", - "CONNECT", - 0 -}; - - -/* HTTP 1xx message strings */ -static const char *http_msg1xx[] = { - "Continue", /* 100 */ - "Switching Protocols" /* 101 */ -}; - -/* HTTP 2xx message strings */ -static const char *http_msg2xx[] = { - "OK", /* 200 */ - "Created", /* 201 */ - "Accepted", /* 202 */ - "Non-Authoritative Information", /* 203 */ - "No Content", /* 204 */ - "Reset Content", /* 205 */ - "Partial Content" /* 206 */ -}; - -/* HTTP 3xx message strings */ -static const char *http_msg3xx[] = { - "Multiple Choices", /* 300 */ - "Moved Permanently", /* 301 */ - "Found", /* 302 */ - "See Other", /* 303 */ - "Not Modified", /* 304 */ - "Use Proxy", /* 305 */ - "", /* 306 is undefined? */ - "Temporary Redirect" /* 307 */ -}; - -/* HTTP 4xx error strings */ -static const char *http_msg4xx[] = { - "Bad Request", /* 400 */ - "Unauthorized", /* 401 */ - "What the Fuck?", /* 402 */ - "Forbidden", /* 403 */ - "Not Found", /* 404 */ - "Method Not Allowed", /* 405 */ - "Not Acceptable", /* 406 */ - "Proxy Authentication Required", /* 407 */ - "Request Time-out", /* 408 */ - "Conflict", /* 409 */ - "Gone", /* 410 */ - "Length Required", /* 411 */ - "Precondition Failed", /* 412 */ - "Request Entity Too Large", /* 413 */ - "Request-URI Too Large", /* 414 */ - "Unsupported Media Type", /* 415 */ - "Request range not satisfiable", /* 416 */ - "Expectation Failed" /* 417 */ -}; - -/* HTTP 5xx error strings */ -static const char *http_msg5xx[] = { - "Internal Server Error", /* 500 */ - "Not Implemented", /* 501 */ - "Bad Gateway", /* 502 */ - "Service Unavailable", /* 503 */ - "Gateway Time-out", /* 504 */ - "HTTP Version not supported" /* 505 */ -}; - - -static enum http_method parse_method(const char *s); - - -int http_parse_request(struct http_req_header *hdr, const char *buf, int bufsz) -{ - int i, nlines = 0; - char *rqline = 0; - char *method, *uri, *version, *ptr; - const char *startln, *endln; - - memset(hdr, 0, sizeof *hdr); - - for(i=1; i 4 && buf[i - 2] == '\n' && buf[i - 3] == '\r') { - hdr->body_offset = i + 1; - break; - } - } - } - - if(!rqline) { - return HTTP_HDR_PARTIAL; - } - - ptr = rqline; - while(*ptr && isspace(*ptr)) ++ptr; - method = ptr; - - /* parse the request line */ - while(*ptr && !isspace(*ptr)) ++ptr; - while(*ptr && isspace(*ptr)) *ptr++ = 0; - - uri = ptr; - while(*ptr && !isspace(*ptr)) ++ptr; - while(*ptr && isspace(*ptr)) *ptr++ = 0; - - version = ptr; - while(*ptr && !isspace(*ptr)) ++ptr; - while(*ptr && isspace(*ptr)) *ptr++ = 0; - - hdr->method = parse_method(method); - hdr->uri = strdup(uri); - if(sscanf(version, "HTTP/%d.%d", &hdr->ver_major, &hdr->ver_minor) != 2) { - fprintf(stderr, "warning: failed to parse HTTP version \"%s\"\n", version); - hdr->ver_major = 1; - hdr->ver_minor = 1; - } - - if(!(hdr->hdrfields = malloc(nlines * sizeof *hdr->hdrfields))) { - perror("failed to allocate memory for the header fields"); - return HTTP_HDR_NOMEM; - } - hdr->num_hdrfields = 0; - - startln = buf; - endln = buf; - for(i=1; ibody_offset - 2; i++) { - if(buf[i] == '\n' && buf[i - 1] == '\r') { - int linesz; - - endln = buf + i - 1; - linesz = endln - startln; - - if(startln > buf) { /* skip first line */ - int idx = hdr->num_hdrfields++; - hdr->hdrfields[idx] = malloc(linesz + 1); - memcpy(hdr->hdrfields[idx], startln, linesz); - hdr->hdrfields[idx][linesz] = 0; - } - startln = endln = buf + i + 1; - } - } - - return HTTP_HDR_OK; -} - -void http_log_request(struct http_req_header *hdr) -{ - int i; - - logmsg("HTTP request header\n"); - logmsg(" method: %s\n", http_method_str[hdr->method]); - logmsg(" uri: %s\n", hdr->uri); - logmsg(" version: %d.%d\n", hdr->ver_major, hdr->ver_minor); - logmsg(" fields (%d):\n", hdr->num_hdrfields); - - for(i=0; inum_hdrfields; i++) { - logmsg(" %s\n", hdr->hdrfields[i]); - } - logmsg("\n"); -} - -void http_destroy_request(struct http_req_header *hdr) -{ - int i; - - if(hdr->hdrfields) { - for(i=0; inum_hdrfields; i++) { - free(hdr->hdrfields[i]); - } - free(hdr->hdrfields); - } - free(hdr->uri); -} - -int http_init_resp(struct http_resp_header *resp) -{ - memset(resp, 0, sizeof *resp); - resp->status = 200; - resp->ver_major = resp->ver_minor = 1; - resp->fields = 0; - resp->num_fields = 0; - - return 0; -} - -int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...) -{ - int sz; - va_list ap; - char *field, **newarr, tmp; - - va_start(ap, fmt); - sz = vsnprintf(&tmp, 0, fmt, ap); - va_end(ap); - - if(sz <= 0) sz = 1023; - if(!(field = malloc(sz + 1))) { - return -1; - } - va_start(ap, fmt); - vsnprintf(field, sz + 1, fmt, ap); - va_end(ap); - - if(!(newarr = realloc(resp->fields, (resp->num_fields + 1) * sizeof *resp->fields))) { - free(field); - return -1; - } - resp->fields = newarr; - - resp->fields[resp->num_fields++] = field; - return 0; -} - -void http_destroy_resp(struct http_resp_header *resp) -{ - int i; - if(resp->fields) { - for(i=0; inum_fields; i++) { - free(resp->fields[i]); - } - free(resp->fields); - resp->fields = 0; - } - resp->num_fields = 0; -} - -int http_serialize_resp(struct http_resp_header *resp, char *buf) -{ - int i, stsize, size, *fsize; - char *ptr, tmp; - - stsize = snprintf(&tmp, 0, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor, - resp->status, http_strmsg(resp->status)); - - fsize = alloca(resp->num_fields * sizeof *fsize); - - size = stsize; - for(i=0; inum_fields; i++) { - int len = strlen(resp->fields[i]) + 2; - fsize[i] = len; - size += len; - } - size += 2; /* CRLF and null */ - - if(buf) { - sprintf(buf, "HTTP/%d.%d %d %s\r\n", resp->ver_major, resp->ver_minor, - resp->status, http_strmsg(resp->status)); - - ptr = buf + stsize; - for(i=0; inum_fields; i++) { - sprintf(ptr, "%s\r\n", resp->fields[i]); - ptr += fsize[i]; - } - *ptr++ = '\r'; - *ptr++ = '\n'; - *ptr++ = 0; - } - - return size; -} - -const char *http_strmsg(int code) -{ - static const char **msgxxx[] = { - 0, http_msg1xx, http_msg2xx, http_msg3xx, http_msg4xx, http_msg5xx - }; - static int msgcount[] = { - 0, - sizeof http_msg1xx / sizeof *http_msg1xx, - sizeof http_msg2xx / sizeof *http_msg2xx, - sizeof http_msg3xx / sizeof *http_msg3xx, - sizeof http_msg4xx / sizeof *http_msg4xx, - sizeof http_msg5xx / sizeof *http_msg5xx - }; - - int type = code / 100; - int idx = code % 100; - - if(type < 1 || type >= sizeof msgxxx / sizeof *msgxxx) { - return "Invalid HTTP Status"; - } - - if(idx < 0 || idx >= msgcount[type]) { - return "Unknown HTTP Status"; - } - - return msgxxx[type][idx]; -} - -static enum http_method parse_method(const char *s) -{ - int i; - for(i=0; http_method_str[i]; i++) { - if(strcmp(s, http_method_str[i]) == 0) { - return (enum http_method)i; - } - } - return HTTP_UNKNOWN; -} diff -r 0244b08cc9d3 -r 0dd50a23f3dd src/http.h --- a/src/http.h Sat Apr 18 21:36:07 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -#ifndef HTTP_H_ -#define HTTP_H_ - -enum http_method { - HTTP_UNKNOWN, - HTTP_OPTIONS, - HTTP_GET, - HTTP_HEAD, - HTTP_POST, - HTTP_PUT, - HTTP_DELETE, - HTTP_TRACE, - HTTP_CONNECT, - - NUM_HTTP_METHODS -}; - -struct http_req_header { - enum http_method method; - char *uri; - int ver_major, ver_minor; /* http version */ - char **hdrfields; - int num_hdrfields; - int body_offset; -}; - -struct http_resp_header { - int status; - int ver_major, ver_minor; - char **fields; - int num_fields; -}; - -#define HTTP_HDR_OK 0 -#define HTTP_HDR_INVALID -1 -#define HTTP_HDR_NOMEM -2 -#define HTTP_HDR_PARTIAL -3 - -int http_parse_request(struct http_req_header *hdr, const char *buf, int bufsz); -void http_log_request(struct http_req_header *hdr); -void http_destroy_request(struct http_req_header *hdr); - -int http_init_resp(struct http_resp_header *resp); -int http_add_resp_field(struct http_resp_header *resp, const char *fmt, ...); -void http_destroy_resp(struct http_resp_header *resp); -int http_serialize_resp(struct http_resp_header *resp, char *buf); - -const char *http_strmsg(int code); - -#endif /* HTTP_H_ */ diff -r 0244b08cc9d3 -r 0dd50a23f3dd src/logger.c --- a/src/logger.c Sat Apr 18 21:36:07 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -#include -#include -#include -#include -#include -#include "logger.h" - -static FILE *logfile; - -int set_log_file(const char *fname) -{ - FILE *fp; - - if(!(fp = fopen(fname, "w"))) { - fprintf(stderr, "failed to open logfile: %s: %s\n", fname, strerror(errno)); - return -1; - } - setvbuf(fp, 0, _IONBF, 0); - logfile = fp; - return 0; -} - -void logmsg(const char *fmt, ...) -{ - va_list ap; - - if(!logfile) { - logfile = stderr; - } - - va_start(ap, fmt); - vfprintf(logfile, fmt, ap); - va_end(ap); -} diff -r 0244b08cc9d3 -r 0dd50a23f3dd src/logger.h --- a/src/logger.h Sat Apr 18 21:36:07 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -#ifndef LOGGER_H_ -#define LOGGER_H_ - -int set_log_file(const char *fname); - -void logmsg(const char *fname, ...); - -#endif /* LOGGER_H_ */ diff -r 0244b08cc9d3 -r 0dd50a23f3dd src/mime.c --- a/src/mime.c Sat Apr 18 21:36:07 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,72 +0,0 @@ -#include -#include -#include "mime.h" -#include "rbtree.h" - -/* TODO: do proper content detection */ -struct mime_type { - const char *suffix, *type; -}; - -static struct mime_type def_types[] = { - {"txt", "text/plain"}, - {"htm", "text/html"}, - {"html", "text/html"}, - {"png", "image/png"}, - {"jpg", "image/jpeg"}, - {"jpeg", "image/jpeg"}, - {"gif", "image/gif"}, - {"bmp", "image/bmp"}, - {"cgi", 0}, - {0, 0} -}; - -static int init_types(void); -static void del_func(struct rbnode *node, void *cls); - -static struct rbtree *types; - -static int init_types(void) -{ - int i; - - if(types) return 0; - - if(!(types = rb_create(RB_KEY_STRING))) { - return -1; - } - rb_set_delete_func(types, del_func, 0); - - for(i=0; def_types[i].suffix; i++) { - add_mime_type(def_types[i].suffix, def_types[i].type); - } - return 0; -} - -static void del_func(struct rbnode *node, void *cls) -{ - free(node->key); - free(node->data); -} - -int add_mime_type(const char *suffix, const char *type) -{ - init_types(); - - return rb_insert(types, strdup(suffix), type ? strdup(type) : 0); -} - -const char *mime_type(const char *path) -{ - const char *suffix; - - init_types(); - - if((suffix = strrchr(path, '.'))) { - struct rbnode *node = rb_find(types, (void*)(suffix + 1)); - if(node) { - return node->data; - } - } - return "text/plain"; -} diff -r 0244b08cc9d3 -r 0dd50a23f3dd src/mime.h --- a/src/mime.h Sat Apr 18 21:36:07 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,7 +0,0 @@ -#ifndef MIME_H_ -#define MIME_H_ - -int add_mime_type(const char *suffix, const char *type); -const char *mime_type(const char *path); - -#endif /* MIME_H_ */ diff -r 0244b08cc9d3 -r 0dd50a23f3dd src/rbtree.c --- a/src/rbtree.c Sat Apr 18 21:36:07 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,501 +0,0 @@ -/* -rbtree - simple balanced binary search tree (red-black tree) library. -Copyright (C) 2011-2014 John Tsiombikas - -rbtree is free software, feel free to use, modify, and redistribute it, under -the terms of the 3-clause BSD license. See COPYING for details. - */ -#include -#include -#include -#include -#include "rbtree.h" - -#define INT2PTR(x) ((void*)(intptr_t)(x)) -#define PTR2INT(x) ((int)(intptr_t)(x)) - -struct rbtree { - struct rbnode *root; - - rb_alloc_func_t alloc; - rb_free_func_t free; - - rb_cmp_func_t cmp; - rb_del_func_t del; - void *del_cls; - - struct rbnode *rstack, *iter; -}; - -static int cmpaddr(const void *ap, const void *bp); -static int cmpint(const void *ap, const void *bp); - -static int count_nodes(struct rbnode *node); -static void del_tree(struct rbnode *node, void (*delfunc)(struct rbnode*, void*), void *cls); -static struct rbnode *insert(struct rbtree *rb, struct rbnode *tree, void *key, void *data); -static struct rbnode *delete(struct rbtree *rb, struct rbnode *tree, void *key); -/*static struct rbnode *find(struct rbtree *rb, struct rbnode *node, void *key);*/ -static void traverse(struct rbnode *node, void (*func)(struct rbnode*, void*), void *cls); - -struct rbtree *rb_create(rb_cmp_func_t cmp_func) -{ - struct rbtree *rb; - - if(!(rb = malloc(sizeof *rb))) { - return 0; - } - if(rb_init(rb, cmp_func) == -1) { - free(rb); - return 0; - } - return rb; -} - -void rb_free(struct rbtree *rb) -{ - rb_destroy(rb); - free(rb); -} - - -int rb_init(struct rbtree *rb, rb_cmp_func_t cmp_func) -{ - memset(rb, 0, sizeof *rb); - - if(!cmp_func) { - rb->cmp = cmpaddr; - } else if(cmp_func == RB_KEY_INT) { - rb->cmp = cmpint; - } else if(cmp_func == RB_KEY_STRING) { - rb->cmp = (rb_cmp_func_t)strcmp; - } else { - rb->cmp = cmp_func; - } - - rb->alloc = malloc; - rb->free = free; - return 0; -} - -void rb_destroy(struct rbtree *rb) -{ - del_tree(rb->root, rb->del, rb->del_cls); -} - -void rb_set_allocator(struct rbtree *rb, rb_alloc_func_t alloc, rb_free_func_t free) -{ - rb->alloc = alloc; - rb->free = free; -} - - -void rb_set_compare_func(struct rbtree *rb, rb_cmp_func_t func) -{ - rb->cmp = func; -} - -void rb_set_delete_func(struct rbtree *rb, rb_del_func_t func, void *cls) -{ - rb->del = func; - rb->del_cls = cls; -} - - -void rb_clear(struct rbtree *rb) -{ - del_tree(rb->root, rb->del, rb->del_cls); - rb->root = 0; -} - -int rb_copy(struct rbtree *dest, struct rbtree *src) -{ - struct rbnode *node; - - rb_clear(dest); - rb_begin(src); - while((node = rb_next(src))) { - if(rb_insert(dest, node->key, node->data) == -1) { - return -1; - } - } - return 0; -} - -int rb_size(struct rbtree *rb) -{ - return count_nodes(rb->root); -} - -int rb_insert(struct rbtree *rb, void *key, void *data) -{ - rb->root = insert(rb, rb->root, key, data); - rb->root->red = 0; - return 0; -} - -int rb_inserti(struct rbtree *rb, int key, void *data) -{ - rb->root = insert(rb, rb->root, INT2PTR(key), data); - rb->root->red = 0; - return 0; -} - - -int rb_delete(struct rbtree *rb, void *key) -{ - rb->root = delete(rb, rb->root, key); - rb->root->red = 0; - return 0; -} - -int rb_deletei(struct rbtree *rb, int key) -{ - rb->root = delete(rb, rb->root, INT2PTR(key)); - rb->root->red = 0; - return 0; -} - - -struct rbnode *rb_find(struct rbtree *rb, void *key) -{ - struct rbnode *node = rb->root; - - while(node) { - int cmp = rb->cmp(key, node->key); - if(cmp == 0) { - return node; - } - node = cmp < 0 ? node->left : node->right; - } - return 0; -} - -struct rbnode *rb_findi(struct rbtree *rb, int key) -{ - return rb_find(rb, INT2PTR(key)); -} - - -void rb_foreach(struct rbtree *rb, void (*func)(struct rbnode*, void*), void *cls) -{ - traverse(rb->root, func, cls); -} - - -struct rbnode *rb_root(struct rbtree *rb) -{ - return rb->root; -} - -void rb_begin(struct rbtree *rb) -{ - rb->rstack = 0; - rb->iter = rb->root; -} - -#define push(sp, x) ((x)->next = (sp), (sp) = (x)) -#define pop(sp) ((sp) = (sp)->next) -#define top(sp) (sp) - -struct rbnode *rb_next(struct rbtree *rb) -{ - struct rbnode *res = 0; - - while(rb->rstack || rb->iter) { - if(rb->iter) { - push(rb->rstack, rb->iter); - rb->iter = rb->iter->left; - } else { - rb->iter = top(rb->rstack); - pop(rb->rstack); - res = rb->iter; - rb->iter = rb->iter->right; - break; - } - } - return res; -} - -void *rb_node_key(struct rbnode *node) -{ - return node ? node->key : 0; -} - -int rb_node_keyi(struct rbnode *node) -{ - return node ? PTR2INT(node->key) : 0; -} - -void *rb_node_data(struct rbnode *node) -{ - return node ? node->data : 0; -} - -static int cmpaddr(const void *ap, const void *bp) -{ - return ap < bp ? -1 : (ap > bp ? 1 : 0); -} - -static int cmpint(const void *ap, const void *bp) -{ - return PTR2INT(ap) - PTR2INT(bp); -} - - -/* ---- left-leaning 2-3 red-black implementation ---- */ - -/* helper prototypes */ -static int is_red(struct rbnode *tree); -static void color_flip(struct rbnode *tree); -static struct rbnode *rot_left(struct rbnode *a); -static struct rbnode *rot_right(struct rbnode *a); -static struct rbnode *find_min(struct rbnode *tree); -static struct rbnode *del_min(struct rbtree *rb, struct rbnode *tree); -/*static struct rbnode *move_red_right(struct rbnode *tree);*/ -static struct rbnode *move_red_left(struct rbnode *tree); -static struct rbnode *fix_up(struct rbnode *tree); - -static int count_nodes(struct rbnode *node) -{ - if(!node) - return 0; - - return 1 + count_nodes(node->left) + count_nodes(node->right); -} - -static void del_tree(struct rbnode *node, rb_del_func_t delfunc, void *cls) -{ - if(!node) - return; - - del_tree(node->left, delfunc, cls); - del_tree(node->right, delfunc, cls); - - if(delfunc) { - delfunc(node, cls); - } - free(node); -} - -static struct rbnode *insert(struct rbtree *rb, struct rbnode *tree, void *key, void *data) -{ - int cmp; - - if(!tree) { - struct rbnode *node = rb->alloc(sizeof *node); - node->red = 1; - node->key = key; - node->data = data; - node->left = node->right = 0; - return node; - } - - cmp = rb->cmp(key, tree->key); - - if(cmp < 0) { - tree->left = insert(rb, tree->left, key, data); - } else if(cmp > 0) { - tree->right = insert(rb, tree->right, key, data); - } else { - tree->data = data; - } - - /* fix right-leaning reds */ - if(is_red(tree->right)) { - tree = rot_left(tree); - } - /* fix two reds in a row */ - if(is_red(tree->left) && is_red(tree->left->left)) { - tree = rot_right(tree); - } - - /* if 4-node, split it by color inversion */ - if(is_red(tree->left) && is_red(tree->right)) { - color_flip(tree); - } - - return tree; -} - -static struct rbnode *delete(struct rbtree *rb, struct rbnode *tree, void *key) -{ - int cmp; - - if(!tree) { - return 0; - } - - cmp = rb->cmp(key, tree->key); - - if(cmp < 0) { - if(!is_red(tree->left) && !is_red(tree->left->left)) { - tree = move_red_left(tree); - } - tree->left = delete(rb, tree->left, key); - } else { - /* need reds on the right */ - if(is_red(tree->left)) { - tree = rot_right(tree); - } - - /* found it at the bottom (XXX what certifies left is null?) */ - if(cmp == 0 && !tree->right) { - if(rb->del) { - rb->del(tree, rb->del_cls); - } - rb->free(tree); - return 0; - } - - if(!is_red(tree->right) && !is_red(tree->right->left)) { - tree = move_red_left(tree); - } - - if(key == tree->key) { - struct rbnode *rmin = find_min(tree->right); - tree->key = rmin->key; - tree->data = rmin->data; - tree->right = del_min(rb, tree->right); - } else { - tree->right = delete(rb, tree->right, key); - } - } - - return fix_up(tree); -} - -/*static struct rbnode *find(struct rbtree *rb, struct rbnode *node, void *key) -{ - int cmp; - - if(!node) - return 0; - - if((cmp = rb->cmp(key, node->key)) == 0) { - return node; - } - return find(rb, cmp < 0 ? node->left : node->right, key); -}*/ - -static void traverse(struct rbnode *node, void (*func)(struct rbnode*, void*), void *cls) -{ - if(!node) - return; - - traverse(node->left, func, cls); - func(node, cls); - traverse(node->right, func, cls); -} - -/* helpers */ - -static int is_red(struct rbnode *tree) -{ - return tree && tree->red; -} - -static void color_flip(struct rbnode *tree) -{ - tree->red = !tree->red; - tree->left->red = !tree->left->red; - tree->right->red = !tree->right->red; -} - -static struct rbnode *rot_left(struct rbnode *a) -{ - struct rbnode *b = a->right; - a->right = b->left; - b->left = a; - b->red = a->red; - a->red = 1; - return b; -} - -static struct rbnode *rot_right(struct rbnode *a) -{ - struct rbnode *b = a->left; - a->left = b->right; - b->right = a; - b->red = a->red; - a->red = 1; - return b; -} - -static struct rbnode *find_min(struct rbnode *tree) -{ - if(!tree) - return 0; - - while(tree->left) { - tree = tree->left; - } - return tree; -} - -static struct rbnode *del_min(struct rbtree *rb, struct rbnode *tree) -{ - if(!tree->left) { - if(rb->del) { - rb->del(tree->left, rb->del_cls); - } - rb->free(tree->left); - return 0; - } - - /* make sure we've got red (3/4-nodes) at the left side so we can delete at the bottom */ - if(!is_red(tree->left) && !is_red(tree->left->left)) { - tree = move_red_left(tree); - } - tree->left = del_min(rb, tree->left); - - /* fix right-reds, red-reds, and split 4-nodes on the way up */ - return fix_up(tree); -} - -#if 0 -/* push a red link on this node to the right */ -static struct rbnode *move_red_right(struct rbnode *tree) -{ - /* flipping it makes both children go red, so we have a red to the right */ - color_flip(tree); - - /* if after the flip we've got a red-red situation to the left, fix it */ - if(is_red(tree->left->left)) { - tree = rot_right(tree); - color_flip(tree); - } - return tree; -} -#endif - -/* push a red link on this node to the left */ -static struct rbnode *move_red_left(struct rbnode *tree) -{ - /* flipping it makes both children go red, so we have a red to the left */ - color_flip(tree); - - /* if after the flip we've got a red-red on the right-left, fix it */ - if(is_red(tree->right->left)) { - tree->right = rot_right(tree->right); - tree = rot_left(tree); - color_flip(tree); - } - return tree; -} - -static struct rbnode *fix_up(struct rbnode *tree) -{ - /* fix right-leaning */ - if(is_red(tree->right)) { - tree = rot_left(tree); - } - /* change invalid red-red pairs into a proper 4-node */ - if(is_red(tree->left) && is_red(tree->left->left)) { - tree = rot_right(tree); - } - /* split 4-nodes */ - if(is_red(tree->left) && is_red(tree->right)) { - color_flip(tree); - } - return tree; -} diff -r 0244b08cc9d3 -r 0dd50a23f3dd src/rbtree.h --- a/src/rbtree.h Sat Apr 18 21:36:07 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/* -rbtree - simple balanced binary search tree (red-black tree) library. -Copyright (C) 2011-2014 John Tsiombikas - -rbtree is free software, feel free to use, modify, and redistribute it, under -the terms of the 3-clause BSD license. See COPYING for details. - */ -#ifndef RBTREE_H_ -#define RBTREE_H_ - -struct rbtree; - - -struct rbnode { - void *key, *data; - int red; - struct rbnode *left, *right; - struct rbnode *next; /* for iterator stack */ -}; - - -typedef void *(*rb_alloc_func_t)(size_t); -typedef void (*rb_free_func_t)(void*); - -typedef int (*rb_cmp_func_t)(const void*, const void*); -typedef void (*rb_del_func_t)(struct rbnode*, void*); - -#define RB_KEY_ADDR (rb_cmp_func_t)(0) -#define RB_KEY_INT (rb_cmp_func_t)(1) -#define RB_KEY_STRING (rb_cmp_func_t)(3) - - -#ifdef __cplusplus -extern "C" { -#endif - -struct rbtree *rb_create(rb_cmp_func_t cmp_func); -void rb_free(struct rbtree *rb); - -int rb_init(struct rbtree *rb, rb_cmp_func_t cmp_func); -void rb_destroy(struct rbtree *rb); - -void rb_set_allocator(struct rbtree *rb, rb_alloc_func_t alloc, rb_free_func_t free); -void rb_set_compare_func(struct rbtree *rb, rb_cmp_func_t func); -void rb_set_delete_func(struct rbtree *rb, rb_del_func_t func, void *cls); -/* TODO add user deep copy function */ - -void rb_clear(struct rbtree *rb); -int rb_copy(struct rbtree *dest, struct rbtree *src); - -int rb_size(struct rbtree *rb); - -int rb_insert(struct rbtree *rb, void *key, void *data); -int rb_inserti(struct rbtree *rb, int key, void *data); - -int rb_delete(struct rbtree *rb, void *key); -int rb_deletei(struct rbtree *rb, int key); - -struct rbnode *rb_find(struct rbtree *rb, void *key); -struct rbnode *rb_findi(struct rbtree *rb, int key); - -void rb_foreach(struct rbtree *rb, void (*func)(struct rbnode*, void*), void *cls); - -struct rbnode *rb_root(struct rbtree *rb); - -void rb_begin(struct rbtree *rb); -struct rbnode *rb_next(struct rbtree *rb); - -void *rb_node_key(struct rbnode *node); -int rb_node_keyi(struct rbnode *node); -void *rb_node_data(struct rbnode *node); - -#ifdef __cplusplus -} -#endif - - -#endif /* RBTREE_H_ */ diff -r 0244b08cc9d3 -r 0dd50a23f3dd src/tinyweb.c --- a/src/tinyweb.c Sat Apr 18 21:36:07 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,391 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "tinyweb.h" -#include "http.h" -#include "mime.h" -#include "logger.h" - -/* HTTP version */ -#define HTTP_VER_MAJOR 1 -#define HTTP_VER_MINOR 1 -#define HTTP_VER_STR "1.1" - -/* maximum request length: 64mb */ -#define MAX_REQ_LENGTH (65536 * 1024) - -struct client { - int s; - char *rcvbuf; - int bufsz; - struct client *next; -}; - -static int accept_conn(int lis); -static void close_conn(struct client *c); -static int handle_client(struct client *c); -static int do_get(struct client *c, const char *uri, int with_body); -static void respond_error(struct client *c, int errcode); - -static int lis = -1; -static int maxfd; -static int port = 8080; -static struct client *clist; -static int num_clients; - -static const char *indexfiles[] = { - "index.cgi", - "index.html", - "index.htm", - 0 -}; - -void tw_set_port(int p) -{ - port = p; -} - -int tw_set_root(const char *path) -{ - return chdir(path); -} - -int tw_set_logfile(const char *fname) -{ - return set_log_file(fname); -} - -int tw_start(void) -{ - int s; - struct sockaddr_in sa; - - logmsg("starting server ...\n"); - - if(lis != -1) { - logmsg("can't start tinyweb server: already running!\n"); - return -1; - } - - if((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) { - logmsg("failed to create listening socket: %s\n", strerror(errno)); - return -1; - } - fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK); - - memset(&sa, 0, sizeof sa); - sa.sin_family = AF_INET; - sa.sin_addr.s_addr = INADDR_ANY; - sa.sin_port = htons(port); - - if(bind(s, (struct sockaddr*)&sa, sizeof sa) == -1) { - logmsg("failed to bind socket to port %d: %s\n", port, strerror(errno)); - return -1; - } - listen(s, 16); - - lis = s; - return s; -} - -int tw_stop(void) -{ - if(lis == -1) { - return -1; - } - - logmsg("stopping server...\n"); - - close(lis); - lis = -1; - - while(clist) { - struct client *c = clist; - clist = clist->next; - close_conn(c); - free(c); - } - clist = 0; - - return 0; -} - -int tw_get_sockets(int *socks) -{ - struct client *c, dummy; - - /* first cleanup the clients marked for removal */ - dummy.next = clist; - c = &dummy; - - while(c->next) { - struct client *n = c->next; - - if(n->s == -1) { - /* marked for removal */ - c->next = n->next; - free(n); - --num_clients; - } else { - c = c->next; - } - } - clist = dummy.next; - - - if(!socks) { - /* just return the count */ - return num_clients + 1; /* +1 for the listening socket */ - } - - /* go through the client list and populate the array */ - maxfd = lis; - *socks++ = lis; - - c = clist; - while(c) { - *socks++ = c->s; - if(c->s > maxfd) { - maxfd = c->s; - } - c = c->next; - } - return num_clients + 1; /* +1 for the listening socket */ -} - -int tw_get_maxfd(void) -{ - return maxfd; -} - -int tw_handle_socket(int s) -{ - struct client *c; - - if(s == lis) { - return accept_conn(s); - } - - /* find which client corresponds to this socket */ - c = clist; - while(c) { - if(c->s == s) { - return handle_client(c); - } - c = c->next; - } - - logmsg("socket %d doesn't correspond to any client\n"); - return -1; -} - -static int accept_conn(int lis) -{ - int s; - struct client *c; - struct sockaddr_in addr; - socklen_t addr_sz = sizeof addr; - - if((s = accept(lis, (struct sockaddr*)&addr, &addr_sz)) == -1) { - logmsg("failed to accept incoming connection: %s\n", strerror(errno)); - return -1; - } - fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK); - - if(!(c = malloc(sizeof *c))) { - logmsg("failed to allocate memory while accepting connection: %s\n", strerror(errno)); - return -1; - } - c->s = s; - c->rcvbuf = 0; - c->bufsz = 0; - c->next = clist; - clist = c; - ++num_clients; - return 0; -} - -static void close_conn(struct client *c) -{ - close(c->s); - c->s = -1; /* mark it for removal */ - free(c->rcvbuf); - c->rcvbuf = 0; -} - -static int handle_client(struct client *c) -{ - struct http_req_header hdr; - static char buf[2048]; - int rdsz, status; - - while((rdsz = recv(c->s, buf, sizeof buf, 0)) > 0) { - char *newbuf; - int newsz = c->bufsz + rdsz; - if(newsz > MAX_REQ_LENGTH) { - respond_error(c, 413); - return -1; - } - - if(!(newbuf = realloc(c->rcvbuf, newsz + 1))) { - logmsg("failed to allocate %d byte buffer\n", newsz); - respond_error(c, 503); - return -1; - } - - memcpy(newbuf + c->bufsz, buf, rdsz); - newbuf[newsz] = 0; - - c->rcvbuf = newbuf; - c->bufsz = newsz; - } - - if((status = http_parse_request(&hdr, c->rcvbuf, c->bufsz)) != HTTP_HDR_OK) { - http_log_request(&hdr); - switch(status) { - case HTTP_HDR_INVALID: - respond_error(c, 400); - return -1; - - case HTTP_HDR_NOMEM: - respond_error(c, 503); - return -1; - - case HTTP_HDR_PARTIAL: - return 0; /* partial header, continue reading */ - } - } - http_log_request(&hdr); - - /* we only support GET and HEAD at this point, so freak out on anything else */ - switch(hdr.method) { - case HTTP_GET: - if(do_get(c, hdr.uri, 1) == -1) { - return -1; - } - break; - - case HTTP_HEAD: - if(do_get(c, hdr.uri, 0) == -1) { - return -1; - } - break; - - default: - respond_error(c, 501); - return -1; - } - - close_conn(c); - return 0; -} - -static int do_get(struct client *c, const char *uri, int with_body) -{ - const char *ptr; - struct http_resp_header resp; - - if((ptr = strstr(uri, "://"))) { - uri = ptr + 3; - } - - /* skip the host part and the first slash if it exists */ - if((ptr = strchr(uri, '/'))) { - uri = ptr + 1; - } - - if(*uri) { - struct stat st; - char *path = 0; - char *rsphdr; - const char *type; - int fd, rspsize; - - if(stat(uri, &st) == -1) { - respond_error(c, 404); - return -1; - } - - if(S_ISDIR(st.st_mode)) { - int i; - path = alloca(strlen(uri) + 64); - - for(i=0; indexfiles[i]; i++) { - sprintf(path, "%s/%s", uri, indexfiles[i]); - if(stat(path, &st) == 0 && !S_ISDIR(st.st_mode)) { - break; - } - } - - if(indexfiles[i] == 0) { - respond_error(c, 404); - return -1; - } - } else { - path = (char*)uri; - } - - if((fd = open(path, O_RDONLY)) == -1) { - respond_error(c, 403); - return -1; - } - - /* construct response header */ - http_init_resp(&resp); - http_add_resp_field(&resp, "Content-Length: %d", st.st_size); - if((type = mime_type(path))) { - http_add_resp_field(&resp, "Content-Type: %s", type); - } - - rspsize = http_serialize_resp(&resp, 0); - rsphdr = alloca(rspsize); - http_serialize_resp(&resp, rsphdr); - - if(with_body) { - int cont_left = st.st_size; - char *cont = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if(cont == (void*)-1) { - respond_error(c, 503); - close(fd); - return -1; - } - ptr = cont; - - send(c->s, rsphdr, rspsize, 0); - while(cont_left > 0) { - int sz = cont_left < 4096 ? cont_left : 4096; - send(c->s, ptr, sz, 0); - ptr += sz; - cont_left -= sz; - } - - munmap(cont, st.st_size); - } else { - send(c->s, rsphdr, rspsize, 0); - } - - close(fd); - } - return 0; -} - -static void respond_error(struct client *c, int errcode) -{ - char buf[512]; - - sprintf(buf, "HTTP/" HTTP_VER_STR " %d %s\r\n\r\n", errcode, http_strmsg(errcode)); - - send(c->s, buf, strlen(buf), 0); - close_conn(c); -} - diff -r 0244b08cc9d3 -r 0dd50a23f3dd src/tinyweb.h --- a/src/tinyweb.h Sat Apr 18 21:36:07 2015 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -#ifndef TINYWEB_H_ -#define TINYWEB_H_ - -void tw_set_port(int port); -int tw_set_root(const char *path); -int tw_set_logfile(const char *fname); - -int tw_start(void); -int tw_stop(void); - -int tw_get_sockets(int *socks); -int tw_get_maxfd(void); -int tw_handle_socket(int s); - - -#endif /* TINYWEB_H_ */