Changeset 5881

Show
Ignore:
Timestamp:
03/20/10 00:10:03 (3 years ago)
Author:
Cyrus
Message:

zhttpd: Complete static file & CGI serving, TODO: UCI configuration

Location:
luci2/zhttpd
Files:
6 added
10 modified

Legend:

Unmodified
Added
Removed
  • luci2/zhttpd/env.c

    r5858 r5881  
    1818} 
    1919 
    20 const char* zhttpd_env_get(uhtbl_t *tbl, const char *key) { 
     20unsigned int zhttpd_env_count(uhtbl_t *tbl) { 
     21    return tbl->used; 
     22} 
     23 
     24char* zhttpd_env_next(uhtbl_t *tbl, uint32_t *iter, const char **key) { 
     25    zhttpd_env_bucket_t *bucket = (zhttpd_env_bucket_t*)uhtbl_next(tbl, iter); 
     26    if (bucket) { 
     27        uhtbl_key((uhtbl_bucket_t*)bucket, (void**)key, NULL); 
     28        return bucket->value; 
     29    } else { 
     30        return NULL; 
     31    } 
     32} 
     33 
     34char* zhttpd_env_get(uhtbl_t *tbl, const char *key) { 
    2135    zhttpd_env_bucket_t *bucket = 
    22             (zhttpd_env_bucket_t*)uhtbl_get(tbl, (const void*)key, strlen(key)); 
     36            (zhttpd_env_bucket_t*)uhtbl_get(tbl, key, strlen(key)); 
    2337    return (bucket) ? bucket->value : NULL; 
    2438} 
     
    3145 
    3246    if (!(ekey = strdup(key)) || !(evalue = malloc(len + 1)) 
    33     || !(bucket = (zhttpd_env_bucket_t*)uhtbl_set(tbl, ekey, strlen(key)))) { 
     47    || !(bucket = (zhttpd_env_bucket_t*)uhtbl_set(tbl, ekey, strlen(ekey)))) { 
    3448        free(ekey); 
    3549        free(evalue); 
     
    4256} 
    4357 
     58void zhttpd_env_clear_custom(uhtbl_t *tbl) { 
     59    uint32_t iter = 0; 
     60    uhtbl_bucket_t *bucket; 
     61    while ((bucket = uhtbl_next(tbl, &iter))) { 
     62        void *key; 
     63        long len; 
     64        uhtbl_key(bucket, &key, &len); 
     65        if (!strncmp((char*)key, "HTTP_", 5)) { 
     66            uhtbl_unset(tbl, key, len); 
     67        } 
     68    } 
     69} 
     70 
    4471int zhttpd_env_set_int(uhtbl_t *tbl, const char *key, long long value) { 
    4572    char buf[32]; 
  • luci2/zhttpd/env.h

    r5830 r5881  
    1111 
    1212int zhttpd_env_init(uhtbl_t *tbl, uint32_t sizehint); 
    13 const char* zhttpd_env_get(uhtbl_t *tbl, const char *key); 
     13char* zhttpd_env_get(uhtbl_t *tbl, const char *key); 
    1414int zhttpd_env_set 
    1515(uhtbl_t *tbl, const char *name, const char *value, size_t len); 
    1616int zhttpd_env_set_int(uhtbl_t *tbl, const char *key, long long value); 
    1717int zhttpd_env_unset(uhtbl_t *tbl, const char *key); 
     18char* zhttpd_env_next(uhtbl_t *tbl, uint32_t *iter, const char **key); 
     19unsigned int zhttpd_env_count(uhtbl_t *tbl); 
     20void zhttpd_env_clear_custom(uhtbl_t *tbl); 
    1821 
    1922 
  • luci2/zhttpd/http.c

    r5858 r5881  
     1#define _XOPEN_SOURCE 
     2#define _SVID_SOURCE 
    13#include <time.h> 
     4#include <stdlib.h> 
     5#include <string.h> 
     6#include <ctype.h> 
    27#include "http.h" 
    38 
    49static zhttpd_error_t errors[] = { 
     10        {ZHTTPD_STATUS_OK, "OK"}, 
     11        {ZHTTPD_STATUS_NOT_MODIFIED, "Not Modified"}, 
     12        {ZHTTPD_STATUS_TEMPORARY_REDIRECT, "Temporary Redirect"}, 
    513        {ZHTTPD_STATUS_BAD_REQUEST, "Bad Request"}, 
    614        {ZHTTPD_STATUS_FORBIDDEN, "Forbidden"}, 
    715        {ZHTTPD_STATUS_NOT_FOUND, "Not Found"}, 
    816        {ZHTTPD_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed"}, 
     17        {ZHTTPD_STATUS_LENGTH_REQUIRED, "Length Required"}, 
     18        {ZHTTPD_STATUS_REQUEST_ENTITY_TOO_LARGE, "Request Entity Too Large"}, 
     19        {ZHTTPD_STATUS_REQUEST_URI_TOO_LONG, "Request-URI Too Long"}, 
     20        {ZHTTPD_STATUS_INTERNAL_SERVER_ERROR, "Internal Server Error"}, 
    921        {0, NULL} 
    1022}; 
     
    1931} 
    2032 
     33int zhttpd_http_urldecode(char *url) { 
     34    char *c = url; 
     35    unsigned char *d; 
     36    char dec[3] = "00"; 
     37    while ((d = (unsigned char*)strchr(c, '%'))) { 
     38        if (!isxdigit(d[1]) || !isxdigit(d[2])) { 
     39            return -1; 
     40        } 
     41        dec[0] = d[1]; 
     42        dec[1] = d[2]; 
     43        *d = (unsigned char)strtol(dec, NULL, 16); 
     44        memmove(d + 1, &d[3], strlen((char*)&d[3]) + 1); 
     45        c = (char*)d + 1; 
     46    } 
     47    return 0; 
     48} 
     49 
    2150int zhttpd_http_date(time_t timestamp, char *buffer, size_t size) { 
    2251    if (!timestamp) { 
     
    2756    return strftime(buffer, size, "%a, %d %h %Y %T GMT", &t); 
    2857} 
     58 
     59time_t zhttpd_http_timestamp(char *buffer) { 
     60    struct tm t; 
     61    return (strptime(buffer, "%a, %d %h %Y %T GMT", &t)) ? timegm(&t) : 0; 
     62} 
  • luci2/zhttpd/http.h

    r5858 r5881  
    22#define ERROR_H_ 
    33 
    4  
     4#define ZHTTPD_STATUS_OK 200 
     5#define ZHTTPD_STATUS_NOT_MODIFIED 304 
     6#define ZHTTPD_STATUS_TEMPORARY_REDIRECT 307 
    57#define ZHTTPD_STATUS_BAD_REQUEST 400 
    68#define ZHTTPD_STATUS_FORBIDDEN 403 
    79#define ZHTTPD_STATUS_NOT_FOUND 404 
    810#define ZHTTPD_STATUS_METHOD_NOT_ALLOWED 405 
    9  
     11#define ZHTTPD_STATUS_LENGTH_REQUIRED 411 
     12#define ZHTTPD_STATUS_REQUEST_ENTITY_TOO_LARGE 413 
     13#define ZHTTPD_STATUS_REQUEST_URI_TOO_LONG 414 
     14#define ZHTTPD_STATUS_INTERNAL_SERVER_ERROR 500 
    1015 
    1116typedef struct zhttpd_error { 
     
    1621const char* zhttpd_http_status(int code); 
    1722int zhttpd_http_date(const time_t timestamp, char *buffer, size_t size); 
     23time_t zhttpd_http_timestamp(char *buffer); 
     24int zhttpd_http_urldecode(char *url); 
    1825 
    1926#endif /* ERROR_H_ */ 
  • luci2/zhttpd/Makefile

    r5858 r5881  
    1919all: $(BINARY) 
    2020 
    21 $(BINARY): *.c 
     21$(BINARY): *.c handler/*.c 
    2222    $(CC) $(CFLAGS) $(TLSCFLAGS) $(SFLAGS) $(WFLAGS) $(LDFLAGS) -I.. -o $@ $+ 
    2323 
  • luci2/zhttpd/request.c

    r5858 r5881  
    1010#include "env.h" 
    1111 
     12static int zhttpd_request_headers(zthread_t *thread); 
     13static int zhttpd_request_dispatch(zthread_t *thread); 
     14 
    1215int zhttpd_request_magic(zthread_t *thread) { 
    1316    char buffer[4096]; 
     
    5255        goto error; 
    5356    } 
     57    req->size += c - buffer; 
    5458 
    5559    zhttpd_env_set(&req->env, "REQUEST_METHOD", method, strlen(method)); 
     
    6266    } 
    6367 
     68    char *query = strchr(url, '?'); 
     69    if (query) { 
     70        *query = 0; 
     71        if (zhttpd_http_urldecode(url)) { 
     72            goto error; 
     73        } 
     74        zhttpd_env_set(&req->env, "SCRIPT_NAME", url, query - url); 
     75        zhttpd_env_set(&req->env, "QUERY_STRING", query + 1, strlen(query + 1)); 
     76    } else { 
     77        zhttpd_env_set(&req->env, "SCRIPT_NAME", url, strlen(url)); 
     78        zhttpd_env_unset(&req->env, "QUERY_STRING"); 
     79    } 
     80 
    6481    thread->state = zhttpd_request_headers; 
    65     return ZTHREAD_READY; 
     82    return zhttpd_request_headers(thread); 
    6683error: 
    6784    return zhttpd_request_error(thread, ZHTTPD_STATUS_BAD_REQUEST); 
    6885} 
    6986 
    70 int zhttpd_request_headers(zthread_t *thread) { 
    71     return ZTHREAD_DONE; 
    72 } 
    73  
    74 int zhttpd_request_status(zthread_t *thread, int status) { 
     87static int zhttpd_request_headers(zthread_t *thread) { 
     88    char buffer[4096] = "HTTP_"; 
     89    zhttpd_request_t *req = thread->context; 
     90 
     91    while (fgets(buffer + 5, sizeof(buffer) - 5, req->streamin)) { 
     92        char *c = buffer + 5, *key = buffer, *value; 
     93        if (!isalnum(*c)) { 
     94            if (*c == '\r' || *c == '\n') { 
     95                return zhttpd_request_dispatch(thread); 
     96            } else { 
     97                goto error; 
     98            } 
     99        } 
     100        for (; isalnum(*c) || *c == '-'; c++) { 
     101            *c = (*c == '-') ? '_' : toupper(*c); 
     102        } 
     103        if (*c == ':') { 
     104            *c++ = 0; 
     105        } else { 
     106            goto error; 
     107        } 
     108        for(; isblank(*c); c++); 
     109        value = c; 
     110        for (c = value + strlen(value) - 1; isspace(*c); c--); 
     111 
     112        if ((req->size += c - buffer) > zhttpd.reqhlimit) { 
     113            return zhttpd_request_error(thread, 
     114                    ZHTTPD_STATUS_REQUEST_ENTITY_TOO_LARGE); 
     115        } 
     116 
     117        if (!strcmp(key, "HTTP_CONNECTION")) { 
     118            if (req->flags & ZHTTPD_FLAG_RECYCLE && strstr(value, "close")) { 
     119                req->flags &= ~ZHTTPD_FLAG_RECYCLE; 
     120            } else if (req->flags & ZHTTPD_FLAG_RECYCLE 
     121            && (strstr(value, "keep-alive") || strstr(value, "Keep-Alive"))) { 
     122                req->flags |= ZHTTPD_FLAG_RECYCLE; 
     123            } 
     124        } else if (!strncmp(key, "HTTP_CONTENT_", 13)) { 
     125            key = buffer + 5; 
     126        } 
     127        zhttpd_env_set(&req->env, key, value, 1 + (c - value)); 
     128    } 
     129 
     130    return (errno == EAGAIN) ? ZTHREAD_BLOCKED : ZTHREAD_DONE; 
     131    error: 
     132        return zhttpd_request_error(thread, ZHTTPD_STATUS_BAD_REQUEST); 
     133} 
     134 
     135static int zhttpd_request_dispatch(zthread_t *thread) { 
     136    zhttpd_request_t *req = thread->context; 
     137    char *url = zhttpd_env_get(&req->env, "SCRIPT_NAME"); 
     138    size_t size = 0, matchlen = 0, urllen = strlen(url); 
     139 
     140    zhttpd_vfs_t *vfs = NULL; 
     141    for (zhttpd_vfs_t *c = zhttpd.vfs; c; c = c->next) { 
     142        matchlen = c->patternlen - 1; 
     143        if (size < c->patternlen && urllen >= matchlen 
     144        && !memcmp(url, c->pattern, matchlen) 
     145        && (url[matchlen] == c->pattern[matchlen] || !url[matchlen])) { 
     146            vfs = c; 
     147            size = c->patternlen; 
     148        } 
     149    } 
     150 
     151    if (!vfs) { 
     152        return zhttpd_request_error(thread, ZHTTPD_STATUS_NOT_FOUND); 
     153    } else { 
     154        matchlen = size - 1; 
     155        if (urllen > matchlen) { 
     156            zhttpd_env_set(&req->env, "PATH_INFO", url + matchlen + 1, 
     157                    urllen - matchlen - 1); 
     158        } else { 
     159            zhttpd_env_set(&req->env, "PATH_INFO", "", 1); 
     160        } 
     161        req->virtual = vfs; 
     162        thread->state = vfs->handler->entrypoint; 
     163        return ZTHREAD_READY; 
     164    } 
     165} 
     166 
     167int zhttpd_request_status(zthread_t *thread, int status, const char *msg) { 
    75168    char date[32]; 
    76169    zhttpd_request_t *req = thread->context; 
    77     const char *http = zhttpd_env_get(&req->env, "HTTP_VERSION"); 
     170    const char *http = zhttpd_env_get(&req->env, "SERVER_PROTOCOL"); 
    78171    if (!http) { 
    79172        http = "HTTP/1.0"; 
    80173    } 
     174    const char *statusmsg = (msg) ? msg : zhttpd_http_status(status); 
     175    zhttpd_http_date(0, date, sizeof(date)); 
     176    if (req->flags & ZHTTPD_FLAG_RECYCLE) { 
     177        return fprintf(req->streamout, "%s %i %s\r\nServer: %s\r\nDate: %s\r\n" 
     178            "Connection: Keep-Alive\r\nKeep-Alive: timeout=%i\r\n", 
     179            http, status, statusmsg, ZHTTPD_COPYMARK, date, zhttpd.timeout); 
     180    } else { 
     181        return fprintf(req->streamout, 
     182        "%s %i %s\r\nServer: %s\r\nDate: %s\r\nConnection: close\r\n", 
     183                http, status, statusmsg, ZHTTPD_COPYMARK, date); 
     184    } 
     185} 
     186 
     187int zhttpd_request_simple(zthread_t *thread, int status) { 
     188    zhttpd_request_t *req = thread->context; 
     189    zhttpd_request_status(thread, status, NULL); 
     190    fputs("\r\n", req->streamout); 
     191    thread->state = zhttpd_request_recycle; 
     192    return zhttpd_request_recycle(thread); 
     193} 
     194 
     195int zhttpd_request_error(zthread_t *thread, int status) { 
     196    zhttpd_request_t *req = thread->context; 
     197    zhttpd_request_status(thread, status, NULL); 
    81198    const char *statusmsg = zhttpd_http_status(status); 
    82     zhttpd_http_date(0, date, sizeof(date)); 
    83     return fprintf(req->streamout, "%s %i %s\r\nServer: %s\r\nDate: %s\r\n", 
    84             http, status, statusmsg, ZHTTPD_COPYMARK, date); 
    85 } 
    86  
    87 int zhttpd_request_error(zthread_t *thread, int status) { 
    88     zhttpd_request_t *req = thread->context; 
    89     zhttpd_request_status(thread, status); 
    90     const char *statusmsg = zhttpd_http_status(status); 
    91     fprintf(req->streamout, "Connection: close\r\nContent-Type: text/plain\r\n" 
     199    fprintf(req->streamout, "Content-Type: text/plain\r\n" 
    92200    "Content-Length: %i\r\n\r\n%s\r\n", (int)strlen(statusmsg) + 2, statusmsg); 
    93201    thread->state = zhttpd_request_recycle; 
    94     return ZTHREAD_READY; 
     202    return zhttpd_request_recycle(thread); 
    95203} 
    96204 
     
    103211    if (req->flags & ZHTTPD_FLAG_RECYCLE) { 
    104212        req->flags &= ~ZHTTPD_FLAG_RECYCLE; 
     213        if (req->handlergc) { 
     214            req->handlergc(req->handlercontext); 
     215        } 
     216        req->handlercontext = NULL; 
     217        req->handlergc = NULL; 
     218        zhttpd_env_clear_custom(&req->env); 
    105219        thread->state = zhttpd_request_magic; 
    106220        return ZTHREAD_READY; 
  • luci2/zhttpd/request.h

    r5858 r5881  
    77int zhttpd_request_error(zthread_t *thread, int status); 
    88int zhttpd_request_recycle(zthread_t *thread); 
    9 int zhttpd_request_headers(zthread_t *thread); 
     9int zhttpd_request_simple(zthread_t *thread, int status); 
     10int zhttpd_request_status(zthread_t *thread, int status, const char *msg); 
    1011 
    1112#endif /* REQUEST_H_ */ 
  • luci2/zhttpd/tcp.h

    r5858 r5881  
    2626const char *tlskey, const char *tlscert, int pem); 
    2727int zhttpd_tcp_shutdown(zthread_t *thread); 
     28ssize_t zhttpd_tcp_sendfile(zhttpd_request_t *req, int fdin, size_t count); 
    2829 
    2930#endif /* TCP_H_ */ 
  • luci2/zhttpd/zhttpd.c

    r5858 r5881  
    66#include <sys/epoll.h> 
    77#include <sys/types.h> 
     8#include <sys/wait.h> 
    89#include <sys/stat.h> 
    910#include <fcntl.h> 
     
    1112#include "zhttpd.h" 
    1213#include "tcp.h" 
     14#include "env.h" 
    1315 
    1416static volatile int running; 
     
    4143 
    4244static void sighandler(int signal) { 
    43     if (signal == SIGHUP) { 
    44         running = 2; 
    45     } else { 
    46         running = 0; 
     45    switch (signal) { 
     46        case SIGCHLD: 
     47            while (waitpid(-1, NULL, WNOHANG) > 0); 
     48 
     49        case SIGPIPE: 
     50            return; 
     51 
     52        case SIGHUP: 
     53            running = 2; 
     54 
     55        default: 
     56            running = 0; 
    4757    } 
    4858} 
    4959 
    5060static void configure() { 
     61    zhttpd_env_init(&zhttpd.mime, 16); 
     62 
     63    zhttpd_env_set(&zhttpd.mime, "htm", ZHTTPD_LITERAL_PTRSIZE("text/html")); 
     64    zhttpd_env_set(&zhttpd.mime, "html", ZHTTPD_LITERAL_PTRSIZE("text/html")); 
     65    zhttpd_env_set(&zhttpd.mime, "css", ZHTTPD_LITERAL_PTRSIZE("text/css")); 
     66    zhttpd_env_set(&zhttpd.mime, "js", ZHTTPD_LITERAL_PTRSIZE("text/javascript")); 
     67    zhttpd_env_set(&zhttpd.mime, "xml", ZHTTPD_LITERAL_PTRSIZE("application/xml")); 
     68    zhttpd_env_set(&zhttpd.mime, "xsl", ZHTTPD_LITERAL_PTRSIZE("application/xml")); 
     69    zhttpd_env_set(&zhttpd.mime, "gif", ZHTTPD_LITERAL_PTRSIZE("image/gif")); 
     70    zhttpd_env_set(&zhttpd.mime, "png", ZHTTPD_LITERAL_PTRSIZE("image/png")); 
     71    zhttpd_env_set(&zhttpd.mime, "jpg", ZHTTPD_LITERAL_PTRSIZE("image/jpeg")); 
     72    zhttpd_env_set(&zhttpd.mime, "jpeg", ZHTTPD_LITERAL_PTRSIZE("image/jpeg")); 
     73    zhttpd_env_set(&zhttpd.mime, "svg", ZHTTPD_LITERAL_PTRSIZE("image/svg+xml")); 
     74 
     75    zhttpd_options_t opt3 = {.key = (char*)"virtual", .value=(char*)"/", .next=NULL}; 
     76    zhttpd_options_t opt2 = {.key = (char*)"physical", .value=(char*)"/tmp", .next=&opt3}; 
     77    zhttpd_options_t opt1 = {.key = (char*)"handler", .value=(char*)"file", .next=&opt2}; 
     78    zhttpd_vfs_mount(&opt1); 
     79 
     80    zhttpd_options_t opt13 = {.key = (char*)"virtual", .value=(char*)"/cgi-bin", .next=NULL}; 
     81    zhttpd_options_t opt12 = {.key = (char*)"physical", .value=(char*)"/tmp", .next=&opt13}; 
     82    zhttpd_options_t opt11 = {.key = (char*)"handler", .value=(char*)"cgi", .next=&opt12}; 
     83    zhttpd_vfs_mount(&opt11); 
     84 
    5185    zhttpd.threads = 32; 
    5286    zhttpd.timeout = 5; 
    5387    zhttpd.daemonize = 0; 
     88    zhttpd.reqhlimit = 8192; 
    5489 
    5590    zhttpd.threadpool = zthread_create(zhttpd.threads); 
     
    65100    close(zhttpd.epollfd); 
    66101    zthread_destroy(zhttpd.threadpool); 
     102    uhtbl_finalize(&zhttpd.mime); 
    67103    zhttpd_vfs_t *d = NULL; 
    68104    for (zhttpd_vfs_t *c = zhttpd.vfs; c; c = d) { 
     
    97133    sigaction(SIGINT, &action, NULL); 
    98134    sigaction(SIGTERM, &action, NULL); 
    99     action.sa_handler = SIG_IGN; 
    100135    sigaction(SIGPIPE, &action, NULL); 
     136    sigaction(SIGCHLD, &action, NULL); 
    101137 
    102138    zhttpd.pidfile = "/var/run/zhttpd.pid"; 
     
    114150} 
    115151 
    116 int zhttpd_vfs_mount(const char *point, zhttpd_options_t *options) { 
     152int zhttpd_vfs_mount(zhttpd_options_t *options) { 
     153    const char *handlername = zhttpd_options_get(options, "handler"); 
     154    const char *point = zhttpd_options_get(options, "virtual"); 
     155    if (!handlername || !point) { 
     156        return ZHTTPD_EINVAL; 
     157    } 
     158 
    117159    size_t pointlen = strlen(point); 
    118     const char *handlername = zhttpd_options_get(options, "handler"); 
    119     if (!handlername) { 
    120         return ZHTTPD_EINVAL; 
    121     } 
    122  
    123160    zhttpd_handler_t *handler = NULL; 
    124161    for (zhttpd_handler_t *c = zhttpd.handler; c; c = c->next) { 
     
    146183    mount->context = handler->vfs_context_create(options); 
    147184    mount->next = zhttpd.vfs; 
     185 
     186    if (!mount->context) { 
     187        free(mount); 
     188        return ZHTTPD_EINVAL; 
     189    } 
     190 
    148191    zhttpd.vfs = mount; 
    149192 
     
    152195 
    153196const char* zhttpd_options_get(zhttpd_options_t *options, const char *key) { 
    154     size_t keylen = strlen(key); 
    155197    for (; options; options = options->next) { 
    156         if (!strcmp(key, options->option) && options->option[keylen] == '=') { 
    157             return &options->option[keylen + 1]; 
     198        if (!strcmp(key, options->key)) { 
     199            return options->value; 
    158200        } 
    159201    } 
  • luci2/zhttpd/zhttpd.h

    r5858 r5881  
    3636struct zhttpd_options { 
    3737    zhttpd_options_t *next; 
    38     char option[]; 
     38    char *key; 
     39    char *value; 
    3940}; 
    4041 
     
    4243    int flags; 
    4344    int fd; 
     45    int size; 
    4446    void *ioctx; 
    4547    uhtbl_t env; 
     
    6264    zhttpd_handler_t *next; 
    6365    zthread_state_t *entrypoint; 
    64     zthread_state_t *cleanup; 
    6566    void* (*vfs_context_create)(zhttpd_options_t*); 
    6667    void (*vfs_context_destroy)(void*); 
    67     char name[]; 
     68    char *name; 
    6869}; 
    6970 
     
    7273    zthread_context_t *threadpool; 
    7374    zhttpd_handler_t *handler; 
     75    uhtbl_t mime; 
    7476    int epollfd; 
    7577    int timeout; 
    7678    int threads; 
    7779    int daemonize; 
     80    int reqhlimit; 
    7881    char *pidfile; 
    7982}; 
    8083 
    81 #define ZHTTPD_REGISTER(handler) __attribute__((constructor (5))) \ 
    82     void __zhttpd_register() { zhttpd_register(handler); } 
     84#define ZHTTPD_HANDLER_REGISTER(name, handler) \ 
     85        void  __attribute__ ((constructor)) name##start(void) { \ 
     86                handler.next = zhttpd.handler; \ 
     87                zhttpd.handler = &handler; \ 
     88        } 
     89 
     90#define ZHTTPD_LITERAL_PTRSIZE(x) x, (sizeof(x) - 1) 
    8391 
    8492extern zhttpd_context_t zhttpd; 
    8593 
     94void zhttpd_register(zhttpd_handler_t *handler); 
     95int zhttpd_vfs_mount(zhttpd_options_t *options); 
    8696const char* zhttpd_options_get(zhttpd_options_t *options, const char *key); 
    8797