#include #include #include #include #include #include #include #include #include #include #ifndef EVHTP_DISABLE_EVTHR int use_threads = 0; int num_threads = 0; #endif char * bind_addr = "0.0.0.0"; uint16_t bind_port = 8081; char * ext_body = NULL; char * ssl_pem = NULL; char * ssl_ca = NULL; char * ssl_capath = NULL; size_t bw_limit = 0; uint64_t max_keepalives = 0; struct pauser { event_t * timer_ev; evhtp_request_t * request; struct timeval * tv; }; /* pause testing */ static void resume_request_timer(evutil_socket_t sock, short which, void * arg) { struct pauser * pause = (struct pauser *)arg; printf("resume_request_timer(%p) timer_ev = %p\n", pause->request->conn, pause->timer_ev); fflush(stdout); evhtp_request_resume(pause->request); } static evhtp_res pause_cb(evhtp_request_t * request, evhtp_header_t * header, void * arg) { struct pauser * pause = (struct pauser *)arg; int s = rand() % 1000000; printf("pause_cb(%p) pause == %p, timer_ev = %p\n", request->conn, pause, pause->timer_ev); printf("pause_cb(%p) k=%s, v=%s timer_ev = %p\n", request->conn, header->key, header->val, pause->timer_ev); printf("pause_cb(%p) setting to %ld usec sleep timer_ev = %p\n", request->conn, (long int)s, pause->timer_ev); pause->tv->tv_sec = 0; pause->tv->tv_usec = s; if (evtimer_pending(pause->timer_ev, NULL)) { evtimer_del(pause->timer_ev); } evtimer_add(pause->timer_ev, pause->tv); return EVHTP_RES_PAUSE; } static evhtp_res pause_connection_fini(evhtp_connection_t * connection, void * arg) { printf("pause_connection_fini(%p)\n", connection); return EVHTP_RES_OK; } static evhtp_res pause_request_fini(evhtp_request_t * request, void * arg) { struct pauser * pause = (struct pauser *)arg; printf("pause_request_fini() req=%p, c=%p\n", request, request->conn); event_free(pause->timer_ev); free(pause->tv); free(pause); return EVHTP_RES_OK; } static evhtp_res pause_init_cb(evhtp_request_t * req, evhtp_path_t * path, void * arg) { evbase_t * evbase = req->conn->evbase; struct pauser * pause = calloc(sizeof(struct pauser), 1); pause->tv = calloc(sizeof(struct timeval), 1); pause->timer_ev = evtimer_new(evbase, resume_request_timer, pause); pause->request = req; evhtp_set_hook(&req->hooks, evhtp_hook_on_header, pause_cb, pause); evhtp_set_hook(&req->hooks, evhtp_hook_on_request_fini, pause_request_fini, pause); evhtp_set_hook(&req->conn->hooks, evhtp_hook_on_connection_fini, pause_connection_fini, NULL); return EVHTP_RES_OK; } static void test_pause_cb(evhtp_request_t * request, void * arg) { printf("test_pause_cb(%p)\n", request->conn); evhtp_send_reply(request, EVHTP_RES_OK); } #ifndef EVHTP_DISABLE_REGEX static void _owned_readcb(evbev_t * bev, void * arg) { /* echo the input back to the client */ bufferevent_write_buffer(bev, bufferevent_get_input(bev)); } static void _owned_eventcb(evbev_t * bev, short events, void * arg) { bufferevent_free(bev); } static void test_ownership(evhtp_request_t * request, void * arg) { evhtp_connection_t * conn = evhtp_request_get_connection(request); evbev_t * bev = evhtp_connection_take_ownership(conn); bufferevent_enable(bev, EV_READ); bufferevent_setcb(bev, _owned_readcb, NULL, _owned_eventcb, NULL); } static void test_regex(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, "start = '%s', end = '%s\n", req->uri->path->match_start, req->uri->path->match_end); evhtp_send_reply(req, EVHTP_RES_OK); } #endif static void dynamic_cb(evhtp_request_t * r, void * arg) { const char * name = arg; evbuffer_add_printf(r->buffer_out, "dynamic_cb = %s\n", name); evhtp_send_reply(r, EVHTP_RES_OK); } static void create_callback(evhtp_request_t * r, void * arg) { char * uri; char * nuri; size_t urilen; uri = r->uri->path->match_start; urilen = strlen(uri); if (urilen == 0) { return evhtp_send_reply(r, EVHTP_RES_BADREQ); } nuri = calloc(urilen + 2, 1); snprintf(nuri, urilen + 2, "/%s", uri); evhtp_set_cb(r->htp, nuri, dynamic_cb, nuri); evhtp_send_reply(r, EVHTP_RES_OK); } static void test_foo_cb(evhtp_request_t * req, void * arg ) { evbuffer_add_reference(req->buffer_out, "test_foo_cb\n", 12, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } static void test_500_cb(evhtp_request_t * req, void * arg ) { evbuffer_add_reference(req->buffer_out, "test_500_cb\n", 12, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_SERVERR); } static void test_max_body(evhtp_request_t * req, void * arg) { evbuffer_add_reference(req->buffer_out, "test_max_body\n", 14, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } const char * chunk_strings[] = { "I give you the light of EƤrendil,\n", "our most beloved star.\n", "May it be a light for you in dark places,\n", "when all other lights go out.\n", NULL }; static void test_chunking(evhtp_request_t * req, void * arg) { const char * chunk_str; evbuf_t * buf; int i = 0; buf = evbuffer_new(); evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); while ((chunk_str = chunk_strings[i++]) != NULL) { evbuffer_add(buf, chunk_str, strlen(chunk_str)); evhtp_send_reply_chunk(req, buf); evbuffer_drain(buf, -1); } evhtp_send_reply_chunk_end(req); evbuffer_free(buf); } static void test_bar_cb(evhtp_request_t * req, void * arg) { evhtp_send_reply(req, EVHTP_RES_OK); } static void test_glob_cb(evhtp_request_t * req, void * arg) { evbuffer_add(req->buffer_out, "test_glob_cb\n", 13); evhtp_send_reply(req, EVHTP_RES_OK); } static void test_default_cb(evhtp_request_t * req, void * arg) { evbuffer_add_reference(req->buffer_out, "test_default_cb\n", 16, NULL, NULL); evhtp_send_reply(req, EVHTP_RES_OK); } static evhtp_res print_kv(evhtp_request_t * req, evhtp_header_t * hdr, void * arg) { evbuffer_add_printf(req->buffer_out, "print_kv() key = '%s', val = '%s'\n", hdr->key, hdr->val); return EVHTP_RES_OK; } static int output_header(evhtp_header_t * header, void * arg) { evbuf_t * buf = arg; evbuffer_add_printf(buf, "print_kvs() key = '%s', val = '%s'\n", header->key, header->val); return 0; } static evhtp_res print_kvs(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) { evhtp_headers_for_each(hdrs, output_header, req->buffer_out); return EVHTP_RES_OK; } static evhtp_res print_path(evhtp_request_t * req, evhtp_path_t * path, void * arg) { if (ext_body) { evbuffer_add_printf(req->buffer_out, "ext_body: '%s'\n", ext_body); } evbuffer_add_printf(req->buffer_out, "print_path() full = '%s'\n" " path = '%s'\n" " file = '%s'\n" " match start = '%s'\n" " match_end = '%s'\n" " methno = '%d'\n", path->full, path->path, path->file, path->match_start, path->match_end, evhtp_request_get_method(req)); return EVHTP_RES_OK; } static evhtp_res print_data(evhtp_request_t * req, evbuf_t * buf, void * arg) { #ifndef NDEBUG evbuffer_add_printf(req->buffer_out, "got %zu bytes of data\n", evbuffer_get_length(buf)); printf("%.*s", (int)evbuffer_get_length(buf), (char *)evbuffer_pullup(buf, evbuffer_get_length(buf))); #endif evbuffer_drain(buf, -1); return EVHTP_RES_OK; } static evhtp_res print_new_chunk_len(evhtp_request_t * req, uint64_t len, void * arg) { evbuffer_add_printf(req->buffer_out, "started new chunk, %" PRId64 "u bytes\n", len); return EVHTP_RES_OK; } static evhtp_res print_chunk_complete(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, "ended a single chunk\n"); return EVHTP_RES_OK; } static evhtp_res print_chunks_complete(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, "all chunks read\n"); return EVHTP_RES_OK; } #ifndef EVHTP_DISABLE_REGEX static evhtp_res test_regex_hdrs_cb(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) { return EVHTP_RES_OK; } #endif static evhtp_res set_max_body(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg) { evhtp_request_set_max_body_size(req, 1024); return EVHTP_RES_OK; } static evhtp_res test_pre_accept(evhtp_connection_t * c, void * arg) { uint16_t port = *(uint16_t *)arg; if (port > 10000) { return EVHTP_RES_ERROR; } return EVHTP_RES_OK; } static evhtp_res test_fini(evhtp_request_t * r, void * arg) { struct ev_token_bucket_cfg * tcfg = arg; if (tcfg) { ev_token_bucket_cfg_free(tcfg); } return EVHTP_RES_OK; } #if 0 static evhtp_res print_hostname(evhtp_request_t * r, const char * host, void * arg) { printf("%s\n", host); return EVHTP_RES_OK; } #endif static evhtp_res set_my_connection_handlers(evhtp_connection_t * conn, void * arg) { struct timeval tick; struct ev_token_bucket_cfg * tcfg = NULL; evhtp_set_hook(&conn->hooks, evhtp_hook_on_header, print_kv, "foo"); evhtp_set_hook(&conn->hooks, evhtp_hook_on_headers, print_kvs, "bar"); evhtp_set_hook(&conn->hooks, evhtp_hook_on_path, print_path, "baz"); evhtp_set_hook(&conn->hooks, evhtp_hook_on_read, print_data, "derp"); evhtp_set_hook(&conn->hooks, evhtp_hook_on_new_chunk, print_new_chunk_len, NULL); evhtp_set_hook(&conn->hooks, evhtp_hook_on_chunk_complete, print_chunk_complete, NULL); evhtp_set_hook(&conn->hooks, evhtp_hook_on_chunks_complete, print_chunks_complete, NULL); /* evhtp_set_hook(&conn->hooks, evhtp_hook_on_hostname, print_hostname, NULL); */ if (bw_limit > 0) { tick.tv_sec = 0; tick.tv_usec = 500 * 100; tcfg = ev_token_bucket_cfg_new(bw_limit, bw_limit, bw_limit, bw_limit, &tick); bufferevent_set_rate_limit(conn->bev, tcfg); } evhtp_set_hook(&conn->hooks, evhtp_hook_on_request_fini, test_fini, tcfg); return EVHTP_RES_OK; } #ifndef EVHTP_DISABLE_SSL static int dummy_ssl_verify_callback(int ok, X509_STORE_CTX * x509_store) { return 1; } static int dummy_check_issued_cb(X509_STORE_CTX * ctx, X509 * x, X509 * issuer) { return 1; } #endif const char * optstr = "htn:a:p:r:s:c:C:l:N:m:"; const char * help = "Options: \n" " -h : This help text\n" #ifndef EVHTP_DISABLE_EVTHR " -t : Run requests in a thread (default: off)\n" " -n : Number of threads (default: 0 if -t is off, 4 if -t is on)\n" #endif #ifndef EVHTP_DISABLE_SSL " -s : Enable SSL and PEM (default: NULL)\n" " -c : CA cert file (default: NULL)\n" " -C : CA Path (default: NULL)\n" #endif " -l : Max bandwidth (in bytes) (default: NULL)\n" " -r : Document root (default: .)\n" " -N : Add this string to body. (default: NULL)\n" " -a : Bind Address (default: 0.0.0.0)\n" " -p : Bind Port (default: 8081)\n" " -m : Max keepalive requests (default: 0)\n"; int parse_args(int argc, char ** argv) { extern char * optarg; extern int optind; extern int opterr; extern int optopt; int c; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case 'h': printf("Usage: %s [opts]\n%s", argv[0], help); return -1; case 'N': ext_body = strdup(optarg); break; case 'a': bind_addr = strdup(optarg); break; case 'p': bind_port = atoi(optarg); break; #ifndef EVHTP_DISABLE_EVTHR case 't': use_threads = 1; break; case 'n': num_threads = atoi(optarg); break; #endif #ifndef EVHTP_DISABLE_SSL case 's': ssl_pem = strdup(optarg); break; case 'c': ssl_ca = strdup(optarg); break; case 'C': ssl_capath = strdup(optarg); break; #endif case 'l': bw_limit = atoll(optarg); break; case 'm': max_keepalives = atoll(optarg); break; default: printf("Unknown opt %s\n", optarg); return -1; } /* switch */ } #ifndef EVHTP_DISABLE_EVTHR if (use_threads && num_threads == 0) { num_threads = 4; } #endif return 0; } /* parse_args */ static void sigint(int sig, short why, void * data) { event_base_loopexit(data, NULL); } int main(int argc, char ** argv) { struct event * ev_sigint; evbase_t * evbase = NULL; evhtp_t * htp = NULL; evhtp_callback_t * cb_1 = NULL; evhtp_callback_t * cb_2 = NULL; evhtp_callback_t * cb_3 = NULL; evhtp_callback_t * cb_4 = NULL; evhtp_callback_t * cb_5 = NULL; evhtp_callback_t * cb_6 = NULL; evhtp_callback_t * cb_7 = NULL; evhtp_callback_t * cb_8 = NULL; evhtp_callback_t * cb_9 = NULL; evhtp_callback_t * cb_10 = NULL; evhtp_callback_t * cb_11 = NULL; evhtp_callback_t * cb_12 = NULL; if (parse_args(argc, argv) < 0) { exit(1); } srand((unsigned)time(NULL)); evbase = event_base_new(); htp = evhtp_new(evbase, NULL); evhtp_set_max_keepalive_requests(htp, max_keepalives); cb_1 = evhtp_set_cb(htp, "/ref", test_default_cb, "fjdkls"); cb_2 = evhtp_set_cb(htp, "/foo", test_foo_cb, "bar"); cb_3 = evhtp_set_cb(htp, "/foo/", test_foo_cb, "bar"); cb_4 = evhtp_set_cb(htp, "/bar", test_bar_cb, "baz"); cb_5 = evhtp_set_cb(htp, "/500", test_500_cb, "500"); #ifndef EVHTP_DISABLE_REGEX cb_6 = evhtp_set_regex_cb(htp, "^(/anything/).*", test_regex, NULL); #endif cb_7 = evhtp_set_cb(htp, "/pause", test_pause_cb, NULL); #ifndef EVHTP_DISABLE_REGEX cb_8 = evhtp_set_regex_cb(htp, "^/create/(.*)", create_callback, NULL); #endif cb_9 = evhtp_set_glob_cb(htp, "*/glob/*", test_glob_cb, NULL); cb_10 = evhtp_set_cb(htp, "/max_body_size", test_max_body, NULL); /* set a callback to test out chunking API */ cb_11 = evhtp_set_cb(htp, "/chunkme", test_chunking, NULL); /* set a callback which takes ownership of the underlying bufferevent and * just starts echoing things */ cb_12 = evhtp_set_cb(htp, "/ownme", test_ownership, NULL); /* set a callback to pause on each header for cb_7 */ evhtp_set_hook(&cb_7->hooks, evhtp_hook_on_path, pause_init_cb, NULL); /* set a callback to set hooks specifically for the cb_6 callback */ #ifndef EVHTP_DISABLE_REGEX evhtp_set_hook(&cb_6->hooks, evhtp_hook_on_headers, test_regex_hdrs_cb, NULL); #endif evhtp_set_hook(&cb_10->hooks, evhtp_hook_on_headers, set_max_body, NULL); /* set a default request handler */ evhtp_set_gencb(htp, test_default_cb, "foobarbaz"); /* set a callback invoked before a connection is accepted */ evhtp_set_pre_accept_cb(htp, test_pre_accept, &bind_port); /* set a callback to set per-connection hooks (via a post_accept cb) */ evhtp_set_post_accept_cb(htp, set_my_connection_handlers, NULL); #ifndef EVHTP_DISABLE_SSL if (ssl_pem != NULL) { evhtp_ssl_cfg_t scfg = { .pemfile = ssl_pem, .privfile = ssl_pem, .cafile = ssl_ca, .capath = ssl_capath, .ciphers = "RC4+RSA:HIGH:+MEDIUM:+LOW", .ssl_opts = SSL_OP_NO_SSLv2, .ssl_ctx_timeout = 60 * 60 * 48, .verify_peer = SSL_VERIFY_PEER, .verify_depth = 42, .x509_verify_cb = dummy_ssl_verify_callback, .x509_chk_issued_cb = dummy_check_issued_cb, .scache_type = evhtp_ssl_scache_type_internal, .scache_size = 1024, .scache_timeout = 1024, .scache_init = NULL, .scache_add = NULL, .scache_get = NULL, .scache_del = NULL, }; evhtp_ssl_init(htp, &scfg); #ifndef EVHTP_DISABLE_EVTHR if (use_threads) { #define OPENSSL_THREAD_DEFINES #include #if defined(OPENSSL_THREADS) #else fprintf(stderr, "Your version of OpenSSL does not support threading!\n"); exit(-1); #endif } #endif } #endif #ifndef EVHTP_DISABLE_EVTHR if (use_threads) { evhtp_use_threads(htp, NULL, num_threads, NULL); } #endif if (evhtp_bind_socket(htp, bind_addr, bind_port, 128) < 0) { fprintf(stderr, "Could not bind socket: %s\n", strerror(errno)); exit(-1); } ev_sigint = evsignal_new(evbase, SIGINT, sigint, evbase); evsignal_add(ev_sigint, NULL); event_base_loop(evbase, 0); event_free(ev_sigint); evhtp_unbind_socket(htp); evhtp_free(htp); event_base_free(evbase); return 0; } /* main */