blob: 9813133bad227c169c568429016e858483329bef [file] [log] [blame]
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>
#include <limits.h>
#include <errno.h>
#include <signal.h>
#include <sys/ioctl.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include "http_parser.h"
#include "onigposix.h"
#include "evhtp.h"
typedef struct evhtp_callback evhtp_callback_t;
typedef struct evhtp_callbacks evhtp_callbacks_t;
typedef void (*htp_conn_write_fini_cb)(evhtp_conn_t * conn, void * args);
#ifdef DISABLE_EVTHR
#define pthread_self() 0
#define pthread_mutex_lock(a) 0
#define pthread_mutex_unlock(a) 0
#define pthread_mutex_init(a, b) 0
#define evthr_pool_new(a, b) NULL
#define evthr_pool_start(a) 0
#define evthr_pool_defer(a, b, c) 0
#define evthr_get_base(a) 0
#define evthr_inc_backlog(a) 0
#define evthr_dec_backlog(a) 0
#endif
#ifdef DISABLE_SSL
#define CRYPTO_set_id_callback(a) 0
#define CRYPTO_set_locking_callback(a) 0
#define CRYPTO_num_locks() 0
#define CRYPTO_LOCK 0
#endif
struct evhtp {
evbase_t * evbase;
evserv_t * listener;
evhtp_callbacks_t * callbacks;
void * default_cbarg;
void * post_accept_cbarg;
char * server_name;
evhtp_callback_cb default_cb;
evhtp_post_accept post_accept_cb;
http_parser_settings psets;
evhtp_ssl_ctx_t * ssl_ctx;
evhtp_ssl_cfg * ssl_cfg;
evthr_pool_t * pool;
};
struct evhtp_hooks {
evhtp_hook_hdr _hdr;
evhtp_hook_hdrs _hdrs;
evhtp_hook_path _path;
evhtp_hook_uri _uri;
evhtp_hook_read _read;
evhtp_hook_on_expect _on_expect;
void * _hdr_cbargs;
void * _hdrs_cbargs;
void * _path_cbargs;
void * _uri_cbargs;
void * _read_cbargs;
void * _on_expect_cbargs;
};
struct evhtp_request {
char * path;
char * uri;
int matched_soff;
int matched_eoff;
int keepalive;
evhtp_hdrs_t headers_in;
evhtp_hdrs_t headers_out;
evhtp_method method;
evhtp_proto proto;
char major;
char minor;
char chunked;
evhtp_callback_cb cb;
evhtp_stream_cb stream_cb;
void * cbarg;
void * stream_cbarg;
evhtp_conn_t * conn;
evbuf_t * buffer_in;
evbuf_t * buffer_out;
};
typedef enum {
callback_type_uri,
callback_type_regex,
} callback_type_t;
struct evhtp_callback {
callback_type_t type;
void * cbarg;
unsigned int hash;
evhtp_callback_cb cb;
evhtp_callback_t * next;
union {
char * uri;
regex_t * regex;
} val;
};
struct evhtp_callbacks {
evhtp_callback_t ** callbacks;
evhtp_callback_t * regex_callbacks;
unsigned int count;
unsigned int buckets;
};
struct evhtp_conn {
evhtp_t * htp;
evhtp_hooks_t * hooks;
evhtp_request_t * request;
http_parser * parser;
int sock;
evhtp_res err;
evhtp_cflags flags;
evbase_t * evbase;
evbev_t * bev;
evhtp_ssl_t * ssl;
evthr_t * thr;
};
#define _HTP_CONN "Connection"
#define _HTP_CONTLEN "Content-Length"
#define _HTP_CONTYPE "Content-Type"
#define _HTP_EXPECT "Expect"
#define _HTP_SERVER "Server"
#define _HTP_TRANSENC "Transfer-Encoding"
#define _HTP_DEFCLOSE "close"
#define _HTP_DEFKALIVE "keep-alive"
#define _HTP_DEFCONTYPE "text/plain"
#define _HTP_DEFSERVER "libevht"
#define _HTP_DEFCHUNKED "chunked"
#ifdef HTP_DEBUG
#define __QUOTE(x) # x
#define _QUOTE(x) __QUOTE(x)
#define evhtp_log_debug(fmt, ...) do { \
fprintf(stdout, __FILE__ "[" _QUOTE(__LINE__) "] %s: " \
fmt "\n", __func__, ## __VA_ARGS__); \
fflush(stdout); \
} while (0)
#else
#define evhtp_log_debug(fmt, ...) do {} while (0)
#endif
#define htp_conn_hook(c) (c)->hooks
#define htp_conn_has_hook(c, n) (htp_conn_hook(c) && htp_conn_hook(c)->n)
#define htp_conn_hook_cbarg(c, n) htp_conn_hook(c)->n ## _cbargs
#define htp_conn_hook_call(c, n, ...) htp_conn_hook(c)->n(c->request, __VA_ARGS__, htp_conn_hook_cbarg(c, n))
#define htp_conn_hook_set(c, n, f, a) do { \
htp_conn_hook(c)->n = f; \
htp_conn_hook_cbarg(c, n) = a; \
} while (0)
#define CRLF "\r\n"
static evhtp_proto htp_proto(char major, char minor);
static evhtp_callback_t * htp_callbacks_find_callback(evhtp_callbacks_t *, const char *);
static evhtp_callback_t * htp_callbacks_find_callback_woffsets(evhtp_callbacks_t *, const char *, int *, int *);
static void htp_recv_cb(evbev_t * bev, void * arg);
static void htp_err_cb(evbev_t * bev, short events, void * arg);
static evhtp_request_t * htp_request_new(evhtp_conn_t * conn);
static int ssl_num_locks;
static evhtp_mutex_t ** ssl_locks;
static evhtp_res
htp_run_on_expect_hook(evhtp_conn_t * conn, const char * expt_val) {
evhtp_res status = EVHTP_RES_CONTINUE;
evhtp_log_debug("enter");
if (htp_conn_has_hook(conn, _on_expect)) {
status = htp_conn_hook_call(conn, _on_expect, expt_val);
}
return status;
}
static evhtp_res
htp_run_hdr_hook(evhtp_conn_t * conn, evhtp_hdr_t * hdr) {
evhtp_res res = EVHTP_RES_OK;
evhtp_log_debug("enter");
if (htp_conn_has_hook(conn, _hdr)) {
res = htp_conn_hook_call(conn, _hdr, hdr);
}
return res;
}
static evhtp_res
htp_run_hdrs_hook(evhtp_conn_t * conn, evhtp_hdrs_t * hdrs) {
evhtp_res res = EVHTP_RES_OK;
evhtp_log_debug("enter");
if (htp_conn_has_hook(conn, _hdrs)) {
res = htp_conn_hook_call(conn, _hdrs, hdrs);
}
return res;
}
static evhtp_res
htp_run_path_hook(evhtp_conn_t * conn, const char * path) {
evhtp_res res = EVHTP_RES_OK;
evhtp_log_debug("enter");
if (htp_conn_has_hook(conn, _path)) {
res = htp_conn_hook_call(conn, _path, path);
}
return res;
}
static evhtp_res
htp_run_uri_hook(evhtp_conn_t * conn, const char * uri) {
evhtp_res res = EVHTP_RES_OK;
evhtp_log_debug("enter");
if (htp_conn_has_hook(conn, _uri)) {
res = htp_conn_hook_call(conn, _uri, uri);
}
return res;
}
static evhtp_res
htp_run_read_hook(evhtp_conn_t * conn, const char * data, size_t sz) {
evhtp_res res = EVHTP_RES_OK;
evhtp_log_debug("enter");
if (htp_conn_has_hook(conn, _read)) {
res = htp_conn_hook_call(conn, _read, data, sz);
}
return res;
}
static int
htp_start_cb(http_parser * p) {
evhtp_conn_t * conn = p->data;
evhtp_log_debug("enter");
conn->request = htp_request_new(conn);
return 0;
}
static int
htp_end_cb(http_parser * p) {
evhtp_conn_t * conn = NULL;
evhtp_request_t * request = NULL;
evhtp_log_debug("enter");
conn = p->data;
request = conn->request;
if (request->cb) {
request->cb(request, request->cbarg);
}
return 0;
}
static int
htp_query_str_cb(http_parser * p, const char * buf, size_t len) {
/* evhtp_conn_t * conn = p->data; */
evhtp_log_debug("len = %" PRIoMAX " buf = '%.*s'", len, (int)len, buf);
return 0;
}
static int
htp_uri_cb(http_parser * p, const char * buf, size_t len) {
evhtp_conn_t * conn;
evhtp_request_t * request;
evhtp_log_debug("enter");
conn = p->data;
request = conn->request;
request->uri = malloc(len + 1);
request->uri[len] = '\0';
memcpy(request->uri, buf, len);
if (htp_run_uri_hook(conn, request->uri) != EVHTP_RES_OK) {
conn->err = 1;
return -1;
}
return 0;
}
static int
htp_fragment_cb(http_parser * p, const char * buf, size_t len) {
/* evhtp_conn_t * conn = p->data; */
evhtp_log_debug("len = %" PRIoMAX " buf = '%.*s", len, (int)len, buf);
return 0;
}
static int
htp_header_key_cb(http_parser * p, const char * buf, size_t len) {
evhtp_hdr_t * hdr;
evhtp_conn_t * conn;
evhtp_log_debug("len = %" PRIdMAX, len);
conn = p->data;
hdr = malloc(sizeof(evhtp_hdr_t));
hdr->k_heaped = 1;
hdr->key = malloc(len + 1);
hdr->key[len] = '\0';
memcpy(hdr->key, buf, len);
TAILQ_INSERT_TAIL(&conn->request->headers_in, hdr, next);
return 0;
}
static int
htp_header_val_cb(http_parser * p, const char * buf, size_t len) {
evhtp_hdr_t * hdr = NULL;
evhtp_conn_t * conn = NULL;
evhtp_request_t * req = NULL;
evhtp_log_debug("len = %" PRIdMAX, len);
conn = p->data;
req = conn->request;
hdr = TAILQ_LAST(&req->headers_in, evhtp_hdrs);
hdr->v_heaped = 1;
hdr->val = malloc(len + 1);
hdr->val[len] = '\0';
memcpy(hdr->val, buf, len);
if (htp_run_hdr_hook(conn, hdr) != EVHTP_RES_OK) {
conn->err = 1;
return -1;
}
return 0;
}
static int
htp_headers_complete_cb(http_parser * p) {
evhtp_conn_t * conn;
evhtp_log_debug("enter");
conn = p->data;
conn->request->method = p->method;
conn->request->major = p->http_major;
conn->request->minor = p->http_minor;
conn->request->proto = htp_proto(p->http_major, p->http_minor);
if (htp_run_hdrs_hook(conn, &conn->request->headers_in) != EVHTP_RES_OK) {
conn->err = 1;
return -1;
}
if (evhtp_hdr_find(&conn->request->headers_in, _HTP_CONTLEN)) {
const char * expt_val;
evbuf_t * buf;
evhtp_res status;
if (!(expt_val = evhtp_hdr_find(&conn->request->headers_in, _HTP_EXPECT))) {
return 0;
}
if ((status = htp_run_on_expect_hook(conn, expt_val)) != EVHTP_RES_CONTINUE) {
conn->err = 1;
evhtp_send_reply(conn->request, status, "no", NULL);
return -1;
}
buf = evbuffer_new();
evbuffer_add_printf(buf, "HTTP/%d.%d 100 Continue\r\n\r\n", p->http_major, p->http_minor);
evbuffer_write(buf, conn->sock);
evbuffer_free(buf);
}
return 0;
}
static int
htp_path_cb(http_parser * p, const char * buf, size_t len) {
evhtp_conn_t * conn = NULL;
evhtp_request_t * request = NULL;
evhtp_callback_t * cb = NULL;
evhtp_log_debug("enter");
conn = p->data;
request = conn->request;
request->path = malloc(len + 1);
request->path[len] = '\0';
memcpy(request->path, buf, len);
cb = htp_callbacks_find_callback_woffsets(conn->htp->callbacks,
request->path,
&request->matched_soff,
&request->matched_eoff);
if (cb == NULL) {
if (conn->htp->default_cb == NULL) {
evhtp_send_reply(request, EVHTP_RES_400, "NOT FOUND", NULL);
return -1;
}
request->cb = conn->htp->default_cb;
request->cbarg = conn->htp->default_cbarg;
} else {
request->cb = cb->cb;
request->cbarg = cb->cbarg;
}
if (htp_run_path_hook(conn, conn->request->path) != EVHTP_RES_OK) {
conn->err = 1;
return -1;
}
return 0;
}
static int
htp_body_cb(http_parser * p, const char * buf, size_t len) {
evhtp_conn_t * conn = p->data;
evhtp_log_debug("enter");
evbuffer_add(evhtp_request_get_input(conn->request), buf, len);
if (htp_run_read_hook(conn, buf, len) != EVHTP_RES_OK) {
conn->err = 1;
return -1;
}
return 0;
}
static inline unsigned int
htp_thash(const char * key) {
unsigned int h = 0;
for (; *key; key++) {
h = 31 * h + *key;
}
return h;
}
static evhtp_callback_t *
htp_callback_new(const void * uri, callback_type_t type, evhtp_callback_cb cb, void * cbarg) {
evhtp_callback_t * htp_cb;
evhtp_log_debug("enter");
if (!(htp_cb = calloc(sizeof(evhtp_callback_t), sizeof(char)))) {
return NULL;
}
htp_cb->type = type;
switch (type) {
case callback_type_uri:
htp_cb->hash = htp_thash(uri);
htp_cb->val.uri = strdup((const char *)uri);
break;
case callback_type_regex:
htp_cb->val.regex = (regex_t *)malloc(sizeof(regex_t));
if (regcomp(htp_cb->val.regex, (char *)uri, REG_EXTENDED) != 0) {
free(htp_cb->val.regex);
free(htp_cb);
return NULL;
}
break;
}
htp_cb->cb = cb;
htp_cb->cbarg = cbarg;
return htp_cb;
}
static evhtp_callbacks_t *
htp_callbacks_new(unsigned int buckets) {
evhtp_callbacks_t * htp_cbs;
evhtp_log_debug("enter");
if (!(htp_cbs = calloc(sizeof(evhtp_callbacks_t), sizeof(char)))) {
return NULL;
}
if (!(htp_cbs->callbacks = calloc(sizeof(evhtp_callback_t *), buckets))) {
free(htp_cbs);
return NULL;
}
htp_cbs->count = 0;
htp_cbs->buckets = buckets;
return htp_cbs;
}
static evhtp_callback_t *
htp_callbacks_find_callback_woffsets(evhtp_callbacks_t * cbs,
const char * uri,
int * start_offset,
int * end_offset) {
evhtp_callback_t * cb;
unsigned int hash;
if (cbs == NULL) {
return NULL;
}
hash = htp_thash(uri);
cb = cbs->callbacks[hash & (cbs->buckets - 1)];
while (cb != NULL) {
if (cb->hash == hash && !strcmp(cb->val.uri, uri)) {
*start_offset = 0;
*end_offset = strlen(uri);
return cb;
}
cb = cb->next;
}
/* check regex patterns */
cb = cbs->regex_callbacks;
while (cb != NULL) {
regmatch_t pmatch[20];
if (regexec(cb->val.regex, uri, cb->val.regex->re_nsub + 1, pmatch, 0) == 0) {
*start_offset = (int)pmatch[0].rm_so;
*end_offset = (int)pmatch[0].rm_eo;
return cb;
}
cb = cb->next;
}
return NULL;
}
static evhtp_callback_t *
htp_callbacks_find_callback(evhtp_callbacks_t * cbs, const char * uri) {
evhtp_callback_t * cb;
unsigned int hash;
evhtp_log_debug("enter");
if (cbs == NULL) {
return NULL;
}
hash = htp_thash(uri);
cb = cbs->callbacks[hash & (cbs->buckets - 1)];
while (cb != NULL) {
if (cb->hash == hash && !strcmp(cb->val.uri, uri)) {
return cb;
}
cb = cb->next;
}
/* check regex patterns */
cb = cbs->regex_callbacks;
while (cb != NULL) {
if (regexec(cb->val.regex, uri, 0, NULL, 0) == 0) {
return cb;
}
cb = cb->next;
}
return NULL;
}
static int
htp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb) {
unsigned int hkey;
evhtp_log_debug("enter");
switch (cb->type) {
case callback_type_uri:
hkey = cb->hash & (cbs->buckets - 1);
if (cbs->callbacks[hkey] == NULL) {
cbs->callbacks[hkey] = cb;
return 0;
}
cb->next = cbs->callbacks[hkey];
cbs->callbacks[hkey] = cb;
break;
case callback_type_regex:
cb->next = cbs->regex_callbacks;
cbs->regex_callbacks = cb;
break;
}
return 0;
}
void
htp_conn_free(evhtp_conn_t * conn) {
if (conn == NULL) {
return;
}
evhtp_log_debug("enter");
if (conn->hooks) {
free(conn->hooks);
}
if (conn->parser) {
free(conn->parser);
}
if (conn->request) {
evhtp_request_free(conn->request);
}
if (conn->thr) {
evthr_dec_backlog(conn->thr);
}
if (conn->bev) {
bufferevent_free(conn->bev);
}
free(conn);
} /* htp_conn_free */
static evhtp_conn_t *
htp_conn_new(evhtp_t * htp) {
evhtp_conn_t * conn;
evhtp_log_debug("enter");
if (!(conn = malloc(sizeof(evhtp_conn_t)))) {
return NULL;
}
conn->htp = htp;
conn->flags = 0;
conn->hooks = NULL;
conn->request = NULL;
conn->sock = 0;
conn->flags = 0;
conn->evbase = NULL;
conn->bev = NULL;
conn->ssl = NULL;
conn->thr = NULL;
conn->parser = malloc(sizeof(http_parser));
conn->parser->data = conn;
http_parser_init(conn->parser, HTTP_REQUEST);
return conn;
}
static void
htp_conn_reset(evhtp_conn_t * conn) {
http_parser_init(conn->parser, HTTP_REQUEST);
evhtp_log_debug("enter");
evhtp_request_free(conn->request);
conn->request = NULL;
bufferevent_disable(conn->bev, EV_WRITE);
bufferevent_enable(conn->bev, EV_READ);
bufferevent_setwatermark(conn->bev, EV_READ | EV_WRITE, 0, 0);
bufferevent_setcb(conn->bev, htp_recv_cb, NULL, htp_err_cb, conn);
}
static int
htp_conn_get_sock(evhtp_conn_t * conn) {
if (conn == NULL) {
return -1;
}
evhtp_log_debug("enter");
return conn->sock;
}
static evserv_t *
htp_conn_get_listener(evhtp_conn_t * conn) {
if (conn == NULL) {
return NULL;
}
evhtp_log_debug("enter");
return evhtp_get_listener(conn->htp);
}
static evbase_t *
htp_conn_get_evbase(evhtp_conn_t * conn) {
if (conn == NULL) {
return NULL;
}
evhtp_log_debug("enter");
return conn->evbase;
}
static int
htp_resp_can_have_content(evhtp_res code) {
evhtp_log_debug("enter");
if (code >= 100) {
if (code < 300) {
return 1;
}
return 0;
}
return 0;
}
static void
htp_recv_cb(evbev_t * bev, void * arg) {
evbuf_t * ibuf;
evhtp_conn_t * conn;
void * read_buf;
size_t nread;
size_t avail;
evhtp_log_debug("enter");
conn = (evhtp_conn_t *)arg;
ibuf = bufferevent_get_input(bev);
avail = evbuffer_get_length(ibuf);
read_buf = evbuffer_pullup(ibuf, avail);
nread = http_parser_execute(conn->parser, &conn->htp->psets, (char *)read_buf, avail);
if (conn->err != 0) {
return htp_conn_free(conn);
}
evhtp_log_debug("nread = %zu", nread);
if (nread <= evbuffer_get_length(ibuf)) {
evbuffer_drain(ibuf, nread);
} else {
evbuffer_drain(ibuf, -1);
}
}
static void
htp_err_cb(evbev_t * bev, short events, void * arg) {
evhtp_conn_t * conn;
evhtp_log_debug("events = %x", events);
if (events & (BEV_EVENT_ERROR | BEV_EVENT_EOF)) {
conn = (evhtp_conn_t *)arg;
evhtp_log_debug("leaving....");
return htp_conn_free(conn);
}
evhtp_log_debug("leaving....");
}
static int
htp_hdr_output(evhtp_hdr_t * hdr, void * arg) {
evbuf_t * buf = (evbuf_t *)arg;
evhtp_log_debug("enter");
evbuffer_add(buf, hdr->key, strlen(hdr->key));
evbuffer_add(buf, ": ", 2);
evbuffer_add(buf, hdr->val, strlen(hdr->val));
evbuffer_add(buf, CRLF, 2);
return 0;
}
static void
htp_exec_in_thr(evthr_t * thr, void * arg, void * shared) {
evhtp_t * htp;
evhtp_conn_t * conn;
evhtp_log_debug("enter");
htp = (evhtp_t *)shared;
conn = (evhtp_conn_t *)arg;
conn->evbase = evthr_get_base(thr);
conn->thr = thr;
if (htp->ssl_ctx == NULL) {
conn->bev = bufferevent_socket_new(conn->evbase, conn->sock, BEV_OPT_CLOSE_ON_FREE);
} else {
#ifndef DISABLE_SSL
conn->ssl = SSL_new(htp->ssl_ctx);
conn->bev = bufferevent_openssl_socket_new(conn->evbase,
conn->sock, conn->ssl, BUFFEREVENT_SSL_ACCEPTING,
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
SSL_set_app_data(conn->ssl, conn);
#endif
}
bufferevent_setcb(conn->bev, htp_recv_cb, NULL, htp_err_cb, conn);
bufferevent_enable(conn->bev, EV_READ);
bufferevent_disable(conn->bev, EV_WRITE);
if (htp->post_accept_cb) {
htp->post_accept_cb(conn, htp->post_accept_cbarg);
}
evthr_inc_backlog(conn->thr);
}
static void
htp_accept_cb(evserv_t * serv, int fd, struct sockaddr * s, int sl, void * arg) {
evhtp_t * htp;
evhtp_conn_t * conn;
evhtp_log_debug("enter");
htp = (evhtp_t *)arg;
conn = htp_conn_new(htp);
conn->evbase = htp->evbase;
conn->sock = fd;
if (htp->post_accept_cb) {
htp->post_accept_cb(conn, htp->post_accept_cbarg);
}
if (htp->pool != NULL) {
evthr_pool_defer(htp->pool, htp_exec_in_thr, conn);
return;
}
if (htp->ssl_ctx == NULL) {
conn->bev = bufferevent_socket_new(conn->evbase, conn->sock, BEV_OPT_CLOSE_ON_FREE);
} else {
#ifndef DISABLE_SSL
conn->ssl = SSL_new(htp->ssl_ctx);
conn->bev = bufferevent_openssl_socket_new(conn->evbase,
conn->sock, conn->ssl, BUFFEREVENT_SSL_ACCEPTING,
BEV_OPT_CLOSE_ON_FREE | BEV_OPT_DEFER_CALLBACKS);
SSL_set_app_data(conn->ssl, conn);
#else
abort();
#endif
}
bufferevent_disable(conn->bev, EV_WRITE);
bufferevent_enable(conn->bev, EV_READ);
bufferevent_setcb(conn->bev, htp_recv_cb, NULL, htp_err_cb, conn);
if (htp->post_accept_cb) {
htp->post_accept_cb(conn, htp->post_accept_cbarg);
}
} /* htp_accept_cb */
static void
htp_set_kalive_hdr(evhtp_hdrs_t * hdrs, evhtp_proto proto, int kalive) {
evhtp_log_debug("enter");
if (hdrs == NULL) {
return;
}
if (kalive && proto == EVHTP_PROTO_1_0) {
return evhtp_hdr_add(hdrs, evhtp_hdr_new(_HTP_CONN, _HTP_DEFKALIVE));
}
if (!kalive && proto == EVHTP_PROTO_1_1) {
return evhtp_hdr_add(hdrs, evhtp_hdr_new(_HTP_CONN, _HTP_DEFCLOSE));
}
}
static void
htp_reply_set_content_hdrs(evhtp_request_t * req, size_t len) {
const char * content_len_hval;
const char * content_type_hval;
evhtp_log_debug("enter");
if (req == NULL) {
return;
}
if (len == 0) {
evhtp_hdr_add(&req->headers_out, evhtp_hdr_new(_HTP_CONTLEN, "0"));
return;
}
content_len_hval = evhtp_hdr_find(&req->headers_out, _HTP_CONTLEN);
content_type_hval = evhtp_hdr_find(&req->headers_out, _HTP_CONTYPE);
if (content_len_hval == NULL) {
evhtp_hdr_t * hdr;
#if __WORDSIZE == 64
char lstr[22];
#else
char lstr[12];
#endif
snprintf(lstr, sizeof(lstr), "%" PRIuMAX, len);
hdr = evhtp_hdr_new(_HTP_CONTLEN, strdup(lstr));
hdr->v_heaped = 1;
evhtp_hdr_add(&req->headers_out, hdr);
}
if (content_type_hval == NULL) {
evhtp_hdr_add(&req->headers_out, evhtp_hdr_new(_HTP_CONTYPE, _HTP_DEFCONTYPE));
}
} /* htp_reply_set_content_hdrs */
static evhtp_res
htp_code_parent(evhtp_res code) {
evhtp_log_debug("enter");
if (code > 599 || code < 100) {
return EVHTP_RES_SCREWEDUP;
}
if (code >= 100 && code < 200) {
return EVHTP_RES_100;
}
if (code >= 200 && code < 300) {
return EVHTP_RES_200;
}
if (code >= 300 && code < 400) {
return EVHTP_RES_300;
}
if (code >= 400 && code < 500) {
return EVHTP_RES_400;
}
return EVHTP_RES_500;
}
static int
htp_should_close_based_on_cflags(evhtp_cflags flags, evhtp_res code) {
int res = 0;
evhtp_log_debug("enter");
switch (htp_code_parent(code)) {
case EVHTP_RES_100:
res = (flags & EVHTP_CLOSE_ON_100);
break;
case EVHTP_RES_200:
res = (flags & EVHTP_CLOSE_ON_200);
break;
case EVHTP_RES_300:
res = (flags & EVHTP_CLOSE_ON_300);
break;
case EVHTP_RES_400:
if (code == EVHTP_RES_EXPECTFAIL && flags & EVHTP_CLOSE_ON_EXPECT_ERR) {
res = 1;
} else {
res = (flags & EVHTP_CLOSE_ON_400);
}
break;
case EVHTP_RES_500:
res = (flags & EVHTP_CLOSE_ON_500);
break;
case EVHTP_RES_SCREWEDUP:
default:
res = 1;
break;
} /* switch */
return res ? 1 : 0;
}
static int
htp_should_keep_alive(evhtp_request_t * req, evhtp_res code) {
evhtp_conn_t * conn = req->conn;
evhtp_log_debug("enter");
if (http_should_keep_alive(conn->parser) == 0) {
/* parsed request doesn't even support keep-alive */
return 0;
}
if (htp_should_close_based_on_cflags(conn->flags, code)) {
/* one of the user-set flags has informed us to close, thus
* do not keep alive */
return 0;
}
/* all above actions taken into account, the client is
* set to keep-alive */
return 1;
}
static inline int
htp_is_http_1_1x(char major, char minor) {
evhtp_log_debug("enter");
if (major >= 1 && minor >= 1) {
return 1;
}
return 0;
}
static inline int
htp_is_http_1_0x(char major, char minor) {
if (major >= 1 && minor <= 0) {
return 1;
}
return 0;
}
static evhtp_proto
htp_proto(char major, char minor) {
if (htp_is_http_1_0x(major, minor)) {
return EVHTP_PROTO_1_0;
}
if (htp_is_http_1_1x(major, minor)) {
return EVHTP_PROTO_1_1;
}
return EVHTP_PROTO_INVALID;
}
#define htp_set_status_buf(buf, major, minor, code) do { \
evbuffer_add_printf(buf, "HTTP/%d.%d %d DERP\r\n", major, minor, code); \
} while (0)
#define htp_set_header_buf(buf, hdrs) do { \
evhtp_hdrs_for_each(hdrs, htp_hdr_output, buf); \
} while (0)
#define htp_set_server_hdr(hdrs, name) do { \
evhtp_hdr_add(hdrs, evhtp_hdr_new(_HTP_SERVER, name)); \
} while (0)
#define htp_set_crlf_buf(buf) do { \
evbuffer_add_reference(buf, CRLF, 2, NULL, NULL); \
} while (0)
void
htp_set_body_buf(evbuf_t * dst, evbuf_t * src) {
if (dst == NULL) {
return;
}
evhtp_log_debug("enter");
if (src && evbuffer_get_length(src)) {
evbuffer_add_buffer(dst, src);
}
}
static void
htp_resp_fini_cb(evbev_t * bev, void * arg) {
evhtp_request_t * req;
evhtp_conn_t * conn;
int keepalive;
evhtp_log_debug("enter");
req = (evhtp_request_t *)arg;
keepalive = req->keepalive;
conn = req->conn;
if (keepalive) {
return htp_conn_reset(conn);
} else {
return htp_conn_free(conn);
}
}
static void
htp_resp_err_cb(evbev_t * bev, short events, void * arg) {
evhtp_request_t * req;
evhtp_conn_t * conn;
evhtp_log_debug("events = %x", events);
req = (evhtp_request_t *)arg;
conn = req->conn;
return htp_conn_free(conn);
}
static void
htp_stream_fini_cb(evbev_t * bev, void * arg) {
evhtp_request_t * req;
evhtp_conn_t * conn;
evbuf_t * buf;
evhtp_log_debug("enter");
req = (evhtp_request_t *)arg;
conn = req->conn;
buf = evhtp_request_get_output(req);
switch (req->stream_cb(req, req->stream_cbarg)) {
case EVHTP_RES_OK:
bufferevent_write_buffer(conn->bev, buf);
return;
case EVHTP_RES_DONE:
if (req->chunked) {
evbuffer_add_reference(buf, "0\r\n\r\n", 5, NULL, NULL);
bufferevent_setcb(conn->bev, NULL, htp_resp_fini_cb, htp_resp_err_cb, req);
bufferevent_write_buffer(conn->bev, buf);
return;
}
break;
default:
req->keepalive = 0;
break;
}
return htp_resp_fini_cb(conn->bev, arg);
}
void
evhtp_send_reply(evhtp_request_t * req, evhtp_res code, const char * r, evbuf_t * b) {
evhtp_conn_t * conn;
evbuf_t * obuf;
evhtp_log_debug("enter");
conn = req->conn;
obuf = evhtp_request_get_output(req);
req->keepalive = htp_should_keep_alive(req, code);
assert(obuf != NULL);
if (htp_resp_can_have_content(code)) {
htp_reply_set_content_hdrs(req, b ? evbuffer_get_length(b) : 0);
} else {
if ((b != NULL) && evbuffer_get_length(b) > 0) {
evbuffer_drain(b, -1);
}
}
htp_set_kalive_hdr(&req->headers_out, req->proto, req->keepalive);
htp_set_server_hdr(&req->headers_out, evhtp_get_server_name(conn->htp));
htp_set_status_buf(obuf, req->major, req->minor, code);
htp_set_header_buf(obuf, &req->headers_out);
htp_set_crlf_buf(obuf);
htp_set_body_buf(obuf, b);
bufferevent_disable(conn->bev, EV_READ);
bufferevent_enable(conn->bev, EV_WRITE);
bufferevent_setwatermark(conn->bev, EV_WRITE, 1, 0);
bufferevent_setcb(conn->bev, NULL, htp_resp_fini_cb, htp_resp_err_cb, req);
bufferevent_write_buffer(conn->bev, obuf);
} /* evhtp_send_reply */
void
evhtp_send_reply_stream(evhtp_request_t * req, evhtp_res code, evhtp_stream_cb cb, void * arg) {
evhtp_conn_t * conn;
evbuf_t * obuf;
evhtp_log_debug("enter");
conn = req->conn;
obuf = evhtp_request_get_output(req);
assert(obuf != NULL);
if (req->proto == EVHTP_PROTO_1_1) {
req->keepalive = htp_should_keep_alive(req, code);
if (!evhtp_hdr_find(&req->headers_out, _HTP_TRANSENC)) {
evhtp_hdr_add(&req->headers_out, evhtp_hdr_new(_HTP_TRANSENC, _HTP_DEFCHUNKED));
}
req->chunked = 1;
} else {
req->keepalive = 0;
}
if (!evhtp_hdr_find(&req->headers_out, _HTP_CONTYPE)) {
evhtp_hdr_add(&req->headers_out, evhtp_hdr_new(_HTP_CONTYPE, _HTP_DEFCONTYPE));
}
htp_set_kalive_hdr(&req->headers_out, req->proto, req->keepalive);
htp_set_server_hdr(&req->headers_out, evhtp_get_server_name(conn->htp));
htp_set_status_buf(obuf, req->major, req->minor, code);
htp_set_header_buf(obuf, &req->headers_out);
htp_set_crlf_buf(obuf);
req->stream_cb = cb;
req->stream_cbarg = arg;
bufferevent_disable(conn->bev, EV_READ);
bufferevent_enable(conn->bev, EV_WRITE);
bufferevent_setwatermark(conn->bev, EV_WRITE, 1, 0);
bufferevent_setcb(conn->bev, NULL, htp_stream_fini_cb, htp_resp_err_cb, req);
bufferevent_write_buffer(conn->bev, obuf);
} /* evhtp_send_reply_stream */
void
evhtp_request_make_chunk(evhtp_request_t * req, evbuf_t * buf) {
evbuf_t * obuf = evhtp_request_get_output(req);
evhtp_log_debug("enter");
evbuffer_add_printf(obuf, "%" PRIxMAX "\r\n", evbuffer_get_length(buf));
evbuffer_add_buffer(obuf, buf);
evbuffer_add_reference(obuf, CRLF, 2, NULL, NULL);
}
void
evhtp_send_stream(evhtp_request_t * req, evbuf_t * buf) {
evhtp_log_debug("enter");
switch (req->proto) {
case EVHTP_PROTO_1_1:
return evhtp_request_make_chunk(req, buf);
case EVHTP_PROTO_1_0:
evbuffer_add_buffer(evhtp_request_get_output(req), buf);
req->keepalive = 0;
break;
default:
return htp_conn_free(req->conn);
}
}
int
evhtp_conn_set_flags(evhtp_conn_t * conn, evhtp_cflags flags) {
evhtp_log_debug("enter");
conn->flags |= flags;
return 0;
}
int
evhtp_set_hook(evhtp_conn_t * conn, evhtp_hook_type type, void * cb, void * cbarg) {
evhtp_log_debug("enter");
if (conn->hooks == NULL) {
conn->hooks = calloc(sizeof(evhtp_hooks_t), sizeof(char));
}
switch (type) {
case EVHTP_HOOK_HDRS_READ:
htp_conn_hook_set(conn, _hdrs, cb, cbarg);
break;
case EVHTP_HOOK_HDR_READ:
htp_conn_hook_set(conn, _hdr, cb, cbarg);
break;
case EVHTP_HOOK_PATH_READ:
htp_conn_hook_set(conn, _read, cb, cbarg);
break;
case EVHTP_HOOK_URI_READ:
htp_conn_hook_set(conn, _uri, cb, cbarg);
break;
case EVHTP_HOOK_READ:
htp_conn_hook_set(conn, _read, cb, cbarg);
break;
case EVHTP_HOOK_ON_EXPECT:
htp_conn_hook_set(conn, _on_expect, cb, cbarg);
break;
case EVHTP_HOOK_COMPLETE:
break;
default:
return -1;
} /* switch */
return 0;
}
int
evhtp_set_cb(evhtp_t * htp, const char * uri, evhtp_callback_cb cb, void * cbarg) {
evhtp_callback_t * htp_cb;
evhtp_log_debug("enter");
if (htp->callbacks == NULL) {
htp->callbacks = htp_callbacks_new(1024);
} else {
if (htp_callbacks_find_callback(htp->callbacks, uri)) {
return -1;
}
}
if (!(htp_cb = htp_callback_new(uri, callback_type_uri, cb, cbarg))) {
return -1;
}
if (!htp_callbacks_add_callback(htp->callbacks, htp_cb)) {
return -1;
}
return 0;
}
int
evhtp_set_regex_cb(evhtp_t * htp, const char * pat, evhtp_callback_cb cb, void * arg) {
evhtp_callback_t * htp_cb;
evhtp_log_debug("enter");
if (htp->callbacks == NULL) {
htp->callbacks = htp_callbacks_new(1024);
}
if (!(htp_cb = htp_callback_new(pat, callback_type_regex, cb, arg))) {
return -1;
}
if (!htp_callbacks_add_callback(htp->callbacks, htp_cb)) {
return -1;
}
return 0;
}
void
evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * cbarg) {
evhtp_log_debug("enter");
htp->default_cb = cb;
htp->default_cbarg = cbarg;
}
void
evhtp_bind_socket(evhtp_t * htp, const char * baddr, uint16_t port) {
struct sockaddr_in sin = { 0 };
evhtp_log_debug("enter");
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = inet_addr(baddr);
signal(SIGPIPE, SIG_IGN);
htp->listener = evconnlistener_new_bind(htp->evbase,
htp_accept_cb, htp,
LEV_OPT_CLOSE_ON_FREE | LEV_OPT_REUSEABLE, 1024,
(struct sockaddr *)&sin, sizeof(sin));
}
void
evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept cb, void * cbarg) {
evhtp_log_debug("enter");
htp->post_accept_cb = cb;
htp->post_accept_cbarg = cbarg;
}
const char *
evhtp_hdr_get_key(evhtp_hdr_t * hdr) {
evhtp_log_debug("enter");
return hdr ? hdr->key : NULL;
}
const char *
evhtp_hdr_get_val(evhtp_hdr_t * hdr) {
evhtp_log_debug("enter");
return hdr ? hdr->val : NULL;
}
int
evhtp_hdrs_for_each(evhtp_hdrs_t * hdrs, evhtp_hdrs_iter_cb cb, void * arg) {
evhtp_hdr_t * hdr = NULL;
evhtp_log_debug("enter");
if (hdrs == NULL || cb == NULL) {
return -1;
}
TAILQ_FOREACH(hdr, hdrs, next) {
int res;
if ((res = cb(hdr, arg))) {
return res;
}
}
return 0;
}
void
evhtp_hdr_add(evhtp_hdrs_t * hdrs, evhtp_hdr_t * hdr) {
evhtp_log_debug("enter");
TAILQ_INSERT_TAIL(hdrs, hdr, next);
}
const char *
evhtp_hdr_find(evhtp_hdrs_t * hdrs, const char * key) {
evhtp_hdr_t * hdr = NULL;
evhtp_log_debug("enter");
TAILQ_FOREACH(hdr, hdrs, next) {
if (!strcasecmp(hdr->key, key)) {
return hdr->val;
}
}
return NULL;
}
void
evhtp_request_free(evhtp_request_t * req) {
evhtp_log_debug("enter");
if (req == NULL) {
return;
}
if (req->path) {
free(req->path);
}
if (req->uri) {
free(req->uri);
}
evhtp_hdrs_free(&req->headers_in);
evhtp_hdrs_free(&req->headers_out);
if (req->buffer_in) {
evbuffer_free(req->buffer_in);
}
if (req->buffer_out) {
evbuffer_free(req->buffer_out);
}
free(req);
}
void
evhtp_hdr_free(evhtp_hdr_t * hdr) {
evhtp_log_debug("enter");
if (hdr == NULL) {
return;
}
if (hdr->k_heaped && hdr->key) {
free(hdr->key);
}
if (hdr->v_heaped && hdr->val) {
free(hdr->val);
}
free(hdr);
}
void
evhtp_hdrs_free(evhtp_hdrs_t * hdrs) {
evhtp_hdr_t * hdr;
evhtp_hdr_t * save;
evhtp_log_debug("enter");
if (hdrs == NULL) {
return;
}
hdr = NULL;
for (hdr = TAILQ_FIRST(hdrs); hdr != NULL; hdr = save) {
save = TAILQ_NEXT(hdr, next);
TAILQ_REMOVE(hdrs, hdr, next);
evhtp_hdr_free(hdr);
}
}
int
evhtp_set_server_name(evhtp_t * htp, char * n) {
evhtp_log_debug("enter");
if (htp == NULL || n == NULL) {
return -1;
}
htp->server_name = strdup(n);
return 0;
}
const char *
evhtp_request_get_path(evhtp_request_t * request) {
return (const char *)request->path;
}
const char *
evhtp_request_get_uri(evhtp_request_t * request) {
return (const char *)request->uri;
}
int
evhtp_request_get_matched_soff(evhtp_request_t * request) {
return request->matched_soff;
}
int
evhtp_request_get_matched_eoff(evhtp_request_t * request) {
return request->matched_eoff;
}
evhtp_method
evhtp_request_get_method(evhtp_request_t * request) {
return request->method;
}
evhtp_proto
evhtp_request_get_proto(evhtp_request_t * request) {
return request->proto;
}
evhtp_conn_t *
evhtp_request_get_conn(evhtp_request_t * request) {
return request->conn;
}
evhtp_hdrs_t *
evhtp_request_get_headers_in(evhtp_request_t * request) {
return &request->headers_in;
}
evhtp_hdrs_t *
evhtp_request_get_headers_out(evhtp_request_t * request) {
return &request->headers_out;
}
evbuf_t *
evhtp_request_get_input(evhtp_request_t * request) {
return request->buffer_in;
}
evbuf_t *
evhtp_request_get_output(evhtp_request_t * request) {
return request->buffer_out;
}
evhtp_callback_cb
evhtp_request_get_cb(evhtp_request_t * request) {
return request->cb;
}
void *
evhtp_request_get_cbarg(evhtp_request_t * request) {
return request->cbarg;
}
const char *
evhtp_request_method_str(evhtp_request_t * request) {
return evhtp_method_str(request->method);
}
int64_t
evhtp_request_content_length(evhtp_request_t * request) {
return request->conn->parser->content_length;
}
const char *
evhtp_method_str(evhtp_method method) {
return http_method_str(method);
}
evbase_t *
evhtp_request_get_evbase(evhtp_request_t * request) {
evhtp_log_debug("enter");
if (request == NULL) {
return NULL;
}
return htp_conn_get_evbase(request->conn);
}
int
evhtp_request_get_sock(evhtp_request_t * request) {
evhtp_log_debug("enter");
if (request == NULL) {
return -1;
}
return htp_conn_get_sock(request->conn);
}
evserv_t *
evhtp_request_get_listener(evhtp_request_t * request) {
evhtp_log_debug("enter");
if (request == NULL) {
return NULL;
}
return htp_conn_get_listener(request->conn);
}
evhtp_hdr_t *
evhtp_hdr_new(char * key, char * val) {
evhtp_hdr_t * hdr;
evhtp_log_debug("enter");
hdr = malloc(sizeof(evhtp_hdr_t));
hdr->key = key;
hdr->val = val;
hdr->k_heaped = 0;
hdr->v_heaped = 0;
return hdr;
}
static evhtp_request_t *
htp_request_new(evhtp_conn_t * conn) {
evhtp_request_t * request;
evhtp_log_debug("enter");
if (!(request = calloc(sizeof(evhtp_request_t), sizeof(char)))) {
return NULL;
}
request->conn = conn;
request->buffer_in = evbuffer_new();
request->buffer_out = evbuffer_new();
TAILQ_INIT(&request->headers_out);
TAILQ_INIT(&request->headers_in);
return request;
}
evbase_t *
evhtp_get_evbase(evhtp_t * htp) {
evhtp_log_debug("enter");
return htp ? htp->evbase : NULL;
}
char *
evhtp_get_server_name(evhtp_t * htp) {
evhtp_log_debug("enter");
return htp ? htp->server_name : NULL;
}
evserv_t *
evhtp_get_listener(evhtp_t * htp) {
evhtp_log_debug("enter");
return htp ? htp->listener : NULL;
}
#ifndef DISABLE_SSL
typedef struct htp_scache htp_scache_t;
typedef struct htp_scache_ent htp_scache_ent_t;
static int s_server_session_id_context = 1;
struct htp_scache_ent {
htp_scache_t * scache;
unsigned long hash;
unsigned char * id;
unsigned char * der;
int id_len;
int der_len;
evhtp_ssl_sess_t * sess;
event_t * timeout_ev;
TAILQ_ENTRY(htp_scache_ent) next;
};
TAILQ_HEAD(htp_scache, htp_scache_ent);
evhtp_ssl_cfg *
evhtp_get_ssl_cfg(evhtp_t * htp) {
return htp->ssl_cfg;
}
evhtp_ssl_cfg *
evhtp_conn_get_ssl_cfg(evhtp_conn_t * conn) {
return evhtp_get_ssl_cfg(conn->htp);
}
static void
htp_ssl_scache_builtin_expire(int fd, short what, void * arg) {
htp_scache_ent_t * ent;
htp_scache_t * scache;
printf("expire cache ent\n");
ent = (htp_scache_ent_t *)arg;
scache = ent->scache;
TAILQ_REMOVE(scache, ent, next);
event_free(ent->timeout_ev);
free(ent->id);
free(ent->der);
free(ent->sess);
free(ent);
}
int
evhtp_ssl_scache_builtin_add(evhtp_conn_t * conn, unsigned char * id, int len, evhtp_ssl_sess_t * sess) {
evhtp_ssl_cfg * scfg;
htp_scache_ent_t * cache_ent;
htp_scache_t * scache;
unsigned char * der_ptr;
struct timeval tv;
if (!(scfg = evhtp_conn_get_ssl_cfg(conn))) {
return 0;
}
if (!(scache = (htp_scache_t *)scfg->args)) {
return 0;
}
if (!(cache_ent = calloc(sizeof(htp_scache_ent_t), sizeof(char)))) {
return 0;
}
cache_ent->id_len = len;
cache_ent->der_len = i2d_SSL_SESSION(sess, NULL);
cache_ent->id = malloc(len);
cache_ent->der = malloc(cache_ent->der_len);
cache_ent->scache = scache;
der_ptr = cache_ent->der;
memcpy(cache_ent->id, id, len);
i2d_SSL_SESSION(sess, &der_ptr);
/* set expire timeout event, XXX: abstract the timeout API allowing the API
* to create the proper timeout events instead of the user */
tv.tv_sec = scfg->scache_timeout;
tv.tv_usec = 0;
cache_ent->timeout_ev = evtimer_new(htp_conn_get_evbase(conn),
htp_ssl_scache_builtin_expire, (void *)cache_ent);
evtimer_add(cache_ent->timeout_ev, &tv);
TAILQ_INSERT_TAIL(scache, cache_ent, next);
return 1;
} /* evhtp_ssl_scache_builtin_add */
evhtp_ssl_sess_t *
evhtp_ssl_scache_builtin_get(evhtp_conn_t * conn, unsigned char * id, int len) {
evhtp_ssl_cfg * scfg;
htp_scache_t * scache;
htp_scache_ent_t * ent;
scfg = evhtp_conn_get_ssl_cfg(conn);
scache = (htp_scache_t *)scfg->args;
TAILQ_FOREACH(ent, scache, next) {
if (len == ent->id_len && !memcmp(ent->id, id, len)) {
const unsigned char * p = ent->der;
return d2i_SSL_SESSION(NULL, &p, ent->der_len);
}
}
return NULL;
}
void *
evhtp_ssl_scache_builtin_init(evhtp_t * htp) {
htp_scache_t * scache;
scache = malloc(sizeof(htp_scache_t));
TAILQ_INIT(scache);
return (void *)scache;
}
static int
htp_ssl_add_scache_ent(evhtp_ssl_t * ssl, evhtp_ssl_sess_t * sess) {
evhtp_conn_t * conn;
evhtp_ssl_cfg * scfg;
int slen;
unsigned char * sid;
conn = (evhtp_conn_t *)SSL_get_app_data(ssl);
scfg = evhtp_conn_get_ssl_cfg(conn);
if (!scfg) {
return 0;
}
sid = sess->session_id;
slen = sess->session_id_length;
SSL_set_timeout(sess, scfg->scache_timeout);
if (scfg->scache_add) {
return (scfg->scache_add)(conn, sid, slen, sess);
}
return 0;
}
static evhtp_ssl_sess_t *
htp_ssl_get_scache_ent(evhtp_ssl_t * ssl, unsigned char * sid, int sid_len, int * copy) {
evhtp_conn_t * conn;
evhtp_t * htp;
evhtp_ssl_cfg * scfg;
evhtp_ssl_sess_t * sess;
conn = (evhtp_conn_t *)SSL_get_app_data(ssl);
htp = conn->htp;
scfg = htp->ssl_cfg;
sess = NULL;
if (scfg->scache_get) {
sess = (scfg->scache_get)(conn, sid, sid_len);
}
*copy = 0;
return sess;
}
static void
htp_ssl_del_scache_ent(evhtp_ssl_ctx_t * ctx, evhtp_ssl_sess_t * sess) {
evhtp_t * htp;
evhtp_ssl_cfg * scfg;
unsigned char * sid;
unsigned int slen;
htp = (evhtp_t *)SSL_CTX_get_app_data(ctx);
scfg = htp->ssl_cfg;
sid = sess->session_id;
slen = sess->session_id_length;
if (scfg->scache_del) {
scfg->scache_del(htp, sid, slen);
}
}
int
evhtp_use_ssl(evhtp_t * htp, evhtp_ssl_cfg * cfg) {
long cache_mode;
if (!cfg || !htp || !cfg->pemfile) {
return -1;
}
SSL_load_error_strings();
SSL_library_init();
RAND_status();
htp->ssl_cfg = cfg;
htp->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
SSL_CTX_set_options(htp->ssl_ctx, cfg->ssl_opts);
if (cfg->ciphers) {
SSL_CTX_set_cipher_list(htp->ssl_ctx, cfg->ciphers);
}
if (cfg->cafile) {
SSL_CTX_load_verify_locations(htp->ssl_ctx, cfg->cafile, NULL);
}
if (cfg->enable_scache) {
cache_mode = SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_INTERNAL |
SSL_SESS_CACHE_NO_INTERNAL_LOOKUP;
} else {
cache_mode = SSL_SESS_CACHE_OFF;
}
SSL_CTX_use_certificate_file(htp->ssl_ctx, cfg->pemfile, SSL_FILETYPE_PEM);
SSL_CTX_use_PrivateKey_file(htp->ssl_ctx, cfg->privfile ? : cfg->pemfile, SSL_FILETYPE_PEM);
SSL_CTX_set_session_cache_mode(htp->ssl_ctx, cache_mode);
SSL_CTX_set_session_id_context(htp->ssl_ctx, (void*)&s_server_session_id_context,
sizeof s_server_session_id_context);
SSL_CTX_sess_set_new_cb(htp->ssl_ctx, htp_ssl_add_scache_ent);
SSL_CTX_sess_set_get_cb(htp->ssl_ctx, htp_ssl_get_scache_ent);
SSL_CTX_sess_set_remove_cb(htp->ssl_ctx, htp_ssl_del_scache_ent);
SSL_CTX_set_app_data(htp->ssl_ctx, htp);
if (cfg->scache_init) {
cfg->args = (cfg->scache_init)(htp);
}
return 0;
} /* evhtp_use_ssl */
#endif
static unsigned long
htp_ssl_get_thr_id(void) {
return (unsigned long)pthread_self();
}
static void
htp_ssl_thr_lock(int mode, int type, const char * file, int line) {
if (type < ssl_num_locks) {
if (mode & CRYPTO_LOCK) {
pthread_mutex_lock(ssl_locks[type]);
} else {
pthread_mutex_unlock(ssl_locks[type]);
}
}
}
int
evhtp_use_threads(evhtp_t * htp, int nthreads) {
evhtp_log_debug("enter");
if (htp->ssl_ctx != NULL) {
int i;
ssl_num_locks = CRYPTO_num_locks();
ssl_locks = malloc(ssl_num_locks * sizeof(evhtp_mutex_t *));
for (i = 0; i < ssl_num_locks; i++) {
ssl_locks[i] = malloc(sizeof(evhtp_mutex_t));
pthread_mutex_init(ssl_locks[i], NULL);
}
CRYPTO_set_id_callback(htp_ssl_get_thr_id);
CRYPTO_set_locking_callback(htp_ssl_thr_lock);
}
if (!(htp->pool = evthr_pool_new(nthreads, htp))) {
return -1;
}
evthr_pool_start(htp->pool);
return 0;
}
evhtp_t *
evhtp_new(evbase_t * evbase) {
evhtp_t * htp;
evhtp_log_debug("enter");
if (!(htp = calloc(sizeof(evhtp_t), sizeof(char)))) {
return NULL;
}
htp->server_name = _HTP_DEFSERVER;
htp->psets.on_message_begin = htp_start_cb;
htp->psets.on_path = htp_path_cb;
htp->psets.on_query_string = htp_query_str_cb;
htp->psets.on_url = htp_uri_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_headers_complete = htp_headers_complete_cb;
htp->psets.on_body = htp_body_cb;
htp->psets.on_message_complete = htp_end_cb;
htp->evbase = evbase ? : event_base_new();
evhtp_log_debug("created new instance");
return htp;
}
const char *
evhtp_version(void) {
return EVHTP_VERSION;
}