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