Initial commit

This commit is contained in:
Mark Ellzey 2011-05-11 18:07:41 -04:00
commit ea3ff86b28
9 changed files with 648 additions and 0 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "http_parser"]
path = http_parser
url = https://github.com/ry/http-parser.git

37
CMakeLists.txt Normal file
View file

@ -0,0 +1,37 @@
cmake_minimum_required(VERSION 2.8)
project(reason)
set(PROJECT_MAJOR_VERSION 0)
set(PROJECT_MINOR_VERSION 1)
set(PROJECT_PATCH_VERSION 0)
set (PROJECT_VERSION ${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION})
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
include(BaseConfig)
message("Build Type: ${CMAKE_BUILD_TYPE}")
message("Std CFLAGS: ${CMAKE_C_FLAGS}")
message("Dbg CFLAGS: ${CMAKE_C_FLAGS_DEBUG}")
message("Rel CFLAGS: ${CMAKE_C_FLAGS_RELEASE}")
find_package(LibEvent)
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/http_parser
)
set(LIBEVHTP_EXTERNAL_LIBS
${LIBEVENT_LIBRARY}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/build
)
set(LIBEVHTP_SOURCES http_parser/http_parser.c evhtp.c)
add_library(libevhtp STATIC ${LIBEVHTP_SOURCES})
set_target_properties(libevhtp PROPERTIES OUTPUT_NAME "evhtp")
target_link_libraries(libevhtp ${LIVEVHTP_EXTERNAL_LIBS})
add_executable(test test.c)
target_link_libraries(test libevhtp event)

View file

@ -0,0 +1,20 @@
if (CMAKE_COMPILER_IS_GNUCC)
set(RSN_BASE_C_FLAGS "-std=c99 -Wall")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${RSN_BASE_C_FLAGS}")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${RSN_BASE_C_FLAGS} -DDEBUG -ggdb")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${RSN_BASE_C_FLAGS}")
if(APPLE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshorten-64-to-32 -D_BSD_SOURCE")
endif(APPLE)
if (UNIX)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_BSD_SOURCE -D_POSIX_C_SOURCE=199309L")
endif(UNIX)
endif(CMAKE_COMPILER_IS_GNUCC)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif(NOT CMAKE_BUILD_TYPE)

View file

@ -0,0 +1,21 @@
# - Try to find the LibEvent config processing library
# Once done this will define
#
# LIBEVENT_FOUND - System has LibEvent
# LIBEVENT_INCLUDE_DIR - the LibEvent include directory
# LIBEVENT_LIBRARIES 0 The libraries needed to use LibEvent
FIND_PATH(LIBEVENT_INCLUDE_DIR NAMES event.h)
FIND_LIBRARY(LIBEVENT_LIBRARY NAMES event)
FIND_LIBRARY(LIBEVENT_CORE_LIBRARY NAMES event_core)
FIND_LIBRARY(LIBEVENT_PTHREADS_LIBRARY NAMES event_pthreads)
FIND_LIBRARY(LIBEVENT_EXTRA_LIBRARY NAMES event_extra)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibEvent DEFAULT_MSG LIBEVENT_LIBRARY LIBEVENT_INCLUDE_DIR)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibEventPthreads DEFAULT_MSG LIBEVENT_PTHREADS_LIBRARY LIBEVENT_INCLUDE_DIR)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibEventCore DEFAULT_MSG LIBEVENT_CORE_LIBRARY LIBEVENT_INCLUDE_DIR)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibEventExtra DEFAULT_MSG LIBEVENT_EXTRA_LIBRARY LIBEVENT_INCLUDE_DIR)
MARK_AS_ADVANCED(LIBEVENT_INCLUDE_DIR LIBEVENT_LIBRARY LIBEVENT_PTHREADS_LIBRARY LIBEVENT_CORE_LIBRARY LIBEVENT_EXTRA_LIBRARY)

0
build/placeholder Normal file
View file

374
evhtp.c Normal file
View file

@ -0,0 +1,374 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "evhtp.h"
evhtp_request_t *
evhtp_request_new(void) {
evhtp_request_t * request = NULL;
if (!(request = calloc(sizeof(evhtp_request_t), sizeof(char)))) {
return NULL;
}
TAILQ_INIT(&request->headers);
return request;
}
int
evhtp_conn_hook(evhtp_connection_t * conn, evhtp_hook_type type, void * cb) {
if (conn == NULL) {
return -1;
}
if (conn->hooks == NULL) {
if (!(conn->hooks = calloc(sizeof(evhtp_conn_hooks_t), sizeof(char)))) {
return -1;
}
}
switch (type) {
case EVHTP_HOOK_POST_HEADERS:
conn->hooks->post_headers_cb = (evhtp_hook_post_headers)cb;
break;
case EVHTP_HOOK_SINGLE_HEADER:
conn->hooks->single_header_cb = (evhtp_hook_single_header)cb;
break;
case EVHTP_HOOK_ON_PATH:
conn->hooks->path_cb = (evhtp_hook_on_path)cb;
break;
case EVHTP_HOOK_ON_BODY_READ:
conn->hooks->body_read_cb = (evhtp_hook_on_body_read)cb;
break;
case EVHTP_HOOK_COMPLETE:
conn->hooks->complete_cb = (evhtp_hook_complete)cb;
break;
default:
return -1;
}
return 0;
}
int
evhtp_headers_for_each(evhtp_headers_t * headers, evhtp_headers_iter_cb cb, void * arg) {
evhtp_header_t * header = NULL;
if (!headers || !cb) {
return -1;
}
TAILQ_FOREACH(header, headers, next) {
int res;
if ((res = cb(header, arg))) {
return res;
}
}
return 0;
}
static int
_htp_start_cb(http_parser * p) {
evhtp_connection_t * conn = NULL;
if (!(conn = (evhtp_connection_t *)p->data)) {
return -1;
}
if (!(conn->request = evhtp_request_new())) {
return -1;
}
return 0;
}
static int
_htp_headers_complete_cb(http_parser * p) {
evhtp_connection_t * conn = NULL;
if (!(conn = (evhtp_connection_t *)p->data)) {
return -1;
}
if (conn->hooks && conn->hooks->post_headers_cb) {
evhtp_headers_t * hdrs = &conn->request->headers;
if (conn->hooks->post_headers_cb(conn, hdrs, conn->arg) != EVHTP_RES_OK) {
return -1;
}
}
conn->request->method = p->method;
conn->request->major = p->http_major;
conn->request->minor = p->http_minor;
return 0;
}
static int
_htp_end_cb(http_parser * p) {
evhtp_connection_t * conn = NULL;
if (!(conn = (evhtp_connection_t *)p->data)) {
return -1;
}
if (conn->hooks && conn->hooks->complete_cb) {
if (conn->hooks->complete_cb(conn, conn->request, conn->arg) != EVHTP_RES_OK) {
return -1;
}
}
return 0;
}
static int
_htp_path_cb(http_parser * p, const char * buf, size_t len) {
evhtp_connection_t * conn = NULL;
char * uri;
if (len == 0 || buf == NULL) {
return -1;
}
if (!(conn = (evhtp_connection_t *)p->data)) {
return -1;
}
conn->request->uri = malloc(len + 1);
conn->request->uri[len] = '\0';
memcpy(conn->request->uri, buf, len);
if (conn->hooks && conn->hooks->path_cb) {
if (conn->hooks->path_cb(conn, conn->request->uri, conn->arg) != EVHTP_RES_OK) {
return -1;
}
}
return 0;
}
static int
_htp_query_str_cb(http_parser * p, const char * buf, size_t len) {
return 0;
}
static int
_htp_url_cb(http_parser * p, const char * buf, size_t len) {
return 0;
}
static int
_htp_fragment_cb(http_parser * p, const char * buf, size_t len) {
return 0;
}
static int
_htp_header_key_cb(http_parser * p, const char * buf, size_t len) {
evhtp_header_t * header = NULL;
evhtp_connection_t * conn = NULL;
if (!(conn = (evhtp_connection_t *)p->data)) {
return -1;
}
if (!(header = calloc(sizeof(evhtp_header_t), sizeof(char)))) {
return -1;
}
header->key = malloc(len + 1);
header->key[len] = '\0';
memcpy(header->key, buf, len);
TAILQ_INSERT_TAIL(&conn->request->headers, header, next);
return 0;
}
static int
_htp_header_val_cb(http_parser * p, const char * buf, size_t len) {
evhtp_header_t * header = NULL;
evhtp_connection_t * conn = NULL;
if (!(conn = (evhtp_connection_t *)p->data)) {
return -1;
}
if (!(header = TAILQ_LAST(&conn->request->headers, evhtp_headers))) {
return -1;
}
header->val = malloc(len + 1);
header->val[len] = '\0';
memcpy(header->val, buf, len);
if (conn->hooks && conn->hooks->single_header_cb) {
if (conn->hooks->single_header_cb(conn, header, conn->arg) != EVHTP_RES_OK) {
return -1;
}
}
return 0;
}
static int
_htp_body_cb(http_parser * p, const char * buf, size_t len) {
return 0;
}
void
evhtp_connection_free(evhtp_connection_t * conn) {
if (conn == NULL) {
return;
}
if (conn->hooks) {
/* evhtp_conn_hooks_free() */
free(conn->hooks);
}
if (conn->parser) {
free(conn->parser);
}
if (conn->bev) {
bufferevent_free(conn->bev);
}
free(conn);
}
static void
_htp_recv_cb(evbev_t * bev, void * arg) {
evhtp_connection_t * conn = NULL;
struct evbuffer * ibuf = NULL;
size_t nread = 0;
if (!(conn = (evhtp_connection_t *)arg)) {
return;
}
ibuf = bufferevent_get_input(bev);
nread = http_parser_execute(conn->parser, &conn->evhtp->psets,
(const char *)evbuffer_pullup(ibuf, evbuffer_get_length(ibuf)),
evbuffer_get_length(ibuf));
evbuffer_drain(ibuf, nread);
}
static void
_htp_accept_cb(evserv_t * serv, int fd, struct sockaddr * s, int sl, void * arg) {
evhtp_t * htp = NULL;
evbase_t * evbase = NULL;
evbev_t * bev = NULL;
evhtp_connection_t * conn = NULL;
if (!(htp = (evhtp_t *)arg)) {
return;
}
if (htp->pre_accept_cb != NULL) {
if (htp->pre_accept_cb(fd, s, sl, htp->arg) != EVHTP_RES_OK) {
return;
}
}
evbase = evconnlistener_get_base(serv);
bev = bufferevent_socket_new(evbase, fd, BEV_OPT_CLOSE_ON_FREE);
if (!(conn = calloc(sizeof(evhtp_connection_t), sizeof(char)))) {
return;
}
conn->bev = bev;
conn->evhtp = htp;
conn->parser = malloc(sizeof(http_parser));
conn->parser->data = conn;
http_parser_init(conn->parser, HTTP_REQUEST);
if (htp->post_accept_cb != NULL) {
if (htp->post_accept_cb(conn, htp->arg) != EVHTP_RES_OK) {
evhtp_connection_free(conn);
return;
}
}
bufferevent_setcb(bev, _htp_recv_cb, NULL, NULL, (void *)conn);
bufferevent_enable(bev, EV_READ | EV_WRITE);
} /* _htp_accept_cb */
int
evhtp_set_pre_accept_cb(evhtp_t * htp, evhtp_pre_accept cb) {
if (htp == NULL || cb == NULL) {
return -1;
}
htp->pre_accept_cb = cb;
return 0;
}
int
evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept cb) {
if (htp == NULL || cb == NULL) {
return -1;
}
htp->post_accept_cb = cb;
return 0;
}
evhtp_t *
evhtp_new(evbase_t * evbase, evhtp_cfg * cfg, void * arg) {
struct sockaddr_in sin = { 0 };
evhtp_t * htp = NULL;
if (evbase == NULL) {
return NULL;
}
if (!(htp = calloc(sizeof(evhtp_t), sizeof(char)))) {
return NULL;
}
sin.sin_family = AF_INET;
sin.sin_port = htons(cfg->bind_port);
sin.sin_addr.s_addr = inet_addr(cfg->bind_addr);
htp->psets.on_message_begin = _htp_start_cb;
htp->psets.on_headers_complete = _htp_headers_complete_cb;
htp->psets.on_message_complete = _htp_end_cb;
htp->psets.on_path = _htp_path_cb;
htp->psets.on_query_string = _htp_query_str_cb;
htp->psets.on_url = _htp_url_cb;
htp->psets.on_fragment = _htp_fragment_cb;
htp->psets.on_header_field = _htp_header_key_cb;
htp->psets.on_header_value = _htp_header_val_cb;
htp->psets.on_body = _htp_body_cb;
htp->evbase = evbase;
htp->config = cfg;
htp->pre_accept_cb = NULL;
htp->post_accept_cb = NULL;
htp->arg = arg;
htp->serv = evconnlistener_new_bind(evbase,
_htp_accept_cb, (void *)htp, LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, -1,
(struct sockaddr *)&sin, sizeof(sin));
return htp;
} /* evhtp_new */

116
evhtp.h Normal file
View file

@ -0,0 +1,116 @@
#ifndef __EVHTP_H__
#define __EVHTP_H__
#include <sys/queue.h>
#include <event.h>
#include <event2/listener.h>
#include <http_parser.h>
typedef enum http_method evhtp_method;
typedef uint8_t evhtp_ver_major;
typedef uint8_t evhtp_ver_minor;
typedef struct event_base evbase_t;
typedef struct bufferevent evbev_t;
typedef struct evconnlistener evserv_t;
typedef struct evhtp_cfg evhtp_cfg;
typedef struct evhtp evhtp_t;
typedef struct evhtp_connection evhtp_connection_t;
typedef struct evhtp_headers evhtp_headers_t;
typedef struct evhtp_header evhtp_header_t;
typedef struct evhtp_conn_hooks evhtp_conn_hooks_t;
typedef struct evhtp_request evhtp_request_t;
typedef enum evhtp_hook_type evhtp_hook_type;
typedef enum evhtp_res evhtp_res;
typedef evhtp_res (*evhtp_pre_accept)(int fd, struct sockaddr *, int, void *);
typedef evhtp_res (*evhtp_post_accept)(evhtp_connection_t *, void *);
typedef evhtp_res (*evhtp_hook_post_headers)(evhtp_connection_t *, evhtp_headers_t *, void *);
typedef evhtp_res (*evhtp_hook_single_header)(evhtp_connection_t *, evhtp_header_t *, void *);
typedef evhtp_res (*evhtp_hook_on_path)(evhtp_connection_t *, const char *, void *);
typedef evhtp_res (*evhtp_hook_on_body_read)(evhtp_connection_t *, const char *, size_t, void *);
typedef evhtp_res (*evhtp_hook_complete)(evhtp_connection_t *, evhtp_request_t *, void *);
typedef int (*evhtp_headers_iter_cb)(evhtp_header_t * header, void * arg);
struct evhtp_request;
struct evhtp_headers;
struct evhtp_header;
struct evhtp_cfg {
char * base_uri;
char * bind_addr;
uint16_t bind_port;
};
struct evhtp {
evhtp_cfg * config;
evbase_t * evbase;
evserv_t * serv;
void * arg;
evhtp_pre_accept pre_accept_cb;
evhtp_post_accept post_accept_cb;
http_parser_settings psets;
};
struct evhtp_connection {
evhtp_conn_hooks_t * hooks;
evhtp_request_t * request;
http_parser * parser;
evbev_t * bev;
evhtp_t * evhtp;
void * arg;
};
struct evhtp_header {
char * key;
char * val;
TAILQ_ENTRY(evhtp_header) next;
};
TAILQ_HEAD(evhtp_headers, evhtp_header);
struct evhtp_request {
evhtp_headers_t headers;
evhtp_method method;
evhtp_ver_major major;
evhtp_ver_minor minor;
char * uri;
};
enum evhtp_hook_type {
EVHTP_HOOK_POST_HEADERS = 1,
EVHTP_HOOK_SINGLE_HEADER,
EVHTP_HOOK_ON_PATH,
EVHTP_HOOK_ON_BODY_READ,
EVHTP_HOOK_COMPLETE
};
enum evhtp_res {
EVHTP_RES_OK = 0,
EVHTP_RES_DISCONNECT,
EVHTP_RES_CORRUPTED,
EVHTP_RES_ERROR,
EVHTP_RES_CONTINUE
};
struct evhtp_conn_hooks {
evhtp_hook_post_headers post_headers_cb;
evhtp_hook_single_header single_header_cb;
evhtp_hook_on_path path_cb;
evhtp_hook_on_body_read body_read_cb;
evhtp_hook_complete complete_cb;
};
evhtp_t * evhtp_new(evbase_t *, evhtp_cfg *, void *);
evhtp_request_t * evhtp_request_new(void);
int evhtp_set_post_accept_cb(evhtp_t *, evhtp_post_accept);
int evhtp_set_pre_accept_cb(evhtp_t *, evhtp_pre_accept);
int evhtp_conn_hook(evhtp_connection_t *, evhtp_hook_type, void * cb);
int evhtp_headers_for_each(evhtp_headers_t *, evhtp_headers_iter_cb, void *);
void evhtp_connection_free(evhtp_connection_t *);
#endif /* __EVHTP_H__ */

1
http_parser Submodule

@ -0,0 +1 @@
Subproject commit 9639c7c21c868cc743f5cce9ec1cffc1cbcafe01

76
test.c Normal file
View file

@ -0,0 +1,76 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <evhtp.h>
static int
dump_hdrs_cb(evhtp_header_t * header, void * arg) {
printf("key = '%s', val = '%s'\n", header->key, header->val);
return 0;
}
static evhtp_res
test_post_headers_cb(evhtp_connection_t * conn, evhtp_headers_t * hdrs, void * arg) {
printf("test_post_headers_cb()\n");
evhtp_headers_for_each(hdrs, dump_hdrs_cb, NULL);
return EVHTP_RES_OK;
}
static evhtp_res
test_single_header_cb(evhtp_connection_t * conn, evhtp_header_t * hdr, void * arg) {
printf("test_single_header_cb()\n");
printf("key = '%s', val='%s'\n", hdr->key, hdr->val);
return EVHTP_RES_OK;
}
static evhtp_res
test_on_path_cb(evhtp_connection_t * conn, const char * uri) {
printf("test_on_path_cb()\n");
printf("uri = '%s'\n", uri);
return EVHTP_RES_OK;
}
static evhtp_res
test_complete_cb(evhtp_connection_t * conn, evhtp_request_t * req, void * arg) {
printf("test_complete_cb()\n");
printf("method: %d\n", req->method);
printf("uri: %s\n", req->uri);
printf("vmajor: %d\n", req->major);
printf("vminor: %d\n", req->minor);
return EVHTP_RES_OK;
}
static evhtp_res
test_post_accept_cb(evhtp_connection_t * conn, void * arg) {
printf("test_post_accept_cb()\n");
evhtp_conn_hook(conn, EVHTP_HOOK_SINGLE_HEADER, test_single_header_cb);
evhtp_conn_hook(conn, EVHTP_HOOK_POST_HEADERS, test_post_headers_cb);
evhtp_conn_hook(conn, EVHTP_HOOK_ON_PATH, test_on_path_cb);
evhtp_conn_hook(conn, EVHTP_HOOK_COMPLETE, test_complete_cb);
return EVHTP_RES_OK;
}
int
main(int argc, char ** argv) {
int res = 0;
evbase_t * evbase = NULL;
evhtp_t * htp = NULL;
evhtp_cfg cfg = {
.base_uri = "/",
.bind_addr = "0.0.0.0",
.bind_port = 8080
};
evbase = event_base_new();
htp = evhtp_new(evbase, &cfg, NULL);
res = evhtp_set_post_accept_cb(htp, test_post_accept_cb);
event_base_loop(evbase, 0);
return 0;
}