From 1f4577adb91ac65f851e09f9c39b5260c236a551 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Thu, 23 Jan 2014 00:29:09 +0900 Subject: [PATCH] nghttp: Use getaddrnfo directly instead of using evdns_base evdns_base uses /etc/resolve.conf for *nix like systems, but all platforms don't have the file (e.g., android device). For such platforms, address resolution fails. To fix this problem we use getaddrinfo() directly. --- src/nghttp.cc | 72 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/src/nghttp.cc b/src/nghttp.cc index 04551ea1..18fc2796 100644 --- a/src/nghttp.cc +++ b/src/nghttp.cc @@ -58,7 +58,6 @@ #include #include #include -#include #include @@ -435,6 +434,7 @@ struct HttpClient { // for 1 resource. std::set path_cache; std::string scheme; + std::string host; std::string hostport; // Used for parse the HTTP upgrade response from server std::unique_ptr htp; @@ -442,11 +442,12 @@ struct HttpClient { nghttp2_session *session; const nghttp2_session_callbacks *callbacks; event_base *evbase; - evdns_base *dnsbase; SSL_CTX *ssl_ctx; SSL *ssl; bufferevent *bev; event *settings_timerev; + addrinfo *addrs; + addrinfo *next_addr; // The number of completed requests, including failed ones. size_t complete; // The length of settings_payload @@ -465,11 +466,12 @@ struct HttpClient { : session(nullptr), callbacks(callbacks), evbase(evbase), - dnsbase(evdns_base_new(evbase, 1)), ssl_ctx(ssl_ctx), ssl(nullptr), bev(nullptr), settings_timerev(nullptr), + addrs(nullptr), + next_addr(nullptr), complete(0), settings_payloadlen(0), state(STATE_IDLE), @@ -487,9 +489,27 @@ struct HttpClient { return config.upgrade && scheme == "http"; } - int initiate_connection(const std::string& host, uint16_t port) + int resolve_host(const std::string& host, uint16_t port) { int rv; + this->host = host; + rv = getaddrinfo(host.c_str(), util::utos(port).c_str(), nullptr, &addrs); + if(rv != 0) { + std::cerr << "getaddrinfo() failed: " + << gai_strerror(rv) << std::endl; + return -1; + } + if(addrs == nullptr) { + std::cerr << "No address returned" << std::endl; + return -1; + } + next_addr = addrs; + return 0; + } + + int initiate_connection() + { + int rv = 0; if(ssl_ctx) { // We are establishing TLS connection. ssl = SSL_new(ssl_ctx); @@ -523,8 +543,14 @@ struct HttpClient { } else { bev = bufferevent_socket_new(evbase, -1, BEV_OPT_DEFER_CALLBACKS); } - rv = bufferevent_socket_connect_hostname - (bev, dnsbase, AF_UNSPEC, host.c_str(), port); + while(next_addr) { + rv = bufferevent_socket_connect + (bev, next_addr->ai_addr, next_addr->ai_addrlen); + ++next_addr; + if(rv == 0) { + break; + } + } if(rv != 0) { return -1; } @@ -560,10 +586,6 @@ struct HttpClient { bufferevent_free(bev); bev = nullptr; } - if(dnsbase) { - evdns_base_free(dnsbase, 1); - dnsbase = nullptr; - } if(settings_timerev) { event_free(settings_timerev); settings_timerev = nullptr; @@ -576,6 +598,11 @@ struct HttpClient { shutdown(fd, SHUT_WR); close(fd); } + if(addrs) { + freeaddrinfo(addrs); + addrs = nullptr; + next_addr = nullptr; + } } int on_upgrade_connect() @@ -1379,11 +1406,18 @@ void eventcb(bufferevent *bev, short events, void *ptr) client->disconnect(); return; } - } else if(events & BEV_EVENT_EOF) { - std::cerr << "EOF" << std::endl; - client->disconnect(); return; - } else if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) { + } + if(events & BEV_EVENT_EOF) { + std::cerr << "EOF" << std::endl; + auto state = client->state; + client->disconnect(); + if(state == STATE_IDLE) { + client->initiate_connection(); + } + return; + } + if(events & (BEV_EVENT_ERROR | BEV_EVENT_TIMEOUT)) { if(events & BEV_EVENT_ERROR) { if(client->state == STATE_IDLE) { std::cerr << "Could not connect to the host" << std::endl; @@ -1393,8 +1427,11 @@ void eventcb(bufferevent *bev, short events, void *ptr) } else { std::cerr << "Timeout" << std::endl; } - // TODO Needs disconnect()? + auto state = client->state; client->disconnect(); + if(state == STATE_IDLE) { + client->initiate_connection(); + } return; } } @@ -1482,7 +1519,10 @@ int communicate(const std::string& scheme, const std::string& host, } } client.update_hostport(); - if(client.initiate_connection(host, port) != 0) { + if(client.resolve_host(host, port) != 0) { + goto fin; + } + if(client.initiate_connection() != 0) { goto fin; } event_base_loop(evbase, 0);