Support asynchronous name resolution in Http::Client
authorMikko Rasa <tdb@tdb.fi>
Wed, 14 Sep 2016 10:51:56 +0000 (13:51 +0300)
committerMikko Rasa <tdb@tdb.fi>
Wed, 14 Sep 2016 10:51:56 +0000 (13:51 +0300)
source/http/client.cpp
source/http/client.h

index 80815e0c9f6c33a5ced29e65cb6c82674c8b8b97..6f221810c922a78f85289934b36ce4d6b71b2100 100644 (file)
@@ -13,6 +13,9 @@ namespace Http {
 Client::Client():
        sock(0),
        event_disp(0),
+       resolver(0),
+       resolve_listener(0),
+       resolve_tag(0),
        user_agent("libmsphttp/0.1"),
        request(0),
        response(0)
@@ -34,26 +37,26 @@ void Client::use_event_dispatcher(IO::EventDispatcher *ed)
                event_disp->add(*sock);
 }
 
+void Client::use_resolver(Net::Resolver *r)
+{
+       if(resolver)
+       {
+               delete resolve_listener;
+               resolve_listener = 0;
+       }
+
+       resolver = r;
+       if(resolver)
+               resolve_listener = new ResolveListener(*this);
+}
+
 void Client::start_request(const Request &r)
 {
        if(request)
                throw client_busy();
 
-       string host = r.get_header("Host");
-       if(host.find(':')==string::npos)
-               host += ":80";
-       RefPtr<Net::SockAddr> addr = Net::resolve(host);
-
        delete sock;
-       sock = new Net::StreamSocket(addr->get_family());
-       sock->set_block(false);
-
-       sock->signal_data_available.connect(sigc::mem_fun(this, &Client::data_available));
-       sock->signal_connect_finished.connect(sigc::mem_fun(this, &Client::connect_finished));
-       if(event_disp)
-               event_disp->add(*sock);
-
-       sock->connect(*addr);
+       sock = 0;
 
        request = new Request(r);
        if(!user_agent.empty())
@@ -62,6 +65,17 @@ void Client::start_request(const Request &r)
        delete response;
        response = 0;
        in_buf.clear();
+
+       string host = r.get_header("Host");
+       if(host.find(':')==string::npos)
+               host += ":80";
+       if(resolver)
+               resolve_tag = resolver->resolve(host);
+       else
+       {
+               RefPtr<Net::SockAddr> addr = Net::resolve(host);
+               address_resolved(resolve_tag, *addr);
+       }
 }
 
 const Response *Client::get_url(const std::string &url)
@@ -104,6 +118,35 @@ void Client::abort()
        request = 0;
 }
 
+void Client::address_resolved(unsigned tag, const Net::SockAddr &addr)
+{
+       if(tag!=resolve_tag)
+               return;
+       resolve_tag = 0;
+
+       sock = new Net::StreamSocket(addr.get_family());
+       sock->set_block(false);
+
+       sock->signal_data_available.connect(sigc::mem_fun(this, &Client::data_available));
+       sock->signal_connect_finished.connect(sigc::mem_fun(this, &Client::connect_finished));
+       if(event_disp)
+               event_disp->add(*sock);
+
+       sock->connect(addr);
+}
+
+void Client::resolve_failed(unsigned tag, const exception &err)
+{
+       if(tag!=resolve_tag)
+               return;
+       resolve_tag = 0;
+
+       signal_socket_error.emit(err);
+
+       delete request;
+       request = 0;
+}
+
 void Client::connect_finished(const exception *err)
 {
        if(err)
@@ -159,5 +202,23 @@ void Client::data_available()
        }
 }
 
+
+Client::ResolveListener::ResolveListener(Client &c):
+       client(c)
+{
+       client.resolver->signal_address_resolved.connect(sigc::mem_fun(this, &ResolveListener::address_resolved));
+       client.resolver->signal_resolve_failed.connect(sigc::mem_fun(this, &ResolveListener::resolve_failed));
+}
+
+void Client::ResolveListener::address_resolved(unsigned tag, const Net::SockAddr &addr)
+{
+       client.address_resolved(tag, addr);
+}
+
+void Client::ResolveListener::resolve_failed(unsigned tag, const exception &err)
+{
+       client.resolve_failed(tag, err);
+}
+
 } // namespace Http
 } // namespace Msp
index 37acf0cfe0e3bb08d1483520e9a3cd7bc557dc76..ebb73216a4eacfbbdfce4cc511e86f1e771395ef 100644 (file)
@@ -4,6 +4,7 @@
 #include <string>
 #include <sigc++/signal.h>
 #include <msp/io/eventdispatcher.h>
+#include <msp/net/resolve.h>
 #include <msp/net/streamsocket.h>
 
 namespace Msp {
@@ -26,8 +27,21 @@ public:
        sigc::signal<void, const std::exception &> signal_socket_error;
 
 private:
+       struct ResolveListener: public sigc::trackable
+       {
+               Client &client;
+
+               ResolveListener(Client &);
+
+               void address_resolved(unsigned, const Net::SockAddr &);
+               void resolve_failed(unsigned, const std::exception &);
+       };
+
        Net::StreamSocket *sock;
        IO::EventDispatcher *event_disp;
+       Net::Resolver *resolver;
+       ResolveListener *resolve_listener;
+       unsigned resolve_tag;
        std::string user_agent;
        Request *request;
        Response *response;
@@ -40,6 +54,7 @@ public:
        ~Client();
 
        void use_event_dispatcher(IO::EventDispatcher *);
+       void use_resolver(Net::Resolver *);
        void set_user_agent(const std::string &);
        void start_request(const Request &);
        const Response *get_url(const std::string &);
@@ -49,6 +64,8 @@ public:
        const Request *get_request() const { return request; }
        const Response *get_response() const { return response; }
 private:
+       void address_resolved(unsigned, const Net::SockAddr &);
+       void resolve_failed(unsigned, const std::exception &);
        void connect_finished(const std::exception *);
        void data_available();
 };