]> git.tdb.fi Git - libs/net.git/blobdiff - source/http/client.cpp
Prepare for assimilation into mspnet
[libs/net.git] / source / http / client.cpp
diff --git a/source/http/client.cpp b/source/http/client.cpp
new file mode 100644 (file)
index 0000000..0729544
--- /dev/null
@@ -0,0 +1,164 @@
+#include <msp/core/except.h>
+#include <msp/core/refptr.h>
+#include <msp/net/resolve.h>
+#include <msp/time/units.h>
+#include "client.h"
+#include "request.h"
+#include "response.h"
+
+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)
+{
+       if(event_disp && sock)
+               event_disp->remove(*sock);
+       event_disp = ed;
+       if(event_disp && sock)
+               event_disp->add(*sock);
+}
+
+void Client::start_request(const Request &r)
+{
+       if(request)
+               throw InvalidState("Already processing a request");
+
+       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);
+
+       request = new Request(r);
+       if(!user_agent.empty())
+               request->set_header("User-Agent", user_agent);
+
+       delete response;
+       response = 0;
+       in_buf.clear();
+}
+
+const Response *Client::get_url(const std::string &url)
+{
+       start_request(Request::from_url(url));
+       wait_response();
+       return response;
+}
+
+void Client::tick()
+{
+       if(!request)
+               return;
+
+       while(IO::PollEvent ev = IO::poll(*sock, sock->get_events(), Time::zero))
+               sock->event(ev);
+
+       if(response && response->is_complete())
+       {
+               signal_response_complete.emit(*response);
+
+               delete sock;
+               sock = 0;
+               delete request;
+               request = 0;
+       }
+}
+
+void Client::wait_response()
+{
+       while(request && (!response || !response->is_complete()))
+               tick();
+}
+
+void Client::abort()
+{
+       delete sock;
+       sock = 0;
+       delete request;
+       request = 0;
+}
+
+void Client::connect_finished(const exception *err)
+{
+       if(err)
+       {
+               signal_socket_error.emit(err);
+
+               delete request;
+               request = 0;
+       }
+       else
+               sock->write(request->str());
+}
+
+void Client::data_available()
+{
+       char rbuf[4096];
+       unsigned len;
+       try
+       {
+               len = sock->read(rbuf, sizeof(rbuf));
+       }
+       catch(const exception &e)
+       {
+               signal_socket_error.emit(&e);
+               return;
+       }
+
+       if(!len)
+               return;
+       in_buf.append(rbuf, len);
+
+       if(!response)
+       {
+               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->set_user_data(request->get_user_data());
+                       in_buf = string();
+               }
+       }
+       else
+       {
+               len = response->parse_content(in_buf);
+               in_buf.erase(0, len);
+       }
+
+       if(response && response->is_complete())
+       {
+               signal_response_complete.emit(*response);
+
+               delete request;
+               request = 0;
+       }
+}
+
+} // namespace Http
+} // namespace Msp