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:
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, ...)
{