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