Support asynchronous name resolution in Http::Client
[libs/net.git] / source / http / client.cpp
1 #include <msp/core/refptr.h>
2 #include <msp/net/resolve.h>
3 #include <msp/time/units.h>
4 #include "client.h"
5 #include "request.h"
6 #include "response.h"
7
8 using namespace std;
9
10 namespace Msp {
11 namespace Http {
12
13 Client::Client():
14         sock(0),
15         event_disp(0),
16         resolver(0),
17         resolve_listener(0),
18         resolve_tag(0),
19         user_agent("libmsphttp/0.1"),
20         request(0),
21         response(0)
22 { }
23
24 Client::~Client()
25 {
26         delete sock;
27         delete request;
28         delete response;
29 }
30
31 void Client::use_event_dispatcher(IO::EventDispatcher *ed)
32 {
33         if(event_disp && sock)
34                 event_disp->remove(*sock);
35         event_disp = ed;
36         if(event_disp && sock)
37                 event_disp->add(*sock);
38 }
39
40 void Client::use_resolver(Net::Resolver *r)
41 {
42         if(resolver)
43         {
44                 delete resolve_listener;
45                 resolve_listener = 0;
46         }
47
48         resolver = r;
49         if(resolver)
50                 resolve_listener = new ResolveListener(*this);
51 }
52
53 void Client::start_request(const Request &r)
54 {
55         if(request)
56                 throw client_busy();
57
58         delete sock;
59         sock = 0;
60
61         request = new Request(r);
62         if(!user_agent.empty())
63                 request->set_header("User-Agent", user_agent);
64
65         delete response;
66         response = 0;
67         in_buf.clear();
68
69         string host = r.get_header("Host");
70         if(host.find(':')==string::npos)
71                 host += ":80";
72         if(resolver)
73                 resolve_tag = resolver->resolve(host);
74         else
75         {
76                 RefPtr<Net::SockAddr> addr = Net::resolve(host);
77                 address_resolved(resolve_tag, *addr);
78         }
79 }
80
81 const Response *Client::get_url(const std::string &url)
82 {
83         start_request(Request::from_url(url));
84         wait_response();
85         return response;
86 }
87
88 void Client::tick()
89 {
90         if(!request)
91                 return;
92
93         while(IO::PollEvent ev = IO::poll(*sock, sock->get_events(), Time::zero))
94                 sock->event(ev);
95
96         if(response && response->is_complete())
97         {
98                 signal_response_complete.emit(*response);
99
100                 delete sock;
101                 sock = 0;
102                 delete request;
103                 request = 0;
104         }
105 }
106
107 void Client::wait_response()
108 {
109         while(request && (!response || !response->is_complete()))
110                 tick();
111 }
112
113 void Client::abort()
114 {
115         delete sock;
116         sock = 0;
117         delete request;
118         request = 0;
119 }
120
121 void Client::address_resolved(unsigned tag, const Net::SockAddr &addr)
122 {
123         if(tag!=resolve_tag)
124                 return;
125         resolve_tag = 0;
126
127         sock = new Net::StreamSocket(addr.get_family());
128         sock->set_block(false);
129
130         sock->signal_data_available.connect(sigc::mem_fun(this, &Client::data_available));
131         sock->signal_connect_finished.connect(sigc::mem_fun(this, &Client::connect_finished));
132         if(event_disp)
133                 event_disp->add(*sock);
134
135         sock->connect(addr);
136 }
137
138 void Client::resolve_failed(unsigned tag, const exception &err)
139 {
140         if(tag!=resolve_tag)
141                 return;
142         resolve_tag = 0;
143
144         signal_socket_error.emit(err);
145
146         delete request;
147         request = 0;
148 }
149
150 void Client::connect_finished(const exception *err)
151 {
152         if(err)
153         {
154                 signal_socket_error.emit(*err);
155
156                 delete request;
157                 request = 0;
158         }
159         else
160                 sock->write(request->str());
161 }
162
163 void Client::data_available()
164 {
165         char rbuf[4096];
166         unsigned len;
167         try
168         {
169                 len = sock->read(rbuf, sizeof(rbuf));
170         }
171         catch(const exception &e)
172         {
173                 signal_socket_error.emit(e);
174                 return;
175         }
176
177         if(!len)
178                 return;
179         in_buf.append(rbuf, len);
180
181         if(!response)
182         {
183                 if(in_buf.find("\r\n\r\n")!=string::npos || in_buf.find("\n\n")!=string::npos)
184                 {
185                         response = new Response(Response::parse(in_buf));
186                         response->set_user_data(request->get_user_data());
187                         in_buf = string();
188                 }
189         }
190         else
191         {
192                 len = response->parse_content(in_buf);
193                 in_buf.erase(0, len);
194         }
195
196         if(response && response->is_complete())
197         {
198                 signal_response_complete.emit(*response);
199
200                 delete request;
201                 request = 0;
202         }
203 }
204
205
206 Client::ResolveListener::ResolveListener(Client &c):
207         client(c)
208 {
209         client.resolver->signal_address_resolved.connect(sigc::mem_fun(this, &ResolveListener::address_resolved));
210         client.resolver->signal_resolve_failed.connect(sigc::mem_fun(this, &ResolveListener::resolve_failed));
211 }
212
213 void Client::ResolveListener::address_resolved(unsigned tag, const Net::SockAddr &addr)
214 {
215         client.address_resolved(tag, addr);
216 }
217
218 void Client::ResolveListener::resolve_failed(unsigned tag, const exception &err)
219 {
220         client.resolve_failed(tag, err);
221 }
222
223 } // namespace Http
224 } // namespace Msp