]> git.tdb.fi Git - libs/net.git/blobdiff - source/http/client.cpp
Add a dynamic receiver class for more flexible packet handling
[libs/net.git] / source / http / client.cpp
index 3d14b04e59d45b22889d8c699eab428829a16ea5..1c4c953e5d5d5fb31b3243e13a2fa6d728d12a33 100644 (file)
@@ -1,7 +1,7 @@
-#include <msp/core/refptr.h>
-#include <msp/net/resolve.h>
-#include <msp/time/units.h>
 #include "client.h"
+#include <msp/core/except.h>
+#include <msp/net/resolve.h>
+#include <msp/time/timedelta.h>
 #include "request.h"
 #include "response.h"
 
@@ -10,19 +10,8 @@ using namespace std;
 namespace Msp {
 namespace Http {
 
-Client::Client():
-       sock(0),
-       event_disp(0),
-       user_agent("libmsphttp/0.1"),
-       request(0),
-       response(0)
-{ }
-
 Client::~Client()
 {
-       delete sock;
-       delete request;
-       delete response;
 }
 
 void Client::use_event_dispatcher(IO::EventDispatcher *ed)
@@ -34,41 +23,47 @@ void Client::use_event_dispatcher(IO::EventDispatcher *ed)
                event_disp->add(*sock);
 }
 
-void Client::start_request(const Request &r)
+void Client::use_resolver(Net::Resolver *r)
 {
-       if(request)
-               throw client_busy();
+       if(resolver)
+               resolve_listener.reset();
 
-       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);
+       resolver = r;
+       if(resolver)
+               resolve_listener = make_unique<ResolveListener>(*this);
+}
 
-       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);
+void Client::start_request(const Request &r)
+{
+       if(request)
+               throw invalid_state("already processing a request");
 
-       sock->connect(*addr);
+       sock.reset();
 
-       request = new Request(r);
+       request = make_unique<Request>(r);
        if(!user_agent.empty())
                request->set_header("User-Agent", user_agent);
 
-       delete response;
-       response = 0;
+       response.reset();
        in_buf.clear();
+
+       string host = r.get_header("Host");
+       if(host.find(':')==string::npos)
+               host += ":80";
+       if(resolver)
+               resolve_tag = resolver->resolve(host);
+       else
+       {
+               unique_ptr<Net::SockAddr> addr(Net::resolve(host));
+               address_resolved(resolve_tag, *addr);
+       }
 }
 
 const Response *Client::get_url(const std::string &url)
 {
        start_request(Request::from_url(url));
        wait_response();
-       return response;
+       return response.get();
 }
 
 void Client::tick()
@@ -83,10 +78,8 @@ void Client::tick()
        {
                signal_response_complete.emit(*response);
 
-               delete sock;
-               sock = 0;
-               delete request;
-               request = 0;
+               sock.reset();
+               request.reset();
        }
 }
 
@@ -98,23 +91,64 @@ void Client::wait_response()
 
 void Client::abort()
 {
-       delete sock;
-       sock = 0;
-       delete request;
-       request = 0;
+       sock.reset();
+       request.reset();
+}
+
+void Client::address_resolved(unsigned tag, const Net::SockAddr &addr)
+{
+       if(tag!=resolve_tag)
+               return;
+       resolve_tag = 0;
+
+       sock = make_unique<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;
+
+       request.reset();
+
+       if(signal_socket_error.empty())
+               throw err;
+       signal_socket_error.emit(err);
 }
 
 void Client::connect_finished(const exception *err)
 {
        if(err)
        {
-               signal_socket_error.emit(err);
+               request.reset();
 
-               delete request;
-               request = 0;
+               if(signal_socket_error.empty())
+                       throw *err;
+               signal_socket_error.emit(*err);
        }
        else
-               sock->write(request->str());
+       {
+               try
+               {
+                       sock->write(request->str());
+               }
+               catch(const exception &e)
+               {
+                       if(signal_socket_error.empty())
+                               throw;
+                       signal_socket_error.emit(e);
+                       return;
+               }
+       }
 }
 
 void Client::data_available()
@@ -127,7 +161,9 @@ void Client::data_available()
        }
        catch(const exception &e)
        {
-               signal_socket_error.emit(&e);
+               if(signal_socket_error.empty())
+                       throw;
+               signal_socket_error.emit(e);
                return;
        }
 
@@ -139,7 +175,7 @@ void Client::data_available()
        {
                if(in_buf.find("\r\n\r\n")!=string::npos || in_buf.find("\n\n")!=string::npos)
                {
-                       response = new Response(Response::parse(in_buf));
+                       response = make_unique<Response>(Response::parse(in_buf));
                        response->set_user_data(request->get_user_data());
                        in_buf = string();
                }
@@ -154,10 +190,27 @@ void Client::data_available()
        {
                signal_response_complete.emit(*response);
 
-               delete request;
-               request = 0;
+               request.reset();
        }
 }
 
+
+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