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 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:
Makefile | 2+-
includes/http.h | 11++++++++---
includes/kore.h | 13+++++++++++++
src/buf.c | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/http.c | 146+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
src/kore.c | 4++++
src/spdy.c | 3+--
src/utils.c | 2++
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); }