1 #include "HTTPRequest.hxx"
3 #include <simgear/compiler.h>
4 #include <simgear/debug/logstream.hxx>
5 #include <simgear/misc/strutils.hxx>
6 #include <simgear/props/props_io.hxx>
13 extern const int DEFAULT_HTTP_PORT;
15 //------------------------------------------------------------------------------
16 Request::Request(const std::string& url, const std::string method):
19 _responseVersion(HTTP_VERSION_UNKNOWN),
22 _receivedBodyBytes(0),
29 //------------------------------------------------------------------------------
35 //------------------------------------------------------------------------------
36 Request* Request::done(const Callback& cb)
38 if( _ready_state == DONE )
46 //------------------------------------------------------------------------------
47 Request* Request::fail(const Callback& cb)
49 if( _ready_state == FAILED )
57 //------------------------------------------------------------------------------
58 Request* Request::always(const Callback& cb)
68 //------------------------------------------------------------------------------
69 void Request::setBodyData( const std::string& data,
70 const std::string& type )
73 _request_media_type = type;
75 if( !data.empty() && _method == "GET" )
79 //----------------------------------------------------------------------------
80 void Request::setBodyData(const SGPropertyNode* data)
85 std::stringstream buf;
86 writeProperties(buf, data, true);
88 setBodyData(buf.str(), "application/xml");
91 //------------------------------------------------------------------------------
92 void Request::setUrl(const std::string& url)
97 //------------------------------------------------------------------------------
98 void Request::requestStart()
100 setReadyState(OPENED);
103 //------------------------------------------------------------------------------
104 Request::HTTPVersion decodeHTTPVersion(const std::string& v)
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;
112 //------------------------------------------------------------------------------
113 void Request::responseStart(const std::string& r)
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");
123 _responseVersion = decodeHTTPVersion(parts[0]);
124 _responseStatus = strutils::to_int(parts[1]);
125 _responseReason = parts[2];
128 //------------------------------------------------------------------------------
129 void Request::responseHeader(const std::string& key, const std::string& value)
131 if( key == "connection" )
132 _willClose = (value.find("close") != std::string::npos);
134 _responseHeaders[key] = value;
137 //------------------------------------------------------------------------------
138 void Request::responseHeadersComplete()
140 setReadyState(HEADERS_RECEIVED);
143 //------------------------------------------------------------------------------
144 void Request::responseComplete()
150 //------------------------------------------------------------------------------
151 void Request::gotBodyData(const char* s, int n)
153 setReadyState(LOADING);
156 //------------------------------------------------------------------------------
157 void Request::onDone()
162 //------------------------------------------------------------------------------
163 void Request::onFail()
169 "request failed:" << url() << " : "
170 << responseCode() << "/" << responseReason()
174 //------------------------------------------------------------------------------
175 void Request::onAlways()
180 //------------------------------------------------------------------------------
181 void Request::processBodyBytes(const char* s, int n)
183 _receivedBodyBytes += n;
187 //------------------------------------------------------------------------------
188 std::string Request::scheme() const
190 int firstColon = url().find(":");
191 if (firstColon > 0) {
192 return url().substr(0, firstColon);
195 return ""; // couldn't parse scheme
198 //------------------------------------------------------------------------------
199 std::string Request::path() const
201 std::string u(url());
202 int schemeEnd = u.find("://");
204 return ""; // couldn't parse scheme
207 int hostEnd = u.find('/', schemeEnd + 3);
209 // couldn't parse host, or URL looks like 'http://foo.com' (no trailing '/')
210 // fixup to root resource path: '/'
214 int query = u.find('?', hostEnd + 1);
216 // all remainder of URL is path
217 return u.substr(hostEnd);
220 return u.substr(hostEnd, query - hostEnd);
223 //------------------------------------------------------------------------------
224 std::string Request::query() const
226 std::string u(url());
227 int query = u.find('?');
229 return ""; //no query string found
232 return u.substr(query); //includes question mark
235 //------------------------------------------------------------------------------
236 std::string Request::host() const
238 std::string hp(hostAndPort());
239 int colonPos = hp.find(':');
241 return hp.substr(0, colonPos); // trim off the colon and port
243 return hp; // no port specifier
247 //------------------------------------------------------------------------------
248 unsigned short Request::port() const
250 std::string hp(hostAndPort());
251 int colonPos = hp.find(':');
253 return (unsigned short) strutils::to_int(hp.substr(colonPos + 1));
255 return DEFAULT_HTTP_PORT;
259 //------------------------------------------------------------------------------
260 std::string Request::hostAndPort() const
262 std::string u(url());
263 int schemeEnd = u.find("://");
265 return ""; // couldn't parse scheme
268 int hostEnd = u.find('/', schemeEnd + 3);
269 if (hostEnd < 0) { // all remainder of URL is host
270 return u.substr(schemeEnd + 3);
273 return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
276 //------------------------------------------------------------------------------
277 void Request::setResponseLength(unsigned int l)
282 //------------------------------------------------------------------------------
283 unsigned int Request::responseLength() const
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;
290 return _responseLength;
293 //------------------------------------------------------------------------------
294 void Request::setFailure(int code, const std::string& reason)
296 _responseStatus = code;
297 _responseReason = reason;
298 setReadyState(FAILED);
301 //------------------------------------------------------------------------------
302 void Request::setReadyState(ReadyState state)
304 _ready_state = state;
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
316 else if( state == FAILED )
331 //------------------------------------------------------------------------------
332 void Request::abort()
334 abort("Request aborted.");
337 //----------------------------------------------------------------------------
338 void Request::abort(const std::string& reason)
343 setFailure(-1, reason);
347 //------------------------------------------------------------------------------
348 bool Request::closeAfterComplete() const
350 // for non HTTP/1.1 connections, assume server closes
351 return _willClose || (_responseVersion != HTTP_1_1);
354 //------------------------------------------------------------------------------
355 bool Request::isComplete() const
357 return _ready_state == DONE || _ready_state == FAILED;
360 //------------------------------------------------------------------------------
361 bool Request::hasBodyData() const
363 return !_request_media_type.empty();
366 //------------------------------------------------------------------------------
367 std::string Request::bodyType() const
369 return _request_media_type;
372 //------------------------------------------------------------------------------
373 size_t Request::bodyLength() const
375 return _request_data.length();
378 //------------------------------------------------------------------------------
379 size_t Request::getBodyData(char* s, size_t offset, size_t max_count) const
381 size_t bytes_available = _request_data.size() - offset;
382 size_t bytes_to_read = std::min(bytes_available, max_count);
384 memcpy(s, _request_data.data() + offset, bytes_to_read);
386 return bytes_to_read;
389 } // of namespace HTTP
390 } // of namespace simgear