mirror of
https://github.com/cuberite/libevent.git
synced 2025-09-08 20:07:56 -04:00
Merge commit 'niels/http_dns'
This commit is contained in:
commit
854645797b
@ -97,6 +97,7 @@ struct evhttp_connection {
|
||||
void *closecb_arg;
|
||||
|
||||
struct event_base *base;
|
||||
struct evdns_base *dns_base;
|
||||
};
|
||||
|
||||
struct evhttp_cb {
|
||||
|
77
http.c
77
http.c
@ -155,7 +155,6 @@ fake_getnameinfo(const struct sockaddr *sa, size_t salen, char *host,
|
||||
|
||||
extern int debug;
|
||||
|
||||
static int socket_connect(evutil_socket_t kefd, const char *address, unsigned short port);
|
||||
static evutil_socket_t bind_socket_ai(struct evutil_addrinfo *, int reuse);
|
||||
static evutil_socket_t bind_socket(const char *, ev_uint16_t, int reuse);
|
||||
static void name_from_addr(struct sockaddr *, ev_socklen_t, char **, char **);
|
||||
@ -1149,16 +1148,26 @@ evhttp_error_cb(struct bufferevent *bufev, short what, void *arg)
|
||||
}
|
||||
|
||||
/*
|
||||
* Call back for asynchronous connection attempt.
|
||||
* Event callback for asynchronous connection attempt.
|
||||
*/
|
||||
|
||||
static void
|
||||
evhttp_connection_cb(struct bufferevent *bufev, void *arg)
|
||||
evhttp_connection_cb(struct bufferevent *bufev, short what, void *arg)
|
||||
{
|
||||
struct evhttp_connection *evcon = arg;
|
||||
int error;
|
||||
ev_socklen_t errsz = sizeof(error);
|
||||
|
||||
if (!(what & BEV_EVENT_CONNECTED)) {
|
||||
/* some operating systems return ECONNREFUSED immediately
|
||||
* when connecting to a local address. the cleanup is going
|
||||
* to reschedule this function call.
|
||||
*/
|
||||
if (errno == ECONNREFUSED)
|
||||
goto cleanup;
|
||||
evhttp_error_cb(bufev, what, arg);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if the connection completed */
|
||||
if (getsockopt(evcon->fd, SOL_SOCKET, SO_ERROR, (void*)&error,
|
||||
&errsz) == -1) {
|
||||
@ -1730,11 +1739,11 @@ evhttp_read_header(struct evhttp_connection *evcon,
|
||||
struct evhttp_connection *
|
||||
evhttp_connection_new(const char *address, unsigned short port)
|
||||
{
|
||||
return (evhttp_connection_base_new(NULL, address, port));
|
||||
return (evhttp_connection_base_new(NULL, NULL, address, port));
|
||||
}
|
||||
|
||||
struct evhttp_connection *
|
||||
evhttp_connection_base_new(struct event_base *base,
|
||||
evhttp_connection_base_new(struct event_base *base, struct evdns_base *dnsbase,
|
||||
const char *address, unsigned short port)
|
||||
{
|
||||
struct evhttp_connection *evcon = NULL;
|
||||
@ -1776,6 +1785,8 @@ evhttp_connection_base_new(struct event_base *base,
|
||||
bufferevent_base_set(base, evcon->bufev);
|
||||
}
|
||||
|
||||
evcon->dns_base = dnsbase;
|
||||
|
||||
return (evcon);
|
||||
|
||||
error:
|
||||
@ -1850,7 +1861,20 @@ evhttp_connection_connect(struct evhttp_connection *evcon)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (socket_connect(evcon->fd, evcon->address, evcon->port) == -1) {
|
||||
/* Set up a callback for successful connection setup */
|
||||
bufferevent_setfd(evcon->bufev, evcon->fd);
|
||||
bufferevent_setcb(evcon->bufev,
|
||||
NULL /* evhttp_read_cb */,
|
||||
NULL /* evhttp_write_cb */,
|
||||
evhttp_connection_cb,
|
||||
evcon);
|
||||
bufferevent_settimeout(evcon->bufev, 0,
|
||||
evcon->timeout != -1 ? evcon->timeout : HTTP_CONNECT_TIMEOUT);
|
||||
/* make sure that we get a write callback */
|
||||
bufferevent_enable(evcon->bufev, EV_WRITE);
|
||||
|
||||
if (bufferevent_socket_connect_hostname(evcon->bufev, evcon->dns_base,
|
||||
AF_UNSPEC, evcon->address, evcon->port) < 0) {
|
||||
event_sock_warn(evcon->fd, "%s: connection to \"%s\" failed",
|
||||
__func__, evcon->address);
|
||||
/* some operating systems return ECONNREFUSED immediately
|
||||
@ -1861,17 +1885,6 @@ evhttp_connection_connect(struct evhttp_connection *evcon)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Set up a callback for successful connection setup */
|
||||
bufferevent_setfd(evcon->bufev, evcon->fd);
|
||||
bufferevent_setcb(evcon->bufev,
|
||||
NULL /* evhttp_read_cb */,
|
||||
evhttp_connection_cb,
|
||||
evhttp_error_cb, evcon);
|
||||
bufferevent_settimeout(evcon->bufev, 0,
|
||||
evcon->timeout != -1 ? evcon->timeout : HTTP_CONNECT_TIMEOUT);
|
||||
/* make sure that we get a write callback */
|
||||
bufferevent_enable(evcon->bufev, EV_WRITE);
|
||||
|
||||
evcon->state = EVCON_CONNECTING;
|
||||
|
||||
return (0);
|
||||
@ -2906,7 +2919,7 @@ evhttp_get_request_connection(
|
||||
|
||||
/* we need a connection object to put the http request on */
|
||||
evcon = evhttp_connection_base_new(
|
||||
http->base, hostname, atoi(portname));
|
||||
http->base, NULL, hostname, atoi(portname));
|
||||
mm_free(hostname);
|
||||
mm_free(portname);
|
||||
if (evcon == NULL)
|
||||
@ -3114,29 +3127,3 @@ bind_socket(const char *address, ev_uint16_t port, int reuse)
|
||||
return (fd);
|
||||
}
|
||||
|
||||
static int
|
||||
socket_connect(evutil_socket_t fd, const char *address, unsigned short port)
|
||||
{
|
||||
struct evutil_addrinfo *ai = make_addrinfo(address, port);
|
||||
int res = -1;
|
||||
|
||||
if (ai == NULL) {
|
||||
event_debug(("%s: make_addrinfo: \"%s:%d\"",
|
||||
__func__, address, port));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (connect(fd, ai->ai_addr, ai->ai_addrlen) == -1) {
|
||||
int err = evutil_socket_geterror(fd);
|
||||
if (! EVUTIL_ERR_CONNECT_RETRIABLE(err))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* everything is fine */
|
||||
res = 0;
|
||||
|
||||
out:
|
||||
evutil_freeaddrinfo(ai);
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
@ -353,13 +353,23 @@ void evhttp_request_set_chunked_cb(struct evhttp_request *,
|
||||
/** Frees the request object and removes associated events. */
|
||||
void evhttp_request_free(struct evhttp_request *req);
|
||||
|
||||
struct evdns_base;
|
||||
|
||||
/**
|
||||
* A connection object that can be used to for making HTTP requests. The
|
||||
* connection object tries to establish the connection when it is given an
|
||||
* http request object.
|
||||
* connection object tries to resolve address and establish the connection
|
||||
* when it is given an http request object.
|
||||
*
|
||||
* @param base the event_base to use for handling the connection
|
||||
* @param dnsbase the dns_base to use for resolving host names; if not
|
||||
* specified host name resolution will block.
|
||||
* @param address the address to which to connect
|
||||
* @param port the port to connect to
|
||||
* @return an evhttp_connection object that can be used for making requests
|
||||
*/
|
||||
struct evhttp_connection *evhttp_connection_base_new(
|
||||
struct event_base *base, const char *address, unsigned short port);
|
||||
struct event_base *base, struct evdns_base *dnsbase,
|
||||
const char *address, unsigned short port);
|
||||
|
||||
/** Takes ownership of the request object
|
||||
*
|
||||
|
@ -21,6 +21,7 @@ test_ratelim_SOURCES = test-ratelim.c
|
||||
test_ratelim_LDADD = ../libevent_core.la -lm
|
||||
|
||||
regress_SOURCES = regress.c regress_buffer.c regress_http.c regress_dns.c \
|
||||
regress_testutils.c regress_testutils.h \
|
||||
regress_rpc.c regress.gen.c regress.gen.h regress_et.c \
|
||||
regress_bufferevent.c regress_listener.c \
|
||||
regress_util.c tinytest.c regress_main.c regress_minheap.c \
|
||||
|
@ -4,6 +4,7 @@ CFLAGS=/I.. /I../include /I../WIN32-Code /I../compat /DWIN32 /DHAVE_CONFIG_H
|
||||
CFLAGS=$(CFLAGS) /Ox /W3 /wd4996 /nologo
|
||||
|
||||
REGRESS_OBJS=regress.obj regress_buffer.obj regress_http.obj regress_dns.obj \
|
||||
regress_testutils.obj \
|
||||
regress_rpc.obj regress.gen.obj \
|
||||
regress_et.obj regress_bufferevent.obj \
|
||||
regress_listener.obj regress_util.obj tinytest.obj \
|
||||
|
@ -66,12 +66,12 @@
|
||||
#include "evdns.h"
|
||||
#include "log-internal.h"
|
||||
#include "regress.h"
|
||||
#include "regress_testutils.h"
|
||||
|
||||
static int dns_ok = 0;
|
||||
static int dns_got_cancel = 0;
|
||||
static int dns_err = 0;
|
||||
|
||||
static int get_socket_port(evutil_socket_t fd);
|
||||
|
||||
static void
|
||||
dns_gethostbyname_cb(int result, char type, int count, int ttl,
|
||||
@ -430,89 +430,6 @@ end:
|
||||
evdns_base_free(base, 0);
|
||||
}
|
||||
|
||||
struct generic_dns_server_table {
|
||||
const char *q;
|
||||
const char *anstype;
|
||||
const char *ans;
|
||||
int seen;
|
||||
};
|
||||
|
||||
static void
|
||||
generic_dns_server_cb(struct evdns_server_request *req, void *data)
|
||||
{
|
||||
struct generic_dns_server_table *tab = data;
|
||||
const char *question;
|
||||
|
||||
if (req->nquestions != 1)
|
||||
TT_DIE(("Only handling one question at a time; got %d",
|
||||
req->nquestions));
|
||||
|
||||
question = req->questions[0]->name;
|
||||
|
||||
while (tab->q && evutil_ascii_strcasecmp(question, tab->q) &&
|
||||
strcmp("*", tab->q))
|
||||
++tab;
|
||||
if (tab->q == NULL)
|
||||
TT_DIE(("Unexpected question: '%s'", question));
|
||||
|
||||
++tab->seen;
|
||||
|
||||
if (!strcmp(tab->anstype, "err")) {
|
||||
int err = atoi(tab->ans);
|
||||
tt_assert(! evdns_server_request_respond(req, err));
|
||||
return;
|
||||
} else if (!strcmp(tab->anstype, "A")) {
|
||||
struct in_addr in;
|
||||
evutil_inet_pton(AF_INET, tab->ans, &in);
|
||||
evdns_server_request_add_a_reply(req, question, 1, &in.s_addr,
|
||||
100);
|
||||
} else if (!strcmp(tab->anstype, "AAAA")) {
|
||||
struct in6_addr in6;
|
||||
evutil_inet_pton(AF_INET6, tab->ans, &in6);
|
||||
evdns_server_request_add_aaaa_reply(req,
|
||||
question, 1, &in6.s6_addr, 100);
|
||||
} else {
|
||||
TT_DIE(("Weird table entry with type '%s'", tab->anstype));
|
||||
}
|
||||
tt_assert(! evdns_server_request_respond(req, 0))
|
||||
return;
|
||||
end:
|
||||
tt_want(! evdns_server_request_drop(req));
|
||||
}
|
||||
|
||||
static struct evdns_server_port *
|
||||
get_generic_server(struct event_base *base,
|
||||
ev_uint16_t *portnum,
|
||||
evdns_request_callback_fn_type cb,
|
||||
void *arg)
|
||||
{
|
||||
struct evdns_server_port *port = NULL;
|
||||
evutil_socket_t sock;
|
||||
struct sockaddr_in my_addr;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock<=0) {
|
||||
tt_abort_perror("socket");
|
||||
}
|
||||
|
||||
evutil_make_socket_nonblocking(sock);
|
||||
|
||||
memset(&my_addr, 0, sizeof(my_addr));
|
||||
my_addr.sin_family = AF_INET;
|
||||
my_addr.sin_port = htons(*portnum);
|
||||
my_addr.sin_addr.s_addr = htonl(0x7f000001UL);
|
||||
if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) {
|
||||
tt_abort_perror("bind");
|
||||
}
|
||||
port = evdns_add_server_port_with_base(base, sock, 0, cb, arg);
|
||||
if (!*portnum)
|
||||
*portnum = get_socket_port(sock);
|
||||
|
||||
return port;
|
||||
end:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int n_replies_left;
|
||||
static struct event_base *exit_base;
|
||||
|
||||
@ -554,7 +471,7 @@ generic_dns_callback(int result, char type, int count, int ttl, void *addresses,
|
||||
event_base_loopexit(exit_base, NULL);
|
||||
}
|
||||
|
||||
static struct generic_dns_server_table search_table[] = {
|
||||
static struct regress_dns_server_table search_table[] = {
|
||||
{ "host.a.example.com", "err", "3", 0 },
|
||||
{ "host.b.example.com", "err", "3", 0 },
|
||||
{ "host.c.example.com", "A", "11.22.33.44", 0 },
|
||||
@ -573,15 +490,12 @@ dns_search_test(void *arg)
|
||||
{
|
||||
struct basic_test_data *data = arg;
|
||||
struct event_base *base = data->base;
|
||||
struct evdns_server_port *port = NULL;
|
||||
struct evdns_base *dns = NULL;
|
||||
ev_uint16_t portnum = 53900;/*XXXX let the code pick a port*/
|
||||
|
||||
struct generic_dns_callback_result r1, r2, r3, r4, r5;
|
||||
|
||||
port = get_generic_server(base, &portnum, generic_dns_server_cb,
|
||||
search_table);
|
||||
tt_assert(port);
|
||||
tt_assert(regress_dnsserver(base, &portnum, search_table));
|
||||
|
||||
dns = evdns_base_new(base, 0);
|
||||
tt_assert(!evdns_base_nameserver_ip_add(dns, "127.0.0.1:53900"));
|
||||
@ -614,8 +528,8 @@ dns_search_test(void *arg)
|
||||
end:
|
||||
if (dns)
|
||||
evdns_base_free(dns, 0);
|
||||
if (port)
|
||||
evdns_close_server_port(port);
|
||||
|
||||
regress_clean_dnsserver();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -664,8 +578,8 @@ dns_retry_test(void *arg)
|
||||
|
||||
struct generic_dns_callback_result r1;
|
||||
|
||||
port = get_generic_server(base, &portnum, fail_server_cb,
|
||||
&drop_count);
|
||||
port = regress_get_dnsserver(base, &portnum, NULL,
|
||||
fail_server_cb, &drop_count);
|
||||
tt_assert(port);
|
||||
|
||||
dns = evdns_base_new(base, 0);
|
||||
@ -724,7 +638,7 @@ end:
|
||||
evdns_close_server_port(port);
|
||||
}
|
||||
|
||||
static struct generic_dns_server_table internal_error_table[] = {
|
||||
static struct regress_dns_server_table internal_error_table[] = {
|
||||
/* Error 4 (NOTIMPL) makes us reissue the request to another server
|
||||
if we can.
|
||||
|
||||
@ -734,7 +648,7 @@ static struct generic_dns_server_table internal_error_table[] = {
|
||||
{ NULL, NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static struct generic_dns_server_table reissue_table[] = {
|
||||
static struct regress_dns_server_table reissue_table[] = {
|
||||
{ "foof.example.com", "A", "240.15.240.15", 0 },
|
||||
{ NULL, NULL, NULL, 0 }
|
||||
};
|
||||
@ -749,11 +663,11 @@ dns_reissue_test(void *arg)
|
||||
struct generic_dns_callback_result r1;
|
||||
ev_uint16_t portnum1 = 53900, portnum2=53901;
|
||||
|
||||
port1 = get_generic_server(base, &portnum1, generic_dns_server_cb,
|
||||
internal_error_table);
|
||||
port1 = regress_get_dnsserver(base, &portnum1, NULL,
|
||||
regress_dns_server_cb, internal_error_table);
|
||||
tt_assert(port1);
|
||||
port2 = get_generic_server(base, &portnum2, generic_dns_server_cb,
|
||||
reissue_table);
|
||||
port2 = regress_get_dnsserver(base, &portnum2, NULL,
|
||||
regress_dns_server_cb, reissue_table);
|
||||
tt_assert(port2);
|
||||
|
||||
dns = evdns_base_new(base, 0);
|
||||
@ -805,16 +719,13 @@ dns_inflight_test(void *arg)
|
||||
{
|
||||
struct basic_test_data *data = arg;
|
||||
struct event_base *base = data->base;
|
||||
struct evdns_server_port *port = NULL;
|
||||
struct evdns_base *dns = NULL;
|
||||
ev_uint16_t portnum = 53900;/*XXXX let the code pick a port*/
|
||||
|
||||
struct generic_dns_callback_result r[20];
|
||||
int i;
|
||||
|
||||
port = get_generic_server(base, &portnum, generic_dns_server_cb,
|
||||
reissue_table);
|
||||
tt_assert(port);
|
||||
tt_assert(regress_dnsserver(base, &portnum, reissue_table));
|
||||
|
||||
/* Make sure that having another (very bad!) RNG doesn't mess us
|
||||
* up. */
|
||||
@ -842,8 +753,7 @@ dns_inflight_test(void *arg)
|
||||
end:
|
||||
if (dns)
|
||||
evdns_base_free(dns, 0);
|
||||
if (port)
|
||||
evdns_close_server_port(port);
|
||||
regress_clean_dnsserver();
|
||||
}
|
||||
|
||||
/* === Test for bufferevent_socket_connect_hostname */
|
||||
@ -985,22 +895,6 @@ nil_accept_cb(struct evconnlistener *l, evutil_socket_t fd, struct sockaddr *s,
|
||||
/* don't do anything with the socket; let it close when we exit() */
|
||||
}
|
||||
|
||||
/* Helper: return the port that a socket is bound on, in host order. */
|
||||
static int
|
||||
get_socket_port(evutil_socket_t fd)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
ev_socklen_t socklen = sizeof(ss);
|
||||
if (getsockname(fd, (struct sockaddr*)&ss, &socklen) != 0)
|
||||
return -1;
|
||||
if (ss.ss_family == AF_INET)
|
||||
return ntohs( ((struct sockaddr_in*)&ss)->sin_port);
|
||||
else if (ss.ss_family == AF_INET6)
|
||||
return ntohs( ((struct sockaddr_in6*)&ss)->sin6_port);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Bufferevent event callback for the connect_hostname test: remembers what
|
||||
* event we got. */
|
||||
static void
|
||||
@ -1052,9 +946,10 @@ test_bufferevent_connect_hostname(void *arg)
|
||||
&n_accept,
|
||||
LEV_OPT_REUSEABLE|LEV_OPT_CLOSE_ON_EXEC,
|
||||
-1, (struct sockaddr *)&sin, sizeof(sin));
|
||||
listener_port = get_socket_port(evconnlistener_get_fd(listener));
|
||||
listener_port = regress_get_socket_port(
|
||||
evconnlistener_get_fd(listener));
|
||||
|
||||
port = get_generic_server(data->base, &dns_port,
|
||||
port = regress_get_dnsserver(data->base, &dns_port, NULL,
|
||||
be_getaddrinfo_server_cb, &n_dns);
|
||||
tt_assert(port);
|
||||
tt_int_op(dns_port, >=, 0);
|
||||
@ -1329,7 +1224,7 @@ test_getaddrinfo_async(void *arg)
|
||||
|
||||
/* 2. Okay, now we can actually test the asynchronous resolver. */
|
||||
/* Start a dummy local dns server... */
|
||||
port = get_generic_server(data->base, &dns_port,
|
||||
port = regress_get_dnsserver(data->base, &dns_port, NULL,
|
||||
be_getaddrinfo_server_cb, &n_dns_questions);
|
||||
tt_assert(port);
|
||||
tt_int_op(dns_port, >=, 0);
|
||||
|
@ -50,12 +50,15 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "event2/dns.h"
|
||||
|
||||
#include "event.h"
|
||||
#include "evhttp.h"
|
||||
#include "log-internal.h"
|
||||
#include "util-internal.h"
|
||||
#include "http-internal.h"
|
||||
#include "regress.h"
|
||||
#include "regress_testutils.h"
|
||||
|
||||
static struct evhttp *http;
|
||||
/* set if a test needs to call loopexit on a base */
|
||||
@ -725,6 +728,105 @@ http_persist_connection_test(void)
|
||||
_http_connection_test(1);
|
||||
}
|
||||
|
||||
static struct regress_dns_server_table search_table[] = {
|
||||
{ "localhost", "A", "127.0.0.1", 0 },
|
||||
{ NULL, NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
static void
|
||||
http_connection_async_test(void)
|
||||
{
|
||||
short port = -1;
|
||||
struct evhttp_connection *evcon = NULL;
|
||||
struct evhttp_request *req = NULL;
|
||||
struct evdns_base *dns_base = NULL;
|
||||
ev_uint16_t portnum = 0;
|
||||
char address[64];
|
||||
|
||||
tt_assert(regress_dnsserver(base, &portnum, search_table));
|
||||
|
||||
dns_base = evdns_base_new(base, 0/* init name servers */);
|
||||
tt_assert(dns_base);
|
||||
|
||||
/* Add ourself as the only nameserver, and make sure we really are
|
||||
* the only nameserver. */
|
||||
evutil_snprintf(address, sizeof (address), "127.0.0.1:%d", portnum);
|
||||
evdns_base_nameserver_ip_add(dns_base, address);
|
||||
|
||||
test_ok = 0;
|
||||
|
||||
http = http_setup(&port, NULL);
|
||||
|
||||
evcon = evhttp_connection_base_new(base, dns_base, "127.0.0.1", port);
|
||||
tt_assert(evcon);
|
||||
|
||||
/*
|
||||
* At this point, we want to schedule a request to the HTTP
|
||||
* server using our make request method.
|
||||
*/
|
||||
|
||||
req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
|
||||
|
||||
/* Add the information that we care about */
|
||||
evhttp_add_header(req->output_headers, "Host", "somehost");
|
||||
|
||||
/* We give ownership of the request to the connection */
|
||||
if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/test") == -1) {
|
||||
fprintf(stdout, "FAILED\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
event_dispatch();
|
||||
|
||||
tt_assert(test_ok);
|
||||
|
||||
/* try to make another request over the same connection */
|
||||
test_ok = 0;
|
||||
|
||||
req = evhttp_request_new(http_request_done, (void*) BASIC_REQUEST_BODY);
|
||||
|
||||
/* Add the information that we care about */
|
||||
evhttp_add_header(req->output_headers, "Host", "somehost");
|
||||
|
||||
/*
|
||||
* if our connections are not supposed to be persistent; request
|
||||
* a close from the server.
|
||||
*/
|
||||
evhttp_add_header(req->output_headers, "Connection", "close");
|
||||
|
||||
/* 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");
|
||||
}
|
||||
|
||||
event_dispatch();
|
||||
|
||||
/* make another request: request empty reply */
|
||||
test_ok = 0;
|
||||
|
||||
req = evhttp_request_new(http_request_empty_done, NULL);
|
||||
|
||||
/* Add the information that we care about */
|
||||
evhttp_add_header(req->output_headers, "Empty", "itis");
|
||||
|
||||
/* 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");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
event_dispatch();
|
||||
|
||||
end:
|
||||
if (evcon)
|
||||
evhttp_connection_free(evcon);
|
||||
if (http)
|
||||
evhttp_free(http);
|
||||
if (dns_base)
|
||||
evdns_base_free(dns_base, 0);
|
||||
regress_clean_dnsserver();
|
||||
}
|
||||
|
||||
static void
|
||||
http_request_never_call(struct evhttp_request *req, void *arg)
|
||||
{
|
||||
@ -2426,6 +2528,7 @@ struct testcase_t http_testcases[] = {
|
||||
HTTP_LEGACY(failure),
|
||||
HTTP_LEGACY(connection),
|
||||
HTTP_LEGACY(persist_connection),
|
||||
HTTP_LEGACY(connection_async),
|
||||
HTTP_LEGACY(close_detection),
|
||||
HTTP_LEGACY(close_detection_delay),
|
||||
HTTP_LEGACY(bad_request),
|
||||
|
185
test/regress_testutils.c
Normal file
185
test/regress_testutils.c
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Niels Provos and Nick Mathewson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include "event-config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#ifdef _EVENT_HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <sys/queue.h>
|
||||
#ifndef WIN32
|
||||
#include <sys/socket.h>
|
||||
#include <signal.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef _EVENT_HAVE_NETINET_IN6_H
|
||||
#include <netinet/in6.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "event2/event.h"
|
||||
#include "event2/event_compat.h"
|
||||
#include <event2/util.h>
|
||||
#include <event2/listener.h>
|
||||
#include <event2/bufferevent.h>
|
||||
#include "evdns.h"
|
||||
#include "log-internal.h"
|
||||
#include "regress.h"
|
||||
#include "regress_testutils.h"
|
||||
|
||||
/* globals */
|
||||
static struct evdns_server_port *dns_port;
|
||||
evutil_socket_t dns_sock = -1;
|
||||
|
||||
/* Helper: return the port that a socket is bound on, in host order. */
|
||||
int
|
||||
regress_get_socket_port(evutil_socket_t fd)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
ev_socklen_t socklen = sizeof(ss);
|
||||
if (getsockname(fd, (struct sockaddr*)&ss, &socklen) != 0)
|
||||
return -1;
|
||||
if (ss.ss_family == AF_INET)
|
||||
return ntohs( ((struct sockaddr_in*)&ss)->sin_port);
|
||||
else if (ss.ss_family == AF_INET6)
|
||||
return ntohs( ((struct sockaddr_in6*)&ss)->sin6_port);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct evdns_server_port *
|
||||
regress_get_dnsserver(struct event_base *base,
|
||||
ev_uint16_t *portnum,
|
||||
evutil_socket_t *psock,
|
||||
evdns_request_callback_fn_type cb,
|
||||
void *arg)
|
||||
{
|
||||
struct evdns_server_port *port = NULL;
|
||||
evutil_socket_t sock;
|
||||
struct sockaddr_in my_addr;
|
||||
|
||||
sock = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sock <= 0) {
|
||||
tt_abort_perror("socket");
|
||||
}
|
||||
|
||||
evutil_make_socket_nonblocking(sock);
|
||||
|
||||
memset(&my_addr, 0, sizeof(my_addr));
|
||||
my_addr.sin_family = AF_INET;
|
||||
my_addr.sin_port = htons(*portnum);
|
||||
my_addr.sin_addr.s_addr = htonl(0x7f000001UL);
|
||||
if (bind(sock, (struct sockaddr*)&my_addr, sizeof(my_addr)) < 0) {
|
||||
tt_abort_perror("bind");
|
||||
}
|
||||
port = evdns_add_server_port_with_base(base, sock, 0, cb, arg);
|
||||
if (!*portnum)
|
||||
*portnum = regress_get_socket_port(sock);
|
||||
if (psock)
|
||||
*psock = sock;
|
||||
|
||||
return port;
|
||||
end:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
regress_clean_dnsserver(void)
|
||||
{
|
||||
if (dns_port)
|
||||
evdns_close_server_port(dns_port);
|
||||
if (dns_sock >= 0)
|
||||
EVUTIL_CLOSESOCKET(dns_sock);
|
||||
}
|
||||
|
||||
void
|
||||
regress_dns_server_cb(struct evdns_server_request *req, void *data)
|
||||
{
|
||||
struct regress_dns_server_table *tab = data;
|
||||
const char *question;
|
||||
|
||||
if (req->nquestions != 1)
|
||||
TT_DIE(("Only handling one question at a time; got %d",
|
||||
req->nquestions));
|
||||
|
||||
question = req->questions[0]->name;
|
||||
|
||||
while (tab->q && evutil_ascii_strcasecmp(question, tab->q) &&
|
||||
strcmp("*", tab->q))
|
||||
++tab;
|
||||
if (tab->q == NULL)
|
||||
TT_DIE(("Unexpected question: '%s'", question));
|
||||
|
||||
++tab->seen;
|
||||
|
||||
if (!strcmp(tab->anstype, "err")) {
|
||||
int err = atoi(tab->ans);
|
||||
tt_assert(! evdns_server_request_respond(req, err));
|
||||
return;
|
||||
} else if (!strcmp(tab->anstype, "A")) {
|
||||
struct in_addr in;
|
||||
evutil_inet_pton(AF_INET, tab->ans, &in);
|
||||
evdns_server_request_add_a_reply(req, question, 1, &in.s_addr,
|
||||
100);
|
||||
} else if (!strcmp(tab->anstype, "AAAA")) {
|
||||
struct in6_addr in6;
|
||||
evutil_inet_pton(AF_INET6, tab->ans, &in6);
|
||||
evdns_server_request_add_aaaa_reply(req,
|
||||
question, 1, &in6.s6_addr, 100);
|
||||
} else {
|
||||
TT_DIE(("Weird table entry with type '%s'", tab->anstype));
|
||||
}
|
||||
tt_assert(! evdns_server_request_respond(req, 0))
|
||||
return;
|
||||
end:
|
||||
tt_want(! evdns_server_request_drop(req));
|
||||
}
|
||||
|
||||
int
|
||||
regress_dnsserver(struct event_base *base, ev_uint16_t *port,
|
||||
struct regress_dns_server_table *search_table)
|
||||
{
|
||||
dns_port = regress_get_dnsserver(base, port, &dns_sock,
|
||||
regress_dns_server_cb, search_table);
|
||||
return dns_port != NULL;
|
||||
}
|
59
test/regress_testutils.h
Normal file
59
test/regress_testutils.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Niels Provos and Nick Mathewson
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _TESTUTILS_H
|
||||
#define _TESTUTILS_H
|
||||
|
||||
struct regress_dns_server_table {
|
||||
const char *q;
|
||||
const char *anstype;
|
||||
const char *ans;
|
||||
int seen;
|
||||
};
|
||||
|
||||
struct evdns_server_port *
|
||||
regress_get_dnsserver(struct event_base *base,
|
||||
ev_uint16_t *portnum,
|
||||
evutil_socket_t *psock,
|
||||
evdns_request_callback_fn_type cb,
|
||||
void *arg);
|
||||
|
||||
/* Helper: return the port that a socket is bound on, in host order. */
|
||||
int regress_get_socket_port(evutil_socket_t fd);
|
||||
|
||||
/* used to look up pre-canned responses in a search table */
|
||||
void regress_dns_server_cb(
|
||||
struct evdns_server_request *req, void *data);
|
||||
|
||||
/* globally allocates a dns server that serves from a search table */
|
||||
int regress_dnsserver(struct event_base *base, ev_uint16_t *port,
|
||||
struct regress_dns_server_table *seach_table);
|
||||
|
||||
/* clean up the global dns server resources */
|
||||
void regress_clean_dnsserver(void);
|
||||
|
||||
#endif /* _TESTUTILS_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user