]> git.mxchange.org Git - simgear.git/blob - simgear/io/HTTPRequest.cxx
535ec402da8fa9c043ded3de7f9f7e0d0238fa63
[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 #include <simgear/structure/exception.hxx>
8
9 namespace simgear
10 {
11 namespace HTTP
12 {
13
14 extern const int DEFAULT_HTTP_PORT;
15
16 //------------------------------------------------------------------------------
17 Request::Request(const std::string& url, const std::string method):
18   _method(method),
19   _url(url),
20   _responseVersion(HTTP_VERSION_UNKNOWN),
21   _responseStatus(0),
22   _responseLength(0),
23   _receivedBodyBytes(0),
24   _ready_state(UNSENT),
25   _willClose(false)
26 {
27
28 }
29
30 //------------------------------------------------------------------------------
31 Request::~Request()
32 {
33
34 }
35
36 //------------------------------------------------------------------------------
37 Request* Request::done(const Callback& cb)
38 {
39   if( _ready_state == DONE )
40     cb(this);
41   else
42     _cb_done = cb;
43
44   return this;
45 }
46
47 //------------------------------------------------------------------------------
48 Request* Request::fail(const Callback& cb)
49 {
50   if( _ready_state == FAILED )
51     cb(this);
52   else
53     _cb_fail = cb;
54
55   return this;
56 }
57
58 //------------------------------------------------------------------------------
59 Request* Request::always(const Callback& cb)
60 {
61   if( isComplete() )
62     cb(this);
63   else
64     _cb_always = cb;
65
66   return this;
67 }
68
69 //------------------------------------------------------------------------------
70 void Request::setBodyData( const std::string& data,
71                            const std::string& type )
72 {
73   _request_data = data;
74   _request_media_type = type;
75
76   if( !data.empty() && _method == "GET" )
77     _method = "POST";
78 }
79
80 //----------------------------------------------------------------------------
81 void Request::setBodyData(const SGPropertyNode* data)
82 {
83   if( !data )
84     setBodyData("");
85
86   std::stringstream buf;
87   writeProperties(buf, data, true);
88
89   setBodyData(buf.str(), "application/xml");
90 }
91
92 //------------------------------------------------------------------------------
93 void Request::setUrl(const std::string& url)
94 {
95   _url = url;
96 }
97
98 //------------------------------------------------------------------------------
99 void Request::requestStart()
100 {
101   setReadyState(OPENED);
102 }
103
104 //------------------------------------------------------------------------------
105 Request::HTTPVersion decodeHTTPVersion(const std::string& v)
106 {
107   if( v == "HTTP/1.1" ) return Request::HTTP_1_1;
108   if( v == "HTTP/1.0" ) return Request::HTTP_1_0;
109   if( strutils::starts_with(v, "HTTP/0.") ) return Request::HTTP_0_x;
110   return Request::HTTP_VERSION_UNKNOWN;
111 }
112
113 //------------------------------------------------------------------------------
114 void Request::responseStart(const std::string& r)
115 {
116     const int maxSplit = 2; // HTTP/1.1 nnn reason-string
117     string_list parts = strutils::split(r, NULL, maxSplit);
118     if (parts.size() != 3) {
119         throw sg_io_exception("bad HTTP response");
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 std::string Request::responseMime() const
277 {
278   std::string content_type = _responseHeaders.get("content-type");
279   if( content_type.empty() )
280     return "application/octet-stream";
281
282   return content_type.substr(0, content_type.find(';'));
283 }
284
285 //------------------------------------------------------------------------------
286 void Request::setResponseLength(unsigned int l)
287 {
288   _responseLength = l;
289 }
290
291 //------------------------------------------------------------------------------
292 unsigned int Request::responseLength() const
293 {
294   // if the server didn't supply a content length, use the number
295   // of bytes we actually received (so far)
296   if( (_responseLength == 0) && (_receivedBodyBytes > 0) )
297     return _receivedBodyBytes;
298
299   return _responseLength;
300 }
301
302 //------------------------------------------------------------------------------
303 void Request::setFailure(int code, const std::string& reason)
304 {
305   _responseStatus = code;
306   _responseReason = reason;
307   setReadyState(FAILED);
308 }
309
310 //------------------------------------------------------------------------------
311 void Request::setReadyState(ReadyState state)
312 {
313   _ready_state = state;
314   if( state == DONE )
315   {
316     // Finish C++ part of request to ensure everything is finished (for example
317     // files and streams are closed) before calling any callback (possibly using
318     // such files)
319     onDone();
320     onAlways();
321
322     if( _cb_done )
323       _cb_done(this);
324   }
325   else if( state == FAILED )
326   {
327     onFail();
328     onAlways();
329
330     if( _cb_fail )
331       _cb_fail(this);
332   }
333   else
334     return;
335
336   if( _cb_always )
337     _cb_always(this);
338 }
339
340 //------------------------------------------------------------------------------
341 void Request::abort()
342 {
343   abort("Request aborted.");
344 }
345
346 //----------------------------------------------------------------------------
347 void Request::abort(const std::string& reason)
348 {
349   if( isComplete() )
350     return;
351
352   setFailure(-1, reason);
353   _willClose = true;
354 }
355
356 //------------------------------------------------------------------------------
357 bool Request::closeAfterComplete() const
358 {
359   // for non HTTP/1.1 connections, assume server closes
360   return _willClose || (_responseVersion != HTTP_1_1);
361 }
362
363 //------------------------------------------------------------------------------
364 bool Request::isComplete() const
365 {
366   return _ready_state == DONE || _ready_state == FAILED;
367 }
368
369 //------------------------------------------------------------------------------
370 bool Request::hasBodyData() const
371 {
372   return !_request_media_type.empty();
373 }
374
375 //------------------------------------------------------------------------------
376 std::string Request::bodyType() const
377 {
378   return _request_media_type;
379 }
380
381 //------------------------------------------------------------------------------
382 size_t Request::bodyLength() const
383 {
384   return _request_data.length();
385 }
386
387 //------------------------------------------------------------------------------
388 size_t Request::getBodyData(char* s, size_t offset, size_t max_count) const
389 {
390   size_t bytes_available = _request_data.size() - offset;
391   size_t bytes_to_read = std::min(bytes_available, max_count);
392
393   memcpy(s, _request_data.data() + offset, bytes_to_read);
394
395   return bytes_to_read;
396 }
397
398 } // of namespace HTTP
399 } // of namespace simgear