]> git.mxchange.org Git - simgear.git/blob - simgear/io/HTTPRequest.cxx
d51d97902ef3d47a61bb9a9f175bb546bd495b73
[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         setFailure(400, "malformed HTTP response header");
119         return;
120     }
121     
122     _responseVersion = decodeHTTPVersion(parts[0]);
123     _responseStatus = strutils::to_int(parts[1]);
124     _responseReason = parts[2];
125 }
126
127 //------------------------------------------------------------------------------
128 void Request::responseHeader(const std::string& key, const std::string& value)
129 {
130   if( key == "connection" )
131     _willClose = (value.find("close") != std::string::npos);
132
133   _responseHeaders[key] = value;
134 }
135
136 //------------------------------------------------------------------------------
137 void Request::responseHeadersComplete()
138 {
139   setReadyState(HEADERS_RECEIVED);
140 }
141
142 //------------------------------------------------------------------------------
143 void Request::responseComplete()
144 {
145   if( !isComplete() )
146     setReadyState(DONE);
147 }
148
149 //------------------------------------------------------------------------------
150 void Request::gotBodyData(const char* s, int n)
151 {
152   setReadyState(LOADING);
153 }
154
155 //------------------------------------------------------------------------------
156 void Request::onDone()
157 {
158
159 }
160
161 //------------------------------------------------------------------------------
162 void Request::onFail()
163 {
164   SG_LOG
165   (
166     SG_IO,
167     SG_INFO,
168     "request failed:" << url() << " : "
169                       << responseCode() << "/" << responseReason()
170   );
171 }
172
173 //------------------------------------------------------------------------------
174 void Request::onAlways()
175 {
176
177 }
178
179 //------------------------------------------------------------------------------
180 void Request::processBodyBytes(const char* s, int n)
181 {
182   _receivedBodyBytes += n;
183   gotBodyData(s, n);
184 }
185
186 //------------------------------------------------------------------------------
187 std::string Request::scheme() const
188 {
189     int firstColon = url().find(":");
190     if (firstColon > 0) {
191         return url().substr(0, firstColon);
192     }
193     
194     return ""; // couldn't parse scheme
195 }
196
197 //------------------------------------------------------------------------------
198 std::string Request::path() const
199 {
200     std::string u(url());
201     int schemeEnd = u.find("://");
202     if (schemeEnd < 0) {
203         return ""; // couldn't parse scheme
204     }
205     
206     int hostEnd = u.find('/', schemeEnd + 3);
207     if (hostEnd < 0) {
208 // couldn't parse host, or URL looks like 'http://foo.com' (no trailing '/') 
209 // fixup to root resource path: '/' 
210         return "/"; 
211     }
212     
213     int query = u.find('?', hostEnd + 1);
214     if (query < 0) {
215         // all remainder of URL is path
216         return u.substr(hostEnd);
217     }
218     
219     return u.substr(hostEnd, query - hostEnd);
220 }
221
222 //------------------------------------------------------------------------------
223 std::string Request::query() const
224 {
225   std::string u(url());
226   int query = u.find('?');
227   if (query < 0) {
228     return "";  //no query string found
229   }
230   
231   return u.substr(query);   //includes question mark
232 }
233
234 //------------------------------------------------------------------------------
235 std::string Request::host() const
236 {
237   std::string hp(hostAndPort());
238   int colonPos = hp.find(':');
239   if (colonPos >= 0) {
240       return hp.substr(0, colonPos); // trim off the colon and port
241   } else {
242       return hp; // no port specifier
243   }
244 }
245
246 //------------------------------------------------------------------------------
247 unsigned short Request::port() const
248 {
249   std::string hp(hostAndPort());
250   int colonPos = hp.find(':');
251   if (colonPos >= 0) {
252       return (unsigned short) strutils::to_int(hp.substr(colonPos + 1));
253   } else {
254       return DEFAULT_HTTP_PORT;
255   }
256 }
257
258 //------------------------------------------------------------------------------
259 std::string Request::hostAndPort() const
260 {
261   std::string u(url());
262   int schemeEnd = u.find("://");
263   if (schemeEnd < 0) {
264       return ""; // couldn't parse scheme
265   }
266
267   int hostEnd = u.find('/', schemeEnd + 3);
268   if (hostEnd < 0) { // all remainder of URL is host
269       return u.substr(schemeEnd + 3);
270   }
271
272   return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
273 }
274
275 //------------------------------------------------------------------------------
276 void Request::setResponseLength(unsigned int l)
277 {
278   _responseLength = l;
279 }
280
281 //------------------------------------------------------------------------------
282 unsigned int Request::responseLength() const
283 {
284   // if the server didn't supply a content length, use the number
285   // of bytes we actually received (so far)
286   if( (_responseLength == 0) && (_receivedBodyBytes > 0) )
287     return _receivedBodyBytes;
288
289   return _responseLength;
290 }
291
292 //------------------------------------------------------------------------------
293 void Request::setFailure(int code, const std::string& reason)
294 {
295   _responseStatus = code;
296   _responseReason = reason;
297   setReadyState(FAILED);
298 }
299
300 //------------------------------------------------------------------------------
301 void Request::setReadyState(ReadyState state)
302 {
303   _ready_state = state;
304   if( state == DONE )
305   {
306     // Finish C++ part of request to ensure everything is finished (for example
307     // files and streams are closed) before calling any callback (possibly using
308     // such files)
309     onDone();
310     onAlways();
311
312     if( _cb_done )
313       _cb_done(this);
314   }
315   else if( state == FAILED )
316   {
317     onFail();
318     onAlways();
319
320     if( _cb_fail )
321       _cb_fail(this);
322   }
323   else
324     return;
325
326   if( _cb_always )
327     _cb_always(this);
328 }
329
330 //------------------------------------------------------------------------------
331 void Request::abort()
332 {
333   abort("Request aborted.");
334 }
335
336 //----------------------------------------------------------------------------
337 void Request::abort(const std::string& reason)
338 {
339   if( isComplete() )
340     return;
341
342   setFailure(-1, reason);
343   _willClose = true;
344 }
345
346 //------------------------------------------------------------------------------
347 bool Request::closeAfterComplete() const
348 {
349   // for non HTTP/1.1 connections, assume server closes
350   return _willClose || (_responseVersion != HTTP_1_1);
351 }
352
353 //------------------------------------------------------------------------------
354 bool Request::isComplete() const
355 {
356   return _ready_state == DONE || _ready_state == FAILED;
357 }
358
359 //------------------------------------------------------------------------------
360 bool Request::hasBodyData() const
361 {
362   return !_request_media_type.empty();
363 }
364
365 //------------------------------------------------------------------------------
366 std::string Request::bodyType() const
367 {
368   return _request_media_type;
369 }
370
371 //------------------------------------------------------------------------------
372 size_t Request::bodyLength() const
373 {
374   return _request_data.length();
375 }
376
377 //------------------------------------------------------------------------------
378 size_t Request::getBodyData(char* s, size_t offset, size_t max_count) const
379 {
380   size_t bytes_available = _request_data.size() - offset;
381   size_t bytes_to_read = std::min(bytes_available, max_count);
382
383   memcpy(s, _request_data.data() + offset, bytes_to_read);
384
385   return bytes_to_read;
386 }
387
388 } // of namespace HTTP
389 } // of namespace simgear