]> git.mxchange.org Git - simgear.git/blob - simgear/io/HTTPRequest.cxx
Trying to debug HTTP timeouts
[simgear.git] / simgear / io / HTTPRequest.cxx
1 // Copyright (C) 2011  James Turner <zakalawe@mac.com>
2 // Copyright (C) 2013  Thomas Geymayer <tomgey@gmail.com>
3 //
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Library General Public
6 // License as published by the Free Software Foundation; either
7 // version 2 of the License, or (at your option) any later version.
8 //
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 // Library General Public License for more details.
13 //
14 // You should have received a copy of the GNU Library General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
17
18 #include "HTTPRequest.hxx"
19
20 #include <simgear/compiler.h>
21 #include <simgear/debug/logstream.hxx>
22 #include <simgear/misc/strutils.hxx>
23 #include <simgear/props/props_io.hxx>
24 #include <simgear/structure/exception.hxx>
25
26 namespace simgear
27 {
28 namespace HTTP
29 {
30
31 extern const int DEFAULT_HTTP_PORT;
32
33 //------------------------------------------------------------------------------
34 Request::Request(const std::string& url, const std::string method):
35   _method(method),
36   _url(url),
37   _responseVersion(HTTP_VERSION_UNKNOWN),
38   _responseStatus(0),
39   _responseLength(0),
40   _receivedBodyBytes(0),
41   _ready_state(UNSENT),
42   _willClose(false)
43 {
44
45 }
46
47 //------------------------------------------------------------------------------
48 Request::~Request()
49 {
50
51 }
52
53 //------------------------------------------------------------------------------
54 Request* Request::done(const Callback& cb)
55 {
56   if( _ready_state == DONE )
57     cb(this);
58   else
59     _cb_done.push_back(cb);
60
61   return this;
62 }
63
64 //------------------------------------------------------------------------------
65 Request* Request::fail(const Callback& cb)
66 {
67   if( _ready_state == FAILED )
68     cb(this);
69   else
70     _cb_fail.push_back(cb);
71
72   return this;
73 }
74
75 //------------------------------------------------------------------------------
76 Request* Request::always(const Callback& cb)
77 {
78   if( isComplete() )
79     cb(this);
80   else
81     _cb_always.push_back(cb);
82
83   return this;
84 }
85
86 //------------------------------------------------------------------------------
87 void Request::setBodyData( const std::string& data,
88                            const std::string& type )
89 {
90   _request_data = data;
91   _request_media_type = type;
92
93   if( !data.empty() && _method == "GET" )
94     _method = "POST";
95 }
96
97 //----------------------------------------------------------------------------
98 void Request::setBodyData(const SGPropertyNode* data)
99 {
100   if( !data )
101     setBodyData("");
102
103   std::stringstream buf;
104   writeProperties(buf, data, true);
105
106   setBodyData(buf.str(), "application/xml");
107 }
108
109 //------------------------------------------------------------------------------
110 void Request::setUrl(const std::string& url)
111 {
112   _url = url;
113 }
114
115 //------------------------------------------------------------------------------
116 void Request::requestStart()
117 {
118   setReadyState(OPENED);
119 }
120
121 //------------------------------------------------------------------------------
122 Request::HTTPVersion decodeHTTPVersion(const std::string& v)
123 {
124   if( v == "HTTP/1.1" ) return Request::HTTP_1_1;
125   if( v == "HTTP/1.0" ) return Request::HTTP_1_0;
126   if( strutils::starts_with(v, "HTTP/0.") ) return Request::HTTP_0_x;
127   return Request::HTTP_VERSION_UNKNOWN;
128 }
129
130 //------------------------------------------------------------------------------
131 void Request::responseStart(const std::string& r)
132 {
133     const int maxSplit = 2; // HTTP/1.1 nnn reason-string
134     string_list parts = strutils::split(r, NULL, maxSplit);
135     if (parts.size() != 3) {
136         throw sg_io_exception("bad HTTP response:" + r);
137     }
138
139     _responseVersion = decodeHTTPVersion(parts[0]);
140     _responseStatus = strutils::to_int(parts[1]);
141     _responseReason = parts[2];
142
143     setReadyState(STATUS_RECEIVED);
144 }
145
146 //------------------------------------------------------------------------------
147 void Request::responseHeader(const std::string& key, const std::string& value)
148 {
149   if( key == "connection" ) {
150     _willClose = (value.find("close") != std::string::npos);
151   } else if (key == "content-length") {
152     int sz = strutils::to_int(value);
153     setResponseLength(sz);
154   }
155
156   _responseHeaders[key] = value;
157 }
158
159 //------------------------------------------------------------------------------
160 void Request::responseHeadersComplete()
161 {
162   setReadyState(HEADERS_RECEIVED);
163 }
164
165 //------------------------------------------------------------------------------
166 void Request::responseComplete()
167 {
168   if( !isComplete() )
169     setReadyState(DONE);
170 }
171
172 //------------------------------------------------------------------------------
173 void Request::gotBodyData(const char* s, int n)
174 {
175   setReadyState(LOADING);
176 }
177
178 //------------------------------------------------------------------------------
179 void Request::onDone()
180 {
181
182 }
183
184 //------------------------------------------------------------------------------
185 void Request::onFail()
186 {
187   SG_LOG
188   (
189     SG_IO,
190     SG_INFO,
191     "request failed:" << url() << " : "
192                       << responseCode() << "/" << responseReason()
193   );
194 }
195
196 //------------------------------------------------------------------------------
197 void Request::onAlways()
198 {
199
200 }
201
202 //------------------------------------------------------------------------------
203 void Request::processBodyBytes(const char* s, int n)
204 {
205   _receivedBodyBytes += n;
206   gotBodyData(s, n);
207 }
208
209 //------------------------------------------------------------------------------
210 std::string Request::scheme() const
211 {
212     int firstColon = url().find(":");
213     if (firstColon > 0) {
214         return url().substr(0, firstColon);
215     }
216
217     return ""; // couldn't parse scheme
218 }
219
220 //------------------------------------------------------------------------------
221 std::string Request::path() const
222 {
223     std::string u(url());
224     int schemeEnd = u.find("://");
225     if (schemeEnd < 0) {
226         return ""; // couldn't parse scheme
227     }
228
229     int hostEnd = u.find('/', schemeEnd + 3);
230     if (hostEnd < 0) {
231 // couldn't parse host, or URL looks like 'http://foo.com' (no trailing '/')
232 // fixup to root resource path: '/'
233         return "/";
234     }
235
236     int query = u.find('?', hostEnd + 1);
237     if (query < 0) {
238         // all remainder of URL is path
239         return u.substr(hostEnd);
240     }
241
242     return u.substr(hostEnd, query - hostEnd);
243 }
244
245 //------------------------------------------------------------------------------
246 std::string Request::query() const
247 {
248   std::string u(url());
249   int query = u.find('?');
250   if (query < 0) {
251     return "";  //no query string found
252   }
253
254   return u.substr(query);   //includes question mark
255 }
256
257 //------------------------------------------------------------------------------
258 std::string Request::host() const
259 {
260   std::string hp(hostAndPort());
261   int colonPos = hp.find(':');
262   if (colonPos >= 0) {
263       return hp.substr(0, colonPos); // trim off the colon and port
264   } else {
265       return hp; // no port specifier
266   }
267 }
268
269 //------------------------------------------------------------------------------
270 unsigned short Request::port() const
271 {
272   std::string hp(hostAndPort());
273   int colonPos = hp.find(':');
274   if (colonPos >= 0) {
275       return (unsigned short) strutils::to_int(hp.substr(colonPos + 1));
276   } else {
277       return DEFAULT_HTTP_PORT;
278   }
279 }
280
281 //------------------------------------------------------------------------------
282 std::string Request::hostAndPort() const
283 {
284   std::string u(url());
285   int schemeEnd = u.find("://");
286   if (schemeEnd < 0) {
287       return ""; // couldn't parse scheme
288   }
289
290   int hostEnd = u.find('/', schemeEnd + 3);
291   if (hostEnd < 0) { // all remainder of URL is host
292       return u.substr(schemeEnd + 3);
293   }
294
295   return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
296 }
297
298 //------------------------------------------------------------------------------
299 std::string Request::responseMime() const
300 {
301   std::string content_type = _responseHeaders.get("content-type");
302   if( content_type.empty() )
303     return "application/octet-stream";
304
305   return content_type.substr(0, content_type.find(';'));
306 }
307
308 //------------------------------------------------------------------------------
309 void Request::setResponseLength(unsigned int l)
310 {
311   _responseLength = l;
312 }
313
314 //------------------------------------------------------------------------------
315 unsigned int Request::responseLength() const
316 {
317   // if the server didn't supply a content length, use the number
318   // of bytes we actually received (so far)
319   if( (_responseLength == 0) && (_receivedBodyBytes > 0) )
320     return _receivedBodyBytes;
321
322   return _responseLength;
323 }
324
325 //------------------------------------------------------------------------------
326 void Request::setFailure(int code, const std::string& reason)
327 {
328     SG_LOG(SG_IO, SG_WARN, "HTTP request: set failure:" << code << " reason " << reason);
329   _responseStatus = code;
330   _responseReason = reason;
331
332   if( !isComplete() )
333     setReadyState(FAILED);
334 }
335
336 //------------------------------------------------------------------------------
337 void Request::setReadyState(ReadyState state)
338 {
339   _ready_state = state;
340   if( state == DONE )
341   {
342     // Finish C++ part of request to ensure everything is finished (for example
343     // files and streams are closed) before calling any callback (possibly using
344     // such files)
345     onDone();
346     onAlways();
347
348     _cb_done(this);
349   }
350   else if( state == FAILED )
351   {
352     onFail();
353     onAlways();
354
355     _cb_fail(this);
356   }
357   else
358     return;
359
360   _cb_always(this);
361 }
362
363 //------------------------------------------------------------------------------
364 void Request::abort()
365 {
366   abort("Request aborted.");
367 }
368
369 //----------------------------------------------------------------------------
370 void Request::abort(const std::string& reason)
371 {
372   setFailure(-1, reason);
373   _willClose = true;
374 }
375
376 //------------------------------------------------------------------------------
377 bool Request::closeAfterComplete() const
378 {
379   // for non HTTP/1.1 connections, assume server closes
380   return _willClose || (_responseVersion != HTTP_1_1);
381 }
382
383 //------------------------------------------------------------------------------
384 bool Request::isComplete() const
385 {
386   return _ready_state == DONE || _ready_state == FAILED;
387 }
388
389 //------------------------------------------------------------------------------
390 bool Request::hasBodyData() const
391 {
392   return !_request_media_type.empty();
393 }
394
395 //------------------------------------------------------------------------------
396 std::string Request::bodyType() const
397 {
398   return _request_media_type;
399 }
400
401 //------------------------------------------------------------------------------
402 size_t Request::bodyLength() const
403 {
404   return _request_data.length();
405 }
406
407 //------------------------------------------------------------------------------
408 size_t Request::getBodyData(char* s, size_t offset, size_t max_count) const
409 {
410   size_t bytes_available = _request_data.size() - offset;
411   size_t bytes_to_read = std::min(bytes_available, max_count);
412
413   memcpy(s, _request_data.data() + offset, bytes_to_read);
414
415   return bytes_to_read;
416 }
417
418 } // of namespace HTTP
419 } // of namespace simgear