}
+static void
+http_timeout_read_client_test(void *arg)
+{
+ struct basic_test_data *data = arg;
+ ev_uint16_t port = 0;
+ struct evhttp_connection *evcon = NULL;
+ struct evhttp_request *req = NULL;
+ struct timeval tv;
+ struct evhttp *http = http_setup(&port, data->base, 0);
+
+ test_ok = 0;
+ exit_base = data->base;
+
+ evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
+ tt_assert(evcon);
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ evhttp_connection_set_connect_timeout_tv(evcon, &tv);
+ evhttp_connection_set_write_timeout_tv(evcon, &tv);
+ tv.tv_usec = 500000;
+ evhttp_connection_set_read_timeout_tv(evcon, &tv);
+
+ req = evhttp_request_new(http_request_done, (void*)"");
+ tt_assert(req);
+ evhttp_add_header(evhttp_request_get_output_headers(req), "Host", "somehost");
+ tt_int_op(evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/delay"), ==, 0);
+ event_base_dispatch(data->base);
+ tt_int_op(test_ok, ==, 1);
+
+ end:
+ if (evcon)
+ evhttp_connection_free(evcon);
+ if (http)
+ evhttp_free(http);
+}
+
+/*
+ * Error callback tests
+ */
+
+#define ERRCBFLAG_GENCB (0x01)
+#define ERRCBFLAG_ERRCB (0x02)
+#define ERRCBFLAG_BOTHCB (ERRCBFLAG_GENCB | ERRCBFLAG_ERRCB)
+
+struct error_callback_test {
+ unsigned char flags;
+ enum evhttp_cmd_type type;
+ int response_code;
+ int length;
+} error_callback_tests[] = {
+ {0 , EVHTTP_REQ_GET , HTTP_NOTFOUND , 152} , /* 0 */
+ {0 , EVHTTP_REQ_POST , HTTP_NOTIMPLEMENTED , 101} , /* 1 */
+ {ERRCBFLAG_GENCB , EVHTTP_REQ_GET , HTTP_NOTFOUND , 89} , /* 2 */
+ {ERRCBFLAG_GENCB , EVHTTP_REQ_POST , HTTP_NOTIMPLEMENTED , 101} , /* 3 */
+ {ERRCBFLAG_ERRCB , EVHTTP_REQ_GET , HTTP_NOTFOUND , 3} , /* 4 */
+ {ERRCBFLAG_ERRCB , EVHTTP_REQ_POST , HTTP_NOTIMPLEMENTED , 101} , /* 5 */
+ {ERRCBFLAG_BOTHCB , EVHTTP_REQ_GET , HTTP_NOTFOUND , 3} , /* 6 */
+ {ERRCBFLAG_BOTHCB , EVHTTP_REQ_POST , HTTP_NOTIMPLEMENTED , 3} , /* 7 */
+ {ERRCBFLAG_ERRCB , EVHTTP_REQ_GET , HTTP_NOTFOUND , 0} , /* 8 */
+ {ERRCBFLAG_ERRCB , EVHTTP_REQ_GET , HTTP_NOTFOUND , 152} , /* 9 */
+};
+
+struct error_callback_state
+{
+ struct basic_test_data *data;
+ unsigned test_index;
+ struct error_callback_test *test;
+ int test_failed;
+};
+
+static void
+http_error_callback_gencb(struct evhttp_request *req, void *arg)
+{
+ struct error_callback_state *state_info = arg;
+
+ switch (state_info->test_index) {
+ case 2:
+ case 6:
+ evhttp_send_error(req, HTTP_NOTFOUND, NULL);
+ break;
+ default:
+ evhttp_send_error(req, HTTP_INTERNAL, NULL);
+ break;
+ }
+}
+
+static int
+http_error_callback_errorcb(struct evhttp_request *req, struct evbuffer *buf,
+ int error, const char *reason, void *arg)
+{
+ struct error_callback_state *state_info = arg;
+ int return_code = -1;
+
+ switch (state_info->test_index) {
+ case 4:
+ case 6:
+ case 7:
+ evbuffer_add_printf(buf, "%d", error);
+ return_code = 0;
+ break;
+ case 8: /* Add nothing to buffer and then return 0 to trigger default */
+ return_code = 0;
+ break;
+ case 9: /* Add text to buffer but then return -1 to trigger default */
+ evbuffer_add_printf(buf, "%d", error);
+ break;
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ return return_code;
+}
+
+static void
+http_error_callback_test_done(struct evhttp_request *req, void *arg)
+{
+ struct evkeyvalq *headers;
+ const char *header_text;
+ struct error_callback_state *state_info = arg;
+ struct error_callback_test *test_info;
+
+ test_info = state_info->test;
+
+ tt_assert(req);
+ tt_int_op(evhttp_request_get_command(req), ==,
+ test_info->type);
+ tt_int_op(evhttp_request_get_response_code(req), ==,
+ test_info->response_code);
+
+ headers = evhttp_request_get_input_headers(req);
+ tt_assert(headers);
+ header_text = evhttp_find_header(headers, "Content-Type");
+ tt_assert_msg(header_text, "Missing Content-Type");
+ tt_str_op(header_text, ==, "text/html");
+ tt_int_op(evbuffer_get_length(evhttp_request_get_input_buffer(req)),
+ ==, test_info->length);
+
+ event_base_loopexit(state_info->data->base, NULL);
+ return;
+
+end:
+ state_info->test_failed = 1;
+ event_base_loopexit(state_info->data->base, NULL);
+}
+
+static void
+http_error_callback_test(void *arg)
+{
+ struct basic_test_data *data = arg;
+ struct evhttp *http = NULL;
+ ev_uint16_t port = 0;
+ struct evhttp_connection *evcon = NULL;
+ struct evhttp_request *req = NULL;
+ struct error_callback_state state_info;
+
+ test_ok = 0;
+
+ http = http_setup(&port, data->base, 0);
+ evhttp_set_allowed_methods(http,
+ EVHTTP_REQ_GET |
+ EVHTTP_REQ_HEAD |
+ EVHTTP_REQ_PUT |
+ EVHTTP_REQ_DELETE);
+
+ evcon = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
+ tt_assert(evcon);
+
+ /* also bind to local host */
+ evhttp_connection_set_local_address(evcon, "127.0.0.1");
+
+ /* Initialise the state info */
+ state_info.data = data;
+ state_info.test = error_callback_tests;
+ state_info.test_failed = 0;
+
+ /* Perform all the tests */
+ for (state_info.test_index = 0;
+ (state_info.test_index < ARRAY_SIZE(error_callback_tests)) &&
+ (!state_info.test_failed);
+ state_info.test_index++)
+ {
+ evhttp_set_gencb(http,
+ ((state_info.test->flags & ERRCBFLAG_GENCB) != 0 ?
+ http_error_callback_gencb : NULL),
+ &state_info);
+ evhttp_set_errorcb(http,
+ ((state_info.test->flags & ERRCBFLAG_ERRCB) != 0 ?
+ http_error_callback_errorcb : NULL),
+ &state_info);
+
+ req = evhttp_request_new(http_error_callback_test_done,
+ &state_info);
+ tt_assert(req);
+ if (evhttp_make_request(evcon, req, state_info.test->type,
+ "/missing") == -1) {
+ tt_abort_msg("Couldn't make request");
+ exit(1);
+ }
+ event_base_dispatch(data->base);
+
+ if (!state_info.test_failed)
+ test_ok++;
+ else
+ tt_abort_printf(("Sub-test %d failed",
+ state_info.test_index));
+ state_info.test++;
+ }
+
+end:
+ if (evcon)
+ evhttp_connection_free(evcon);
+ if (http)
+ evhttp_free(http);
+}
+
+static void http_add_output_buffer(int fd, short events, void *arg)
+{
+ evbuffer_add(arg, POST_DATA, strlen(POST_DATA));
+}
+static void
+http_timeout_read_server_test(void *arg)
+{
+ struct basic_test_data *data = arg;
+ struct timeval tv;
+ struct bufferevent *bev;
+ struct evbuffer *out;
+ int fd = -1;
+ ev_uint16_t port = 0;
+ struct evhttp *http = http_setup(&port, data->base, 0);
+
+ test_ok = 0;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 100000;
+ evhttp_set_write_timeout_tv(http, &tv);
+ tv.tv_usec = 500000;
+ evhttp_set_read_timeout_tv(http, &tv);
+
+ fd = http_connect("127.0.0.1", port);
+ bev = create_bev(data->base, fd, 0);
+ bufferevent_setcb(bev, http_readcb, http_writecb, http_errorcb, data->base);
+ out = bufferevent_get_output(bev);
+
+ evbuffer_add_printf(out,
+ "POST /postit HTTP/1.1\r\n"
+ "Host: somehost\r\n"
+ "Content-Length: " EV_SIZE_FMT "\r\n"
+ "\r\n", strlen(POST_DATA));
+
+ tv.tv_usec = 200000;
+ event_base_once(data->base, -1, EV_TIMEOUT, http_add_output_buffer, out, &tv);
+
+ event_base_dispatch(data->base);
+ tt_int_op(test_ok, ==, 3);
+
+ end:
+ if (bev)
+ bufferevent_free(bev);
+ if (fd != -1)
+ evutil_closesocket(fd);
+ if (http)
+ evhttp_free(http);
+}
+
+
+
+ #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
+ static void
+ http_max_connections_test(void *arg)
+ {
+ struct basic_test_data *data = arg;
+ ev_uint16_t port = 0;
+ struct evhttp *http = http_setup(&port, data->base, 0);
+ struct evhttp_connection *evcons[2];
+ struct http_newreqcb_test_state newreqcb_test_state;
+ unsigned n;
+
+ exit_base = data->base;
+ test_ok = 0;
+
+ memset(&newreqcb_test_state, 0, sizeof(newreqcb_test_state));
+ memset(evcons, 0, sizeof(evcons));
+
+ evhttp_set_max_connections(http, ARRAY_SIZE(evcons)-1);
+
+ for (n = 0; n < sizeof(evcons)/sizeof(evcons[0]); ++n) {
+ struct evhttp_connection* evcon = NULL;
+ struct evhttp_request *req = NULL;
+ evcons[n] = evhttp_connection_base_new(data->base, NULL, "127.0.0.1", port);
+ evcon = evcons[n];
+ evhttp_connection_set_retries(evcon, 0);
+
+ tt_assert(evcon);
+
+ req = evhttp_request_new(http_request_done_newreqcb, &newreqcb_test_state);
+ evhttp_add_header(evhttp_request_get_output_headers(req), "Connection", "close");
+ evhttp_request_set_error_cb(req, http_request_error_newreqcb);
+
+ /* We give ownership of the request to the connection */
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
+ tt_abort_msg("Couldn't make request");
+ }
+
+ ++newreqcb_test_state.connections_started;
+ http_newreqcb_test_state_check(&newreqcb_test_state);
+ }
+
+ /* XXX: http_newreqcb_test_state_check will not stop the base, since:
+ * - connections_done == 2
+ * - connections_good == 1
+ *
+ * hence timeout
+ */
+ {
+ struct timeval tv = { 0, 300e3 };
+ event_base_loopexit(data->base, &tv);
+ }
+
+ event_base_dispatch(data->base);
+
+ http_newreqcb_test_state_check(&newreqcb_test_state);
+ tt_int_op(newreqcb_test_state.connections_error, ==, 0);
+ tt_int_op(newreqcb_test_state.connections_done, ==, 2);
+ tt_int_op(newreqcb_test_state.connections_good, ==, 1);
+
+ end:
+ evhttp_free(http);
+
+ for (n = 0; n < ARRAY_SIZE(evcons); ++n) {
+ if (evcons[n]) {
+ evhttp_connection_free(evcons[n]);
+ }
+ }
+ }
+
+
#define HTTP_LEGACY(name) \
{ #name, run_legacy_test_fn, TT_ISOLATED|TT_LEGACY, &legacy_setup, \
http_##name##_test }
HTTP(request_extra_body),
HTTP(newreqcb),
+ HTTP(max_connections),
+ HTTP(timeout_read_client),
+ HTTP(timeout_read_server),
+
#ifdef EVENT__HAVE_OPENSSL
HTTPS(basic),
HTTPS(filter_basic),