/*
* Copyright (c) 2003-2007 Niels Provos <provos@citi.umich.edu>
- * Copyright (c) 2007-2010 Niels Provos and Nick Mathewson
+ * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
#ifdef WIN32
#include <winsock2.h>
+#include <ws2tcpip.h>
#include <windows.h>
#endif
#include "event2/http.h"
#include "event2/buffer.h"
#include "event2/bufferevent.h"
+#include "event2/util.h"
#include "log-internal.h"
#include "util-internal.h"
#include "http-internal.h"
http_connect(const char *address, u_short port)
{
/* Stupid code for connecting */
-#ifdef WIN32
- struct hostent *he;
- struct sockaddr_in sin;
-#else
- struct addrinfo ai, *aitop;
+ struct evutil_addrinfo ai, *aitop;
char strport[NI_MAXSERV];
-#endif
+
struct sockaddr *sa;
int slen;
evutil_socket_t fd;
-#ifdef WIN32
- if (!(he = gethostbyname(address))) {
- event_warn("gethostbyname");
- }
- memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length);
- sin.sin_family = AF_INET;
- sin.sin_port = htons(port);
- slen = sizeof(struct sockaddr_in);
- sa = (struct sockaddr*)&sin;
-#else
memset(&ai, 0, sizeof(ai));
ai.ai_family = AF_INET;
ai.ai_socktype = SOCK_STREAM;
evutil_snprintf(strport, sizeof(strport), "%d", port);
- if (getaddrinfo(address, strport, &ai, &aitop) != 0) {
+ if (evutil_getaddrinfo(address, strport, &ai, &aitop) != 0) {
event_warn("getaddrinfo");
return (-1);
}
sa = aitop->ai_addr;
slen = aitop->ai_addrlen;
-#endif
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
#endif
}
-#ifndef WIN32
- freeaddrinfo(aitop);
-#endif
+ evutil_freeaddrinfo(aitop);
return (fd);
}
/* real NULL request */
http_request = "";
+ bufferevent_write(bev, http_request, strlen(http_request));
+
shutdown(fd, SHUT_WR);
timerclear(&tv);
tv.tv_usec = 10000;
static void
http_parse_uri_test(void *ptr)
{
+ const int nonconform = (ptr != NULL);
+ const unsigned parse_flags =
+ nonconform ? EVHTTP_URI_NONCONFORMANT : 0;
struct evhttp_uri *uri = NULL;
char url_tmp[4096];
+#define URI_PARSE(uri) \
+ evhttp_uri_parse_with_flags((uri), parse_flags)
#define TT_URI(want) do { \
char *ret = evhttp_uri_join(uri, url_tmp, sizeof(url_tmp)); \
/* bad URIs: parsing */
#define BAD(s) do { \
- if (evhttp_uri_parse(s) != NULL) \
+ if (URI_PARSE(s) != NULL) \
TT_FAIL(("Expected error parsing \"%s\"",s)); \
} while(0)
- BAD("http://www.test.com/ why hello");
- BAD("http://www.test.com/why-hello\x01");
- BAD("http://www.test.com/why-hello?\x01");
- BAD("http://www.test.com/why-hello#\x01");
+ /* Nonconformant URIs we can parse: parsing */
+#define NCF(s) do { \
+ uri = URI_PARSE(s); \
+ if (uri != NULL && !nonconform) { \
+ TT_FAIL(("Expected error parsing \"%s\"",s)); \
+ } else if (uri == NULL && nonconform) { \
+ TT_FAIL(("Couldn't parse nonconformant URI \"%s\"", \
+ s)); \
+ } \
+ if (uri) { \
+ tt_want(evhttp_uri_join(uri, url_tmp, \
+ sizeof(url_tmp))); \
+ evhttp_uri_free(uri); \
+ } \
+ } while(0)
+
+ NCF("http://www.test.com/ why hello");
+ NCF("http://www.test.com/why-hello\x01");
+ NCF("http://www.test.com/why-hello?\x01");
+ NCF("http://www.test.com/why-hello#\x01");
BAD("http://www.\x01.test.com/why-hello");
BAD("http://www.%7test.com/why-hello");
- BAD("http://www.test.com/why-hell%7o");
+ NCF("http://www.test.com/why-hell%7o");
BAD("h%3ttp://www.test.com/why-hello");
- BAD("http://www.test.com/why-hello%7");
- BAD("http://www.test.com/why-hell%7o");
- BAD("http://www.test.com/foo?ba%r");
- BAD("http://www.test.com/foo#ba%r");
+ NCF("http://www.test.com/why-hello%7");
+ NCF("http://www.test.com/why-hell%7o");
+ NCF("http://www.test.com/foo?ba%r");
+ NCF("http://www.test.com/foo#ba%r");
BAD("99:99/foo");
BAD("http://www.test.com:999x/");
BAD("http://www.test.com:x/");
tt_want(evhttp_uri_join(uri, NULL, sizeof(url_tmp))==NULL);
tt_want(evhttp_uri_join(uri, url_tmp, 0)==NULL);
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("mailto:foo@bar");
+ uri = URI_PARSE("mailto:foo@bar");
tt_want(uri != NULL);
tt_want(evhttp_uri_get_host(uri) == NULL);
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
evhttp_uri_free(uri);
/* Valid parsing */
- uri = evhttp_uri_parse("http://www.test.com/?q=t%33est");
+ uri = URI_PARSE("http://www.test.com/?q=t%33est");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
TT_URI("http://www.test.com/?q=t%33est");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("http://%77ww.test.com");
+ uri = URI_PARSE("http://%77ww.test.com");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "%77ww.test.com") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
TT_URI("http://%77ww.test.com");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("http://www.test.com?q=test");
+ uri = URI_PARSE("http://www.test.com?q=test");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
TT_URI("http://www.test.com?q=test");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("http://www.test.com#fragment");
+ uri = URI_PARSE("http://www.test.com#fragment");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
TT_URI("http://www.test.com#fragment");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("http://8000/");
+ uri = URI_PARSE("http://8000/");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "8000") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
TT_URI("http://8000/");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("http://:8000/");
+ uri = URI_PARSE("http://:8000/");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
TT_URI("http://:8000/");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("http://www.test.com:/"); /* empty port */
+ uri = URI_PARSE("http://www.test.com:/"); /* empty port */
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
tt_want_str_op(evhttp_uri_get_path(uri), ==, "/");
TT_URI("http://www.test.com/");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("http://www.test.com:"); /* empty port 2 */
+ uri = URI_PARSE("http://www.test.com:"); /* empty port 2 */
tt_want(strcmp(evhttp_uri_get_scheme(uri), "http") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "") == 0);
TT_URI("http://www.test.com");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("ftp://www.test.com/?q=test");
+ uri = URI_PARSE("ftp://www.test.com/?q=test");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "www.test.com") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
TT_URI("ftp://www.test.com/?q=test");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("ftp://[::1]:999/?q=test");
+ uri = URI_PARSE("ftp://[::1]:999/?q=test");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "[::1]") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
TT_URI("ftp://[::1]:999/?q=test");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("ftp://[ff00::127.0.0.1]/?q=test");
+ uri = URI_PARSE("ftp://[ff00::127.0.0.1]/?q=test");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "[ff00::127.0.0.1]") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
TT_URI("ftp://[ff00::127.0.0.1]/?q=test");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("ftp://[v99.not_(any:time)_soon]/?q=test");
+ uri = URI_PARSE("ftp://[v99.not_(any:time)_soon]/?q=test");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "ftp") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "[v99.not_(any:time)_soon]") == 0);
tt_want(strcmp(evhttp_uri_get_path(uri), "/") == 0);
TT_URI("ftp://[v99.not_(any:time)_soon]/?q=test");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
+ uri = URI_PARSE("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user:pass") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
TT_URI("scheme://user:pass@foo.com:42/?q=test&s=some+thing#fragment");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("scheme://user@foo.com/#fragment");
+ uri = URI_PARSE("scheme://user@foo.com/#fragment");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
tt_want(strcmp(evhttp_uri_get_userinfo(uri), "user") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
TT_URI("scheme://user@foo.com/#fragment");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("scheme://%75ser@foo.com/#frag@ment");
+ uri = URI_PARSE("scheme://%75ser@foo.com/#frag@ment");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "scheme") == 0);
tt_want(strcmp(evhttp_uri_get_userinfo(uri), "%75ser") == 0);
tt_want(strcmp(evhttp_uri_get_host(uri), "foo.com") == 0);
TT_URI("scheme://%75ser@foo.com/#frag@ment");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("file:///some/path/to/the/file");
+ uri = URI_PARSE("file:///some/path/to/the/file");
tt_want(strcmp(evhttp_uri_get_scheme(uri), "file") == 0);
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
tt_want(strcmp(evhttp_uri_get_host(uri), "") == 0);
TT_URI("file:///some/path/to/the/file");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("///some/path/to/the-file");
+ uri = URI_PARSE("///some/path/to/the-file");
tt_want(uri != NULL);
tt_want(evhttp_uri_get_scheme(uri) == NULL);
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
TT_URI("///some/path/to/the-file");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("/s:ome/path/to/the-file?q=99#fred");
+ uri = URI_PARSE("/s:ome/path/to/the-file?q=99#fred");
tt_want(uri != NULL);
tt_want(evhttp_uri_get_scheme(uri) == NULL);
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
TT_URI("/s:ome/path/to/the-file?q=99#fred");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("relative/path/with/co:lon");
+ uri = URI_PARSE("relative/path/with/co:lon");
tt_want(uri != NULL);
tt_want(evhttp_uri_get_scheme(uri) == NULL);
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
TT_URI("relative/path/with/co:lon");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("bob?q=99&q2=q?33#fr?ed");
+ uri = URI_PARSE("bob?q=99&q2=q?33#fr?ed");
tt_want(uri != NULL);
tt_want(evhttp_uri_get_scheme(uri) == NULL);
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
TT_URI("bob?q=99&q2=q?33#fr?ed");
evhttp_uri_free(uri);
- uri = evhttp_uri_parse("#fr?ed");
+ uri = URI_PARSE("#fr?ed");
tt_want(uri != NULL);
tt_want(evhttp_uri_get_scheme(uri) == NULL);
tt_want(evhttp_uri_get_userinfo(uri) == NULL);
tt_want(strcmp(evhttp_uri_get_fragment(uri), "fr?ed") == 0);
TT_URI("#fr?ed");
evhttp_uri_free(uri);
+#undef URI_PARSE
+#undef TT_URI
+#undef BAD
}
static void
}
+static void
+http_connection_fail_done(struct evhttp_request *req, void *arg)
+{
+ /* An ENETUNREACH error results in an unrecoverable
+ * evhttp_connection error (see evhttp_connection_fail()). The
+ * connection will be reset, and the user will be notified with a NULL
+ * req parameter. */
+ tt_assert(!req);
+
+ test_ok = 1;
+
+ end:
+ event_base_loopexit(arg, NULL);
+}
+
+/* Test unrecoverable evhttp_connection errors by generating an ENETUNREACH
+ * error on connection. */
+static void
+http_connection_fail_test(void *arg)
+{
+ struct basic_test_data *data = arg;
+ ev_uint16_t port = 0;
+ struct evhttp_connection *evcon = NULL;
+ struct evhttp_request *req = NULL;
+
+ exit_base = data->base;
+ test_ok = 0;
+
+ /* auto detect a port */
+ http = http_setup(&port, data->base);
+ evhttp_free(http);
+ http = NULL;
+
+ /* Pick an unroutable address. This administratively scoped multicast
+ * address should do when working with TCP. */
+ evcon = evhttp_connection_base_new(data->base, NULL, "239.10.20.30", 80);
+ tt_assert(evcon);
+
+ /*
+ * At this point, we want to schedule an HTTP GET request
+ * server using our make request method.
+ */
+
+ req = evhttp_request_new(http_connection_fail_done, data->base);
+ tt_assert(req);
+
+ if (evhttp_make_request(evcon, req, EVHTTP_REQ_GET, "/") == -1) {
+ tt_abort_msg("Couldn't make request");
+ }
+
+ event_base_dispatch(data->base);
+
+ tt_int_op(test_ok, ==, 1);
+
+ end:
+ if (evcon)
+ evhttp_connection_free(evcon);
+}
+
static void
http_connection_retry_done(struct evhttp_request *req, void *arg)
{
{ "bad_headers", http_bad_header_test, 0, NULL, NULL },
{ "parse_query", http_parse_query_test, 0, NULL, NULL },
{ "parse_uri", http_parse_uri_test, 0, NULL, NULL },
+ { "parse_uri_nc", http_parse_uri_test, 0, &basic_setup, (void*)"nc" },
{ "uriencode", http_uriencode_test, 0, NULL, NULL },
HTTP(basic),
HTTP(cancel),
HTTP(stream_in),
HTTP(stream_in_cancel),
+ HTTP(connection_fail),
HTTP(connection_retry),
HTTP(data_length_constraints),