summaryrefslogtreecommitdiff
path: root/datastore-leveldb/src/server_eh.c
diff options
context:
space:
mode:
Diffstat (limited to 'datastore-leveldb/src/server_eh.c')
-rw-r--r--datastore-leveldb/src/server_eh.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/datastore-leveldb/src/server_eh.c b/datastore-leveldb/src/server_eh.c
new file mode 100644
index 0000000..1fd3f9e
--- /dev/null
+++ b/datastore-leveldb/src/server_eh.c
@@ -0,0 +1,222 @@
+#include "server_eh.h"
+#include "http_parser.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <unistd.h>
+
+// utils
+
+static inline int setnonblock(int fd) {
+ int flags = fcntl(fd, F_GETFL);
+ if (flags < 0) return flags;
+ flags |= O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, flags) < 0) return -1;
+ return 0;
+}
+
+#define REQUEST_BUFFER_SIZE 2048
+
+#define alloc_cpy(dest, src, len) \
+ dest = (char*)malloc(len + 1);\
+ memcpy(dest, src, len);\
+ dest[len] = '\0';
+
+// http_parser callback and settings
+
+int null_cb(http_parser *parser) { return 0; }
+
+static int count = 0;
+int url_cb(http_parser *parser, const char *buf, size_t len) {
+ count++;
+ struct http_request *request = (struct http_request *) parser->data;
+ request->method = parser->method;
+ request->http_major = parser->http_major;
+ request->http_minor = parser->http_minor;
+ alloc_cpy(request->url, buf, len)
+ if (count % 100 == 0) printf("%d\n", count);
+ return 0;
+}
+
+int header_field_cb(http_parser *parser, const char *buf, size_t len) {
+ struct http_request *request = (struct http_request *) parser->data;
+ struct http_header *header = add_http_header(request);
+ alloc_cpy(header->name, buf, len)
+ return 0;
+}
+
+int header_value_cb(http_parser *parser, const char *buf, size_t len) {
+ struct http_request *request = (struct http_request *) parser->data;
+ struct http_header *header = request->headers;
+ while (header->next != NULL) {
+ header = header->next;
+ }
+ alloc_cpy(header->value, buf, len)
+ return 0;
+}
+
+int body_cb(http_parser *parser, const char *buf, size_t len) {
+ struct http_request *request = (struct http_request *) parser->data;
+ alloc_cpy(request->body, buf, len)
+ return 0;
+}
+
+static http_parser_settings parser_settings =
+{
+ .on_message_begin = null_cb
+ ,.on_message_complete = null_cb
+ ,.on_headers_complete = null_cb
+ ,.on_header_field = header_field_cb
+ ,.on_header_value = header_value_cb
+ ,.on_url = url_cb
+ ,.on_body = body_cb
+};
+
+struct http_request *parse_request(char *request_data, int len) {
+ http_parser *parser = malloc(sizeof(http_parser));
+ http_parser_init(parser, HTTP_REQUEST);
+ struct http_request *request = new_http_request();
+ parser->data = request;
+ int res = http_parser_execute(parser, &parser_settings, request_data, len);
+ if (res == len) {
+ if (http_should_keep_alive(parser)) {
+ request->flags |= F_HREQ_KEEPALIVE;
+ }
+ free(parser);
+ return request;
+ }
+ delete_http_request(request);
+ free(parser);
+ return NULL;
+}
+
+// libev callbacks and client structures
+
+struct client {
+ int fd;
+ ev_io ev_accept;
+ ev_io ev_read;
+ ev_io ev_write;
+ char *request_data;
+ struct http_request *request;
+ void (*handle_request)(struct http_request *request, int fd);
+};
+
+static void write_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
+ if (!(revents & EV_WRITE)) {
+ ev_io_stop(EV_A_ w);
+ return;
+ }
+ struct client *client =
+ (struct client *)
+ (((char *) w) - offsetof(struct client, ev_write));
+ struct http_request *request = client->request;
+ if (request == NULL) {
+ write(client->fd, "HTTP/1.1 400 Bad Request\r\n\r\n", 24);
+ close(client->fd);
+ free(client->request_data);
+ free(client);
+ ev_io_stop(EV_A_ w);
+ return;
+ }
+ client->handle_request(request, client->fd);
+ delete_http_request(request);
+ free(client->request_data);
+ free(client);
+ ev_io_stop(EV_A_ w);
+}
+
+static void read_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
+ if (!(revents & EV_READ)) {
+ ev_io_stop(EV_A_ w);
+ return;
+ }
+ struct client *client =
+ (struct client *)
+ (((char *) w) - offsetof(struct client, ev_read));
+ char *rbuff[REQUEST_BUFFER_SIZE + 1];
+ int sum = 0, len = 0;
+ client->request_data = NULL;
+ do {
+ len = read(client->fd, &rbuff, REQUEST_BUFFER_SIZE);
+ sum += len;
+ if (len < REQUEST_BUFFER_SIZE)
+ rbuff[len] = '\0';
+ if (client->request_data == NULL) {
+ client->request_data = malloc(len+1);
+ memcpy(client->request_data, rbuff, len);
+ } else {
+ client->request_data = realloc(client->request_data, sum + 1);
+ memcpy(client->request_data + sum - len, rbuff, len);
+ }
+ } while (len == REQUEST_BUFFER_SIZE);
+ client->request = NULL;
+ client->request = parse_request(client->request_data, len);
+ ev_io_stop(EV_A_ w);
+ ev_io_init(&client->ev_write, write_cb, client->fd, EV_WRITE);
+ ev_io_start(loop, &client->ev_write);
+}
+
+static void accept_cb(struct ev_loop *loop, struct ev_io *w, int revents) {
+ struct client *main_client =
+ (struct client *)
+ (((char *) w) - offsetof(struct client, ev_accept));
+ struct sockaddr_in client_addr;
+ socklen_t client_len = sizeof(struct sockaddr_in);
+ int client_fd = accept(w->fd, (struct sockaddr *) &client_addr, &client_len);
+ if (client_fd == -1) {
+ return;
+ }
+ if (setnonblock(client_fd) < 0) {
+ perror("failed to set client socket to nonblock");
+ return;
+ }
+ struct client *client = malloc(sizeof(struct client));
+ client->handle_request = main_client->handle_request;
+ client->fd = client_fd;
+ ev_io_init(&client->ev_read, read_cb, client->fd, EV_READ);
+ ev_io_start(loop, &client->ev_read);
+}
+
+int http_server_loop(struct http_server *server) {
+ server->loop = ev_default_loop(0);
+ int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (listen_fd < 0) {
+ perror("listen failed(socket)");
+ return -1;
+ }
+ int reuseaddr_on = 1;
+ if (setsockopt(
+ listen_fd,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ &reuseaddr_on,
+ sizeof(server->listen_addr)) == -1) {
+ perror("setsockopt failed");
+ return -1;
+ }
+ struct sockaddr *listen_addr = (struct sockaddr *) server->listen_addr;
+ if (bind(listen_fd, listen_addr, sizeof(*listen_addr)) < 0) {
+ perror("bind failed");
+ return -1;
+ }
+ if (listen(listen_fd, 5) < 0) {
+ perror("listen failed(listen)");
+ return -1;
+ }
+ if (setnonblock(listen_fd) < 0) {
+ perror("failed to set server socket to nonblock");
+ return -1;
+ }
+ struct client *main_client = malloc(sizeof(struct client));
+ main_client->handle_request = server->handle_request;
+ ev_io_init(&main_client->ev_accept, accept_cb, listen_fd, EV_READ);
+ ev_io_start(server->loop, &main_client->ev_accept);
+ server->ev_accept = &main_client->ev_accept;
+ ev_loop(server->loop, 0);
+ return 0;
+}