]> git.tdb.fi Git - libs/net.git/blob - source/client.cpp
Don't let exceptions from the socket fall out of Client::data_available
[libs/net.git] / source / client.cpp
1 #include <msp/core/except.h>
2 #include <msp/core/refptr.h>
3 #include <msp/net/resolve.h>
4 #include <msp/time/units.h>
5 #include "client.h"
6 #include "request.h"
7 #include "response.h"
8
9 using namespace std;
10
11 namespace Msp {
12 namespace Http {
13
14 Client::Client():
15         sock(0),
16         event_disp(0),
17         user_agent("libmsphttp/0.1"),
18         request(0),
19         response(0)
20 { }
21
22 Client::~Client()
23 {
24         delete sock;
25         delete request;
26         delete response;
27 }
28
29 void Client::use_event_dispatcher(IO::EventDispatcher *ed)
30 {
31         if(event_disp && sock)
32                 event_disp->remove(*sock);
33         event_disp = ed;
34         if(event_disp && sock)
35                 event_disp->add(*sock);
36 }
37
38 void Client::start_request(const Request &r)
39 {
40         if(request)
41                 throw InvalidState("Already processing a request");
42
43         string host = r.get_header("Host");
44         if(host.find(':')==string::npos)
45                 host += ":80";
46         RefPtr<Net::SockAddr> addr = Net::resolve(host);
47
48         delete sock;
49         sock = new Net::StreamSocket(addr->get_family());
50         sock->set_block(false);
51
52         sock->signal_data_available.connect(sigc::mem_fun(this, &Client::data_available));
53         sock->signal_connect_finished.connect(sigc::mem_fun(this, &Client::connect_finished));
54         if(event_disp)
55                 event_disp->add(*sock);
56
57         sock->connect(*addr);
58
59         request = new Request(r);
60         if(!user_agent.empty())
61                 request->set_header("User-Agent", user_agent);
62
63         delete response;
64         response = 0;
65         in_buf.clear();
66 }
67
68 const Response *Client::get_url(const std::string &url)
69 {
70         start_request(Request::from_url(url));
71         wait_response();
72         return response;
73 }
74
75 void Client::tick()
76 {
77         if(!request)
78                 return;
79
80         while(IO::PollEvent ev = IO::poll(*sock, sock->get_events(), Time::zero))
81                 sock->event(ev);
82
83         if(response && response->is_complete())
84         {
85                 signal_response_complete.emit(*response);
86
87                 delete sock;
88                 sock = 0;
89                 delete request;
90                 request = 0;
91         }
92 }
93
94 void Client::wait_response()
95 {
96         while(request && (!response || !response->is_complete()))
97                 tick();
98 }
99
100 void Client::abort()
101 {
102         delete sock;
103         sock = 0;
104         delete request;
105         request = 0;
106 }
107
108 void Client::connect_finished(int err)
109 {
110         if(err)
111         {
112                 signal_socket_error.emit(err);
113
114                 sock->close();
115                 delete request;
116                 request = 0;
117         }
118         else
119                 sock->write(request->str());
120 }
121
122 void Client::data_available()
123 {
124         char rbuf[4096];
125         unsigned len;
126         try
127         {
128                 len = sock->read(rbuf, sizeof(rbuf));
129         }
130         catch(const SystemError &e)
131         {
132                 signal_socket_error.emit(e.get_error_code());
133                 return;
134         }
135
136         if(!len)
137                 return;
138         in_buf.append(rbuf, len);
139
140         if(!response)
141         {
142                 if(in_buf.find("\r\n\r\n")!=string::npos || in_buf.find("\n\n")!=string::npos)
143                 {
144                         response = new Response(Response::parse(in_buf));
145                         response->set_user_data(request->get_user_data());
146                         in_buf = string();
147                 }
148         }
149         else
150         {
151                 len = response->parse_content(in_buf);
152                 in_buf.erase(0, len);
153         }
154
155         if(response && response->is_complete())
156         {
157                 signal_response_complete.emit(*response);
158
159                 sock->close();
160                 delete request;
161                 request = 0;
162         }
163 }
164
165 } // namespace Http
166 } // namespace Msp