commit f46bba50efeb7856352b47d43f56c39e237e8bfb
parent 5f983d575b4e7fa8766f7acdd977173bfd6ae252
Author: Joris Vink <joris@coders.se>
Date: Thu, 2 May 2013 03:51:04 +0200
add very basic support for HTTP/1.1.
Diffstat:
8 files changed, 238 insertions(+), 17 deletions(-)
diff --git a/Makefile b/Makefile
@@ -3,7 +3,7 @@
CC=gcc
BIN=kore
-S_SRC= src/kore.c src/config.c src/net.c src/spdy.c src/http.c \
+S_SRC= src/kore.c src/buf.c src/config.c src/net.c src/spdy.c src/http.c \
src/module.c src/utils.c src/zlib_dict.c
S_OBJS= $(S_SRC:.c=.o)
diff --git a/includes/http.h b/includes/http.h
@@ -17,6 +17,9 @@
#ifndef __H_HTTP_H
#define __H_HTTP_H
+#define HTTP_HEADER_MAX_LEN 8192
+#define HTTP_REQ_HEADER_MAX 25
+
struct http_header {
char *header;
char *value;
@@ -28,11 +31,11 @@ struct http_request {
char *host;
char *method;
char *path;
-
struct connection *owner;
struct spdy_stream *stream;
- TAILQ_HEAD(, http_header) headers;
+ TAILQ_HEAD(, http_header) req_headers;
+ TAILQ_HEAD(, http_header) resp_headers;
TAILQ_ENTRY(http_request) list;
};
@@ -45,6 +48,8 @@ int http_response(struct http_request *, int,
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 *);
+ char *, char *, char *, struct http_request **);
+
+int http_header_recv(struct netbuf *);
#endif /* !__H_HTTP_H */
diff --git a/includes/kore.h b/includes/kore.h
@@ -92,6 +92,15 @@ struct kore_module_handle {
TAILQ_ENTRY(kore_module_handle) list;
};
+#define KORE_BUF_INITIAL 128
+#define KORE_BUF_INCREMENT KORE_BUF_INITIAL
+
+struct kore_buf {
+ u_int8_t *data;
+ u_int32_t length;
+ u_int32_t offset;
+};
+
extern int server_port;
extern char *server_ip;
@@ -130,6 +139,10 @@ int net_recv_expand(struct connection *c, struct netbuf *, size_t,
int net_send_queue(struct connection *, u_int8_t *, size_t, int,
struct netbuf **, int (*cb)(struct netbuf *));
+struct kore_buf *kore_buf_create(u_int32_t);
+void kore_buf_append(struct kore_buf *, u_int8_t *, u_int32_t);
+u_int8_t *kore_buf_release(struct kore_buf *, 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 *,
diff --git a/src/buf.c b/src/buf.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013 Joris Vink <joris@coders.se>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+#include "spdy.h"
+#include "kore.h"
+
+struct kore_buf *
+kore_buf_create(u_int32_t initial)
+{
+ struct kore_buf *buf;
+
+ buf = (struct kore_buf *)kore_malloc(sizeof(*buf));
+ buf->data = (u_int8_t *)kore_malloc(initial);
+ buf->length = initial;
+ buf->offset = 0;
+
+ return (buf);
+}
+
+void
+kore_buf_append(struct kore_buf *buf, u_int8_t *d, u_int32_t len)
+{
+ if ((buf->offset + len) >= buf->length) {
+ buf->length += len + KORE_BUF_INCREMENT;
+ buf->data = (u_int8_t *)kore_realloc(buf->data, buf->length);
+ }
+
+ memcpy((buf->data + buf->offset), d, len);
+ buf->offset += len;
+}
+
+u_int8_t *
+kore_buf_release(struct kore_buf *buf, u_int32_t *len)
+{
+ u_int8_t *p;
+
+ p = buf->data;
+ *len = buf->offset;
+ free(buf);
+
+ return (p);
+}
diff --git a/src/http.c b/src/http.c
@@ -50,7 +50,7 @@ http_init(void)
int
http_request_new(struct connection *c, struct spdy_stream *s, char *host,
- char *method, char *path)
+ char *method, char *path, struct http_request **out)
{
struct http_request *req;
@@ -63,9 +63,13 @@ http_request_new(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_INIT(&(req->resp_headers));
+ TAILQ_INIT(&(req->req_headers));
TAILQ_INSERT_TAIL(&http_requests, req, list);
+ if (out != NULL)
+ *out = req;
+
return (KORE_RESULT_OK);
}
@@ -79,7 +83,7 @@ http_response_header_add(struct http_request *req, char *header, char *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);
+ TAILQ_INSERT_TAIL(&(req->resp_headers), hdr, list);
}
void
@@ -87,10 +91,19 @@ http_request_free(struct http_request *req)
{
struct http_header *hdr, *next;
- for (hdr = TAILQ_FIRST(&(req->headers)); hdr != NULL; hdr = next) {
+ for (hdr = TAILQ_FIRST(&(req->resp_headers)); hdr != NULL; hdr = next) {
next = TAILQ_NEXT(hdr, list);
- TAILQ_REMOVE(&(req->headers), hdr, list);
+ TAILQ_REMOVE(&(req->resp_headers), hdr, list);
+ free(hdr->header);
+ free(hdr->value);
+ free(hdr);
+ }
+
+ for (hdr = TAILQ_FIRST(&(req->req_headers)); hdr != NULL; hdr = next) {
+ next = TAILQ_NEXT(hdr, list);
+
+ TAILQ_REMOVE(&(req->req_headers), hdr, list);
free(hdr->header);
free(hdr->value);
free(hdr);
@@ -107,9 +120,10 @@ http_response(struct http_request *req, int status, u_int8_t *d, u_int32_t len)
{
u_int32_t hlen;
struct http_header *hdr;
+ struct kore_buf *buf;
u_int8_t *htext;
struct spdy_header_block *hblock;
- char sbuf[4];
+ char sbuf[64];
kore_log("http_response(%p, %d, %p, %d)", req, status, d, len);
@@ -119,7 +133,7 @@ http_response(struct http_request *req, int status, u_int8_t *d, u_int32_t len)
hblock = spdy_header_block_create(SPDY_HBLOCK_NORMAL);
spdy_header_block_add(hblock, ":status", sbuf);
spdy_header_block_add(hblock, ":version", "HTTP/1.1");
- TAILQ_FOREACH(hdr, &(req->headers), list)
+ TAILQ_FOREACH(hdr, &(req->resp_headers), list)
spdy_header_block_add(hblock, hdr->header, hdr->value);
htext = spdy_header_block_release(req->owner, hblock, &hlen);
@@ -130,9 +144,12 @@ http_response(struct http_request *req, int status, u_int8_t *d, u_int32_t len)
0, hlen, req->stream->stream_id))
return (KORE_RESULT_ERROR);
- if (!net_send_queue(req->owner, htext, hlen, 0, NULL, NULL))
+ if (!net_send_queue(req->owner, htext, hlen, 0, NULL, NULL)) {
+ free(htext);
return (KORE_RESULT_ERROR);
+ }
+ free(htext);
if (len > 0) {
if (!spdy_frame_send(req->owner, SPDY_DATA_FRAME,
0, len, req->stream->stream_id))
@@ -145,7 +162,33 @@ http_response(struct http_request *req, int status, u_int8_t *d, u_int32_t len)
FLAG_FIN, 0, req->stream->stream_id))
return (KORE_RESULT_ERROR);
} else {
- kore_log("normal http not functional yet");
+ buf = kore_buf_create(KORE_BUF_INITIAL);
+
+ snprintf(sbuf, sizeof(sbuf), "HTTP/1.1 %d\r\n", status);
+ kore_buf_append(buf, (u_int8_t *)sbuf, strlen(sbuf));
+
+ snprintf(sbuf, sizeof(sbuf), "Content-length: %d\r\n", len);
+ kore_buf_append(buf, (u_int8_t *)sbuf, strlen(sbuf));
+
+ snprintf(sbuf, sizeof(sbuf), "Connection: close\r\n");
+ kore_buf_append(buf, (u_int8_t *)sbuf, strlen(sbuf));
+
+ TAILQ_FOREACH(hdr, &(req->resp_headers), list) {
+ snprintf(sbuf, sizeof(sbuf), "%s: %s\r\n",
+ hdr->header, hdr->value);
+ kore_buf_append(buf, (u_int8_t *)sbuf, strlen(sbuf));
+ }
+
+ kore_buf_append(buf, (u_int8_t *)"\r\n\r\n", 4);
+ htext = kore_buf_release(buf, &hlen);
+ if (!net_send_queue(req->owner, htext, hlen, 0, NULL, NULL)) {
+ free(htext);
+ return (KORE_RESULT_ERROR);
+ }
+
+ free(htext);
+ if (!net_send_queue(req->owner, d, len, 0, NULL, NULL))
+ return (KORE_RESULT_ERROR);
}
return (KORE_RESULT_OK);
@@ -154,12 +197,21 @@ http_response(struct http_request *req, int status, u_int8_t *d, u_int32_t len)
int
http_request_header_get(struct http_request *req, char *header, char **out)
{
- int r;
+ int r;
+ struct http_header *hdr;
if (req->owner->proto == CONN_PROTO_SPDY) {
r = spdy_stream_get_header(req->stream->hblock, header, out);
} else {
- kore_log("http not supported yet");
+ TAILQ_FOREACH(hdr, &(req->req_headers), list) {
+ if (!strcasecmp(hdr->header, header)) {
+ r = strlen(hdr->value) + 1;
+ *out = (char *)kore_malloc(r);
+ kore_strlcpy(*out, hdr->value, r);
+ return (KORE_RESULT_OK);
+ }
+ }
+
r = KORE_RESULT_ERROR;
}
@@ -194,6 +246,78 @@ http_process(void)
}
}
+int
+http_header_recv(struct netbuf *nb)
+{
+ char *p;
+ struct http_header *hdr;
+ struct http_request *req;
+ int h, i, v;
+ char *request[4], *host[3], *hbuf;
+ char *headers[HTTP_REQ_HEADER_MAX];
+ struct connection *c = (struct connection *)nb->owner;
+
+ kore_log("http_header_recv(%p)", nb);
+
+ nb->buf[nb->len] = '\0';
+ if ((p = strrchr((char *)nb->buf, '\r')) == NULL)
+ return (KORE_RESULT_OK);
+ if (nb->len > 2 && strncmp((p - 2), "\r\n\r\n", 4))
+ return (KORE_RESULT_OK);
+
+ hbuf = kore_strdup((const char *)nb->buf);
+
+ h = kore_split_string(hbuf, "\r\n", headers, HTTP_REQ_HEADER_MAX);
+ if (h < 2) {
+ free(hbuf);
+ return (KORE_RESULT_ERROR);
+ }
+
+ if (strlen(headers[0]) > 3 && strncasecmp(headers[0], "get", 3)) {
+ free(hbuf);
+ return (KORE_RESULT_ERROR);
+ }
+
+ v = kore_split_string(headers[0], " ", request, 4);
+ if (v != 3) {
+ free(hbuf);
+ return (KORE_RESULT_ERROR);
+ }
+
+ v = kore_split_string(headers[1], ":", host, 3);
+ if (v != 2) {
+ free(hbuf);
+ return (KORE_RESULT_ERROR);
+ }
+
+ if (strlen(host[0]) != 4 || strncasecmp(host[0], "host", 5)) {
+ free(hbuf);
+ return (KORE_RESULT_ERROR);
+ }
+
+ if (!http_request_new(c, NULL, host[1], request[0], request[1], &req)) {
+ free(hbuf);
+ return (KORE_RESULT_ERROR);
+ }
+
+ for (i = 2; i < h; i++) {
+ p = strchr(headers[i], ':');
+ if (p == NULL) {
+ kore_log("malformed header: '%s'", headers[i]);
+ continue;
+ }
+
+ *(p++) = '\0';
+ hdr = (struct http_header *)kore_malloc(sizeof(*hdr));
+ hdr->header = kore_strdup(headers[i]);
+ hdr->value = kore_strdup(p);
+ TAILQ_INSERT_TAIL(&(req->req_headers), hdr, list);
+ }
+
+ free(hbuf);
+ return (KORE_RESULT_OK);
+}
+
static int
http_generic_404(struct http_request *req)
{
diff --git a/src/kore.c b/src/kore.c
@@ -329,6 +329,10 @@ kore_connection_handle(struct connection *c, int flags)
} else {
kore_log("using HTTP/1.1");
c->proto = CONN_PROTO_HTTP;
+ if (!net_recv_queue(c, HTTP_HEADER_MAX_LEN,
+ NETBUF_CALL_CB_ALWAYS, NULL,
+ http_header_recv))
+ return (KORE_RESULT_ERROR);
}
c->state = CONN_STATE_ESTABLISHED;
diff --git a/src/spdy.c b/src/spdy.c
@@ -136,7 +136,6 @@ spdy_frame_send(struct connection *c, u_int16_t type, u_int8_t flags,
net_write32(&nb[8], stream_id);
length = 12;
break;
- break;
case SPDY_DATA_FRAME:
net_write32(&nb[0], stream_id);
nb[0] &= ~(1 << 7);
@@ -370,7 +369,7 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb)
GET_HEADER(":path", &path);
GET_HEADER(":method", &method);
GET_HEADER(":host", &host);
- if (!http_request_new(c, s, host, method, path)) {
+ if (!http_request_new(c, s, host, method, path, NULL)) {
free(s->hblock->header_block);
free(s->hblock);
free(s);
diff --git a/src/utils.c b/src/utils.c
@@ -63,6 +63,7 @@ kore_malloc(size_t len)
if ((ptr = malloc(len)) == NULL)
fatal("kore_malloc(%d): %d", len, errno);
+ memset(ptr, 0, len);
return (ptr);
}
@@ -85,6 +86,7 @@ kore_calloc(size_t memb, size_t len)
if ((ptr = calloc(memb, len)) == NULL)
fatal("kore_calloc(%d, %d): %d", memb, len, errno);
+ memset(ptr, 0, memb * len);
return (ptr);
}