kore

An easy to use, scalable and secure web application framework for writing web APIs in C.
Commits | Files | Refs | README | LICENSE | git clone https://git.kore.io/kore.git

commit a9ebf37caec3a3e2c644c62184f42553bf080136
parent a228cdba0ed434a6b8418abace9684f9c58323f2
Author: Joris Vink <joris@coders.se>
Date:   Wed,  1 May 2013 21:16:09 +0200

allow modules to add headers to the response before calling http_response()
by calling http_response_header_add().

fix wrong overflow check in spdy_stream_get_header().

html_inject now exports last modified timestamp for the files that are
compiled into the module in the format static_mtime_<type>_<name>.

modules can now look into the request headers using http_request_header_get().

Diffstat:
example/src/example.c | 25++++++++++++++++++++++---
example/tools/html_inject.c | 11++++++++++-
includes/http.h | 14++++++++++++--
includes/kore.h | 5++++-
src/http.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++----------
src/spdy.c | 103+++++++++++++++++++++++++++++++++++++++++--------------------------------------
src/utils.c | 20++++++++++++++++++++
7 files changed, 172 insertions(+), 66 deletions(-)

diff --git a/example/src/example.c b/example/src/example.c @@ -47,9 +47,27 @@ int betrippin_serve_style_css(struct http_request *req) { int ret; + char *date; + time_t tstamp; - ret = http_response(req, 200, static_css_style, - static_len_css_style, "text/css"); + if (http_request_header_get(req, "if-modified-since", &date)) { + tstamp = kore_date_to_time(date); + free(date); + + kore_log("header was present with %ld", tstamp); + } + + if (tstamp != 0 && tstamp <= static_mtime_css_style) { + ret = http_response(req, 304, NULL, 0); + } else { + date = kore_time_to_date(static_mtime_css_style); + if (date != NULL) + http_response_header_add(req, "last-modified", date); + + http_response_header_add(req, "content-type", "text/css"); + ret = http_response(req, 200, static_css_style, + static_len_css_style); + } return (ret); } @@ -59,8 +77,9 @@ betrippin_serve_index(struct http_request *req) { int ret; + http_response_header_add(req, "content-type", "text/html"); ret = http_response(req, 200, static_html_index, - static_len_html_index, "text/html"); + static_len_html_index); return (ret); } diff --git a/example/tools/html_inject.c b/example/tools/html_inject.c @@ -15,6 +15,7 @@ */ #include <sys/param.h> +#include <sys/stat.h> #include <ctype.h> #include <err.h> @@ -27,6 +28,7 @@ int main(int argc, char *argv[]) { + struct stat st; size_t len; FILE *fp, *hdr; char *ext, *p, *c, buf[1024]; @@ -40,6 +42,11 @@ main(int argc, char *argv[]) if ((ext = strchr(argv[2], '.')) != NULL) *(ext)++ = '\0'; + if (stat(argv[1], &st) == -1) { + printf("stat(%s) failed: %d\n", argv[1], errno); + exit(99); + } + printf("/**** AUTO GENERATED BY MAKEFILE - DO NOT TOUCH ****/\n"); printf("#include <sys/param.h>\n\n"); printf("u_int8_t *static_%s_%s = (u_int8_t *)", ext, argv[2]); @@ -64,10 +71,12 @@ main(int argc, char *argv[]) fclose(fp); printf(";\n\n"); - printf("u_int32_t static_len_%s_%s = %ld;", ext, argv[2], len); + printf("u_int32_t static_len_%s_%s = %ld;\n", ext, argv[2], len); + printf("time_t static_mtime_%s_%s = %ld;\n", ext, argv[2], st.st_mtime); fprintf(hdr, "extern u_int8_t *static_%s_%s;\n", ext, argv[2]); fprintf(hdr, "extern u_int32_t static_len_%s_%s;\n", ext, argv[2]); + fprintf(hdr, "extern u_int32_t static_mtime_%s_%s;\n", ext, argv[2]); fclose(hdr); return (0); diff --git a/includes/http.h b/includes/http.h @@ -17,6 +17,13 @@ #ifndef __H_HTTP_H #define __H_HTTP_H +struct http_header { + char *header; + char *value; + + TAILQ_ENTRY(http_header) list; +}; + struct http_request { char *host; char *method; @@ -25,6 +32,7 @@ struct http_request { struct connection *owner; struct spdy_stream *stream; + TAILQ_HEAD(, http_header) headers; TAILQ_ENTRY(http_request) list; }; @@ -33,8 +41,10 @@ void http_process(void); time_t http_date_to_time(char *); void http_request_free(struct http_request *); int http_response(struct http_request *, int, - u_int8_t *, u_int32_t, char *); -int http_new_request(struct connection *, struct spdy_stream *, + u_int8_t *, u_int32_t); +int http_request_header_get(struct http_request *, char *, char **); +void http_response_header_add(struct http_request *, char *, char *); +int http_request_new(struct connection *, struct spdy_stream *, char *, char *, char *); #endif /* !__H_HTTP_H */ diff --git a/includes/kore.h b/includes/kore.h @@ -96,6 +96,7 @@ void *kore_malloc(size_t); void *kore_calloc(size_t, size_t); void *kore_realloc(void *, size_t); time_t kore_date_to_time(char *); +char *kore_time_to_date(time_t); char *kore_strdup(const char *); void kore_parse_config(const char *); void kore_strlcpy(char *, const char *, size_t); @@ -126,8 +127,10 @@ int net_recv_expand(struct connection *c, struct netbuf *, size_t, int net_send_queue(struct connection *, u_int8_t *, size_t, struct netbuf **, int (*cb)(struct netbuf *)); -struct spdy_stream *spdy_stream_lookup(struct connection *, u_int32_t); struct spdy_header_block *spdy_header_block_create(int); +struct spdy_stream *spdy_stream_lookup(struct connection *, u_int32_t); +int spdy_stream_get_header(struct spdy_header_block *, + char *, char **); int spdy_frame_recv(struct netbuf *); int spdy_frame_send(struct connection *, u_int16_t, diff --git a/src/http.c b/src/http.c @@ -49,12 +49,12 @@ http_init(void) } int -http_new_request(struct connection *c, struct spdy_stream *s, char *host, +http_request_new(struct connection *c, struct spdy_stream *s, char *host, char *method, char *path) { struct http_request *req; - kore_log("http_new_request(%p, %p, %s, %s, %s)", c, s, + kore_log("http_request_new(%p, %p, %s, %s, %s)", c, s, host, method, path); req = (struct http_request *)kore_malloc(sizeof(*req)); @@ -63,14 +63,39 @@ http_new_request(struct connection *c, struct spdy_stream *s, char *host, req->host = kore_strdup(host); req->path = kore_strdup(path); req->method = kore_strdup(method); + TAILQ_INIT(&(req->headers)); TAILQ_INSERT_TAIL(&http_requests, req, list); return (KORE_RESULT_OK); } void +http_response_header_add(struct http_request *req, char *header, char *value) +{ + struct http_header *hdr; + + kore_log("http_response_header_add(%p, %s, %s)", req, header, value); + + hdr = (struct http_header *)kore_malloc(sizeof(*hdr)); + hdr->header = kore_strdup(header); + hdr->value = kore_strdup(value); + TAILQ_INSERT_TAIL(&(req->headers), hdr, list); +} + +void http_request_free(struct http_request *req) { + struct http_header *hdr, *next; + + for (hdr = TAILQ_FIRST(&(req->headers)); hdr != NULL; hdr = next) { + next = TAILQ_NEXT(hdr, list); + + TAILQ_REMOVE(&(req->headers), hdr, list); + free(hdr->header); + free(hdr->value); + free(hdr); + } + free(req->method); free(req->path); free(req->host); @@ -78,25 +103,25 @@ http_request_free(struct http_request *req) } int -http_response(struct http_request *req, int status, u_int8_t *d, - u_int32_t len, char *content_type) +http_response(struct http_request *req, int status, u_int8_t *d, u_int32_t len) { u_int32_t hlen; + struct http_header *hdr; u_int8_t *htext; - char sbuf[4]; struct spdy_header_block *hblock; + char sbuf[4]; kore_log("http_response(%p, %d, %p, %d)", req, status, d, len); if (req->owner->proto == CONN_PROTO_SPDY) { snprintf(sbuf, sizeof(sbuf), "%d", status); + hblock = spdy_header_block_create(SPDY_HBLOCK_NORMAL); spdy_header_block_add(hblock, ":status", sbuf); spdy_header_block_add(hblock, ":version", "HTTP/1.1"); - if (content_type != NULL) { - spdy_header_block_add(hblock, - "content-type", content_type); - } + TAILQ_FOREACH(hdr, &(req->headers), list) + spdy_header_block_add(hblock, hdr->header, hdr->value); + htext = spdy_header_block_release(req->owner, hblock, &hlen); if (htext == NULL) return (KORE_RESULT_ERROR); @@ -126,6 +151,21 @@ http_response(struct http_request *req, int status, u_int8_t *d, return (KORE_RESULT_OK); } +int +http_request_header_get(struct http_request *req, char *header, char **out) +{ + int r; + + if (req->owner->proto == CONN_PROTO_SPDY) { + r = spdy_stream_get_header(req->stream->hblock, header, out); + } else { + kore_log("http not supported yet"); + r = KORE_RESULT_ERROR; + } + + return (r); +} + void http_process(void) { @@ -160,5 +200,5 @@ http_generic_404(struct http_request *req) kore_log("http_generic_404(%s, %s, %s)", req->host, req->method, req->path); - return (http_response(req, 404, NULL, 0, NULL)); + return (http_response(req, 404, NULL, 0)); } diff --git a/src/spdy.c b/src/spdy.c @@ -40,8 +40,6 @@ static int spdy_ctrl_frame_syn_stream(struct netbuf *); static int spdy_ctrl_frame_settings(struct netbuf *); static int spdy_ctrl_frame_ping(struct netbuf *); -static int spdy_stream_get_header(struct spdy_header_block *, - char *, char **); static int spdy_zlib_inflate(struct connection *, u_int8_t *, size_t, u_int8_t **, u_int32_t *); @@ -237,6 +235,59 @@ spdy_header_block_release(struct connection *c, return (deflated); } +int +spdy_stream_get_header(struct spdy_header_block *s, char *header, char **out) +{ + char *cmp, t[128]; + u_int8_t *p, *end; + u_int32_t i, nlen, vlen; + + kore_log("spdy_stream_get_header(%p, %s) <%d>", s, header, + s->header_pairs); + + p = s->header_block + 4; + end = s->header_block + s->header_block_len; + + if (p >= end) { + kore_log("p >= end when looking for headers"); + return (KORE_RESULT_ERROR); + } + + for (i = 0; i < s->header_pairs; i++) { + nlen = net_read32(p); + if ((p + nlen + 4) > end) { + kore_log("nlen out of bounds on %d (%d)", i, nlen); + return (KORE_RESULT_ERROR); + } + + vlen = net_read32(p + nlen + 4); + if ((p + nlen + vlen + 8) > end) { + kore_log("vlen out of bounds on %d (%d)", i, vlen); + return (KORE_RESULT_ERROR); + } + + cmp = (char *)(p + 4); + memcpy(t, cmp, nlen); + t[nlen] = '\0'; + kore_log("header %s", t); + + if (!strncasecmp(cmp, header, nlen)) { + kore_log("found %s header", header); + + cmp = (char *)(p + nlen + 8); + *out = (char *)kore_malloc(vlen + 1); + kore_strlcpy(*out, cmp, vlen + 1); + return (KORE_RESULT_OK); + } + + kore_log("pair name %d bytes, value %d bytes", nlen, vlen); + + p += nlen + vlen + 8; + } + + return (KORE_RESULT_ERROR); +} + static int spdy_ctrl_frame_syn_stream(struct netbuf *nb) { @@ -321,7 +372,7 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb) GET_HEADER(":path", &path); GET_HEADER(":method", &method); GET_HEADER(":host", &host); - if (!http_new_request(c, s, host, method, path)) { + if (!http_request_new(c, s, host, method, path)) { free(s->hblock->header_block); free(s->hblock); free(s); @@ -366,52 +417,6 @@ spdy_ctrl_frame_ping(struct netbuf *nb) } static int -spdy_stream_get_header(struct spdy_header_block *s, char *header, char **out) -{ - char *cmp; - u_int8_t *p, *end; - u_int32_t i, nlen, vlen; - - p = s->header_block + 4; - end = s->header_block + s->header_block_len; - - if (p >= end) { - kore_log("p >= end when looking for headers"); - return (KORE_RESULT_ERROR); - } - - for (i = 0; i < s->header_pairs; i++) { - nlen = net_read32(p); - if ((p + nlen + 4) >= end) { - kore_log("nlen out of bounds (%d)", nlen); - return (KORE_RESULT_ERROR); - } - - vlen = net_read32(p + nlen + 4); - if ((p + nlen + vlen + 8) >= end) { - kore_log("vlen out of bounds (%d)", vlen); - return (KORE_RESULT_ERROR); - } - - cmp = (char *)(p + 4); - if (!strncasecmp(cmp, header, nlen)) { - kore_log("found %s header", header); - - cmp = (char *)(p + nlen + 8); - *out = (char *)kore_malloc(vlen + 1); - kore_strlcpy(*out, cmp, vlen + 1); - return (KORE_RESULT_OK); - } - - kore_log("pair name %d bytes, value %d bytes", nlen, vlen); - - p += nlen + vlen + 8; - } - - return (KORE_RESULT_ERROR); -} - -static int spdy_zlib_inflate(struct connection *c, u_int8_t *src, size_t len, u_int8_t **dst, u_int32_t *olen) { diff --git a/src/utils.c b/src/utils.c @@ -255,6 +255,26 @@ out: return (t); } +char * +kore_time_to_date(time_t now) +{ + struct tm *tm; + static time_t last = 0; + static char tbuf[32]; + + if (now != last) { + last = now; + + tm = gmtime(&now); + if (!strftime(tbuf, sizeof(tbuf), "%a, %d %b %Y %T GMT", tm)) { + kore_log("strftime() gave us NULL (%ld)", now); + return (NULL); + } + } + + return (tbuf); +} + void fatal(const char *fmt, ...) {