summaryrefslogtreecommitdiffstats
path: root/fluent-bit/lib/monkey/mk_server/mk_http2.c
diff options
context:
space:
mode:
Diffstat (limited to 'fluent-bit/lib/monkey/mk_server/mk_http2.c')
-rw-r--r--fluent-bit/lib/monkey/mk_server/mk_http2.c384
1 files changed, 384 insertions, 0 deletions
diff --git a/fluent-bit/lib/monkey/mk_server/mk_http2.c b/fluent-bit/lib/monkey/mk_server/mk_http2.c
new file mode 100644
index 000000000..7a4f1e82b
--- /dev/null
+++ b/fluent-bit/lib/monkey/mk_server/mk_http2.c
@@ -0,0 +1,384 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+
+/* Monkey HTTP Server
+ * ==================
+ * Copyright 2001-2017 Eduardo Silva <eduardo@monkey.io>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE
+
+#include <inttypes.h>
+
+#include <monkey/mk_http2.h>
+#include <monkey/mk_http2_settings.h>
+#include <monkey/mk_header.h>
+#include <monkey/mk_scheduler.h>
+
+/* HTTP/2 Connection Preface */
+#define MK_HTTP2_PREFACE "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
+static mk_ptr_t http2_preface = {
+ .data = MK_HTTP2_PREFACE,
+ .len = sizeof(MK_HTTP2_PREFACE) - 1
+};
+
+static inline void buffer_consume(struct mk_http2_session *h2s, int bytes)
+{
+ memmove(h2s->buffer,
+ h2s->buffer + bytes,
+ h2s->buffer_length - bytes);
+
+ MK_TRACE("[h2] consume buffer length from %i to %i",
+ h2s->buffer_length, h2s->buffer_length - bytes);
+ h2s->buffer_length -= bytes;
+}
+
+static struct mk_http2_session *mk_http2_session_create()
+{
+ struct mk_http2_session *h2s;
+
+ h2s = mk_mem_alloc(sizeof(struct mk_http2_session));
+ if (!h2s) {
+ return NULL;
+ }
+ h2s->buffer = NULL;
+ h2s->buffer_length = 0;
+ h2s->buffer_size = sizeof(h2s->buffer_fixed);
+ h2s->buffer = h2s->buffer_fixed;
+ h2s->settings = MK_HTTP2_SETTINGS_DEFAULT;
+
+ return h2s;
+}
+
+/* FIXME
+static int mk_http2_session_destroy(struct mk_http2_session *h2s)
+{
+ if (h2s->buffer != h2s->buffer_fixed) {
+ mk_mem_free(h2s->buffer);
+ }
+ mk_mem_free(h2s);
+ return 0;
+}
+
+static int mk_http2_frame_header(char *buf, uint32_t length, uint8_t type,
+ uint32_t flags, void *data)
+{
+ struct mk_http2_frame *f = (struct mk_http2_frame *) buf;
+
+ f->len_type = (length << 8 | type);
+ f->flags = flags;
+ f->payload = data;
+
+ return sizeof(struct mk_http2_frame);
+}
+
+*/
+
+/* Handle an upgraded session */
+static int mk_http2_upgrade(void *cs, void *sr, struct mk_server *server)
+{
+ struct mk_http_session *s = cs;
+ struct mk_http_request *r = sr;
+ struct mk_http2_session *h2s;
+
+ mk_header_set_http_status(r, MK_INFO_SWITCH_PROTOCOL);
+ r->headers.connection = MK_HEADER_CONN_UPGRADED;
+ r->headers.upgrade = MK_HEADER_UPGRADED_H2C;
+ mk_header_prepare(s, r, server);
+
+ h2s = mk_http2_session_create();
+ if (!h2s) {
+ return -1;
+ }
+
+ h2s->status = MK_HTTP2_UPGRADED;
+ s->conn->data = h2s;
+
+ return MK_HTTP_OK;
+}
+
+/* FIXME Decode a frame header, no more... no less
+static inline void mk_http2_frame_decode_header(uint8_t *buf,
+ struct mk_http2_frame *frame)
+{
+ struct mk_http2_session *h2s;
+ (void) h2s;
+
+ frame->len_type = mk_http2_bitdec_32u(buf);
+ frame->flags = buf[4];
+ frame->stream_id = mk_http2_bitdec_stream_id(buf + 5);
+ frame->payload = buf + 9;
+
+#ifdef MK_HAVE_TRACE
+ MK_TRACE("Frame Header");
+ printf(" length=%i, type=%i, stream_id=%i\n",
+ mk_http2_frame_len(frame),
+ mk_http2_frame_type(frame),
+ frame->stream_id);
+#endif
+}
+*/
+
+static inline int mk_http2_handle_settings(struct mk_sched_conn *conn,
+ struct mk_http2_frame *frame)
+{
+ int i;
+ int frame_len;
+ int settings;
+ int setting_size = 6; /* 16 bits identifier + 32 bits value = 6 bytes */
+ uint16_t setting_id;
+ uint32_t setting_value;
+ uint8_t *p;
+ struct mk_http2_session *h2s;
+
+ h2s = conn->data;
+ frame_len = mk_http2_frame_len(frame);
+ if (frame->flags == MK_HTTP2_SETTINGS_ACK) {
+ /*
+ * Nothing to do, the peer just received our SETTINGS and it's
+ * sending an acknowledge.
+ *
+ * note: validate that frame length is zero.
+ */
+ if (frame_len > 0) {
+ /*
+ * This must he handled as a connection error, we must reply
+ * with a FRAME_SIZE_ERROR. ref:
+ *
+ * https://httpwg.github.io/specs/rfc7540.html#SETTINGS
+ */
+
+ /* FIXME: send a GOAWAY error frame */
+ MK_TRACE("FRAME SIZE ERR: %i\n", frame_len);
+ return -1;
+
+ }
+ return 0;
+ }
+
+ /*
+ * Iterate our SETTINGS payload, it may contain many entries in the
+ * following format:
+ *
+ * +-------------------------------+
+ * | Identifier (16) |
+ * +-------------------------------+-------------------------------+
+ * | Value (32) |
+ * +---------------------------------------------------------------+
+ *
+ * 48 bits = 6 bytes
+ */
+ settings = (frame_len / setting_size);
+ for (i = 0; i < settings; i++ ) {
+ /* Seek payload per SETTINGS entry */
+ p = frame->payload + (setting_size * i);
+
+ setting_id = p[0] << 8 | p[1];
+ setting_value = p[2] << 24 | p[3] << 16 | p[4] << 8 | p[5];
+ MK_H2_TRACE(conn, "[Setting] ID=%" PRIu16 " VAL=%" PRIu32,
+ setting_id, setting_value);
+
+ switch (setting_id) {
+ case MK_HTTP2_SETTINGS_HEADER_TABLE_SIZE:
+ /* unhandled */
+ break;
+ case MK_HTTP2_SETTINGS_ENABLE_PUSH:
+ if (setting_value != 0 && setting_value != 1) {
+ /* FIXME: PROTOCOL_ERROR */
+ MK_H2_TRACE(conn, "Invalid SETTINGS_ENABLE_PUSH");
+ return -1;
+ }
+ h2s->settings.enable_push = setting_value;
+ break;
+ case MK_HTTP2_SETTINGS_MAX_CONCURRENT_STREAMS:
+ if (setting_value < 64) {
+ h2s->settings.max_concurrent_streams = setting_value;
+ }
+ else {
+ h2s->settings.max_concurrent_streams = 64;
+ }
+ MK_H2_TRACE(conn, "SETTINGS MAX_CONCURRENT_STREAMS=%i",
+ setting_value);
+ break;
+ case MK_HTTP2_SETTINGS_INITIAL_WINDOW_SIZE:
+ if (setting_value < 65535 || setting_value > 2147483647) {
+ /* FIXME: send FLOW_CONTROL_ERROR */
+ MK_H2_TRACE(conn, "Invalid INITIAL_WINDOW_SIZE");
+ return -1;
+ }
+ h2s->settings.initial_window_size = setting_value;
+ break;
+ case MK_HTTP2_SETTINGS_MAX_FRAME_SIZE:
+ if (setting_value < 16384 || setting_value > 2147483647) {
+ /* FIXME: send PROTOCOL_ERROR */
+ return -1;
+ }
+ h2s->settings.max_frame_size = setting_value;
+ break;
+ case MK_HTTP2_SETTINGS_MAX_HEADER_LIST_SIZE:
+ /* Unhandled */
+ break;
+ default:
+ /*
+ * 5.5 Extending HTTP/2: ...Implementations MUST ignore unknown
+ * or unsupported values in all extensible protocol elements...
+ */
+ break;
+ }
+ }
+
+ /* FIXME // No errors, send the ACK
+ mk_http2_send_raw(conn, MK_HTTP2_SETTINGS_ACK_FRAME,
+ sizeof(MK_HTTP2_SETTINGS_ACK_FRAME) - 1);
+ */
+ return 0;
+}
+
+
+static inline int mk_http2_frame_run(struct mk_sched_conn *conn,
+ struct mk_sched_worker *worker)
+{
+ int ret;
+ struct mk_http2_frame frame;
+ struct mk_http2_session *h2s;
+ (void) worker;
+
+ h2s = conn->data;
+
+ /* Decode the frame header */
+ //FIXME mk_http2_frame_decode_header(h2s->buffer, &frame);
+
+ /* Do some validations */
+ if (h2s->buffer_length < (MK_HTTP2_HEADER_SIZE + (frame.len_type >> 8))) {
+ /* FIXME: need more data */
+ return 0;
+ }
+
+ /* Do some work based on the frame type */
+ if (mk_http2_frame_type(&frame) == MK_HTTP2_SETTINGS) {
+ ret = mk_http2_handle_settings(conn, &frame);
+ /* FIXME: send our MK_HTTP2_SETTINGS_ACK_FRAME */
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mk_http2_sched_read(struct mk_sched_conn *conn,
+ struct mk_sched_worker *worker,
+ struct mk_server *server)
+{
+ int bytes;
+ int new_size;
+ int available;
+ char *tmp;
+ struct mk_http2_session *h2s;
+ (void) worker;
+ (void) server;
+
+ h2s = conn->data;
+ available = h2s->buffer_size - h2s->buffer_length;
+ if (available == 0) {
+ new_size = h2s->buffer_size + MK_HTTP2_CHUNK;
+ if (h2s->buffer == h2s->buffer_fixed) {
+ h2s->buffer = mk_mem_alloc(new_size);
+ if (!h2s->buffer) {
+ /* FIXME: send internal server error ? */
+ return -1;
+ }
+ memcpy(h2s->buffer, h2s->buffer_fixed, h2s->buffer_length);
+ MK_TRACE("[FD %i] Buffer new size: %i, length: %i",
+ conn->event.fd, new_size, h2s->buffer_length);
+ }
+ else {
+ MK_TRACE("[FD %i] Buffer realloc from %i to %i",
+ conn->event.fd, h2s->buffer_size, new_size);
+ tmp = mk_mem_realloc(h2s->buffer, new_size);
+ if (tmp) {
+ h2s->buffer = tmp;
+ h2s->buffer_size = new_size;
+ }
+ else {
+ /* FIXME: send internal server error ? */
+ return -1;
+ }
+
+ }
+ }
+
+ /* Read the incoming data */
+ bytes = mk_sched_conn_read(conn,
+ h2s->buffer,
+ h2s->buffer_size - h2s->buffer_length);
+ if (bytes == 0) {
+ errno = 0;
+ return -1;
+ }
+ else if (bytes == -1) {
+ return -1;
+ }
+
+ h2s->buffer_length += bytes;
+
+ /* Upgraded connections from HTTP/1.x requires the preface */
+ if (h2s->status == MK_HTTP2_UPGRADED) {
+ if (h2s->buffer_length >= http2_preface.len) {
+ if (memcmp(h2s->buffer,
+ http2_preface.data, http2_preface.len) != 0) {
+ MK_H2_TRACE(conn, "Invalid HTTP/2 preface");
+ return 0;
+ }
+
+ MK_H2_TRACE(conn, "HTTP/2 preface OK");
+
+ buffer_consume(h2s, http2_preface.len);
+ h2s->status = MK_HTTP2_OK;
+
+ /* Send out our default settings
+ mk_stream_set(&h2s->stream_settings,
+ MK_STREAM_RAW,
+ &conn->channel,
+ MK_HTTP2_SETTINGS_DEFAULT_FRAME,
+ sizeof(MK_HTTP2_SETTINGS_DEFAULT_FRAME) - 1,
+ NULL,
+ NULL, NULL, NULL);
+ */
+ }
+ else {
+ /* We need more data */
+ return 0;
+ }
+ }
+
+ /* Check that we have a minimum header size */
+ if (h2s->buffer_length < MK_HTTP2_HEADER_SIZE) {
+ MK_TRACE("HEADER FRAME incomplete %i/%i bytes",
+ h2s->buffer_length, MK_HTTP2_HEADER_SIZE);
+ return 0;
+ }
+
+ /* We have at least one frame */
+ return mk_http2_frame_run(conn, worker);
+}
+
+
+struct mk_sched_handler mk_http2_handler = {
+ .name = "http2",
+ .cb_read = mk_http2_sched_read,
+ .cb_close = NULL,
+ .cb_done = NULL,
+ .cb_upgrade = mk_http2_upgrade,
+ .sched_extra_size = sizeof(struct mk_http2_session),
+ .capabilities = MK_CAP_HTTP2
+};