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>
7 #include <simgear/structure/exception.hxx>
14 extern const int DEFAULT_HTTP_PORT;
16 //------------------------------------------------------------------------------
17 Request::Request(const std::string& url, const std::string method):
20 _responseVersion(HTTP_VERSION_UNKNOWN),
23 _receivedBodyBytes(0),
30 //------------------------------------------------------------------------------
36 //------------------------------------------------------------------------------
37 Request* Request::done(const Callback& cb)
39 if( _ready_state == DONE )
47 //------------------------------------------------------------------------------
48 Request* Request::fail(const Callback& cb)
50 if( _ready_state == FAILED )
58 //------------------------------------------------------------------------------
59 Request* Request::always(const Callback& cb)
69 //------------------------------------------------------------------------------
70 void Request::setBodyData( const std::string& data,
71 const std::string& type )
74 _request_media_type = type;
76 if( !data.empty() && _method == "GET" )
80 //----------------------------------------------------------------------------
81 void Request::setBodyData(const SGPropertyNode* data)
86 std::stringstream buf;
87 writeProperties(buf, data, true);
89 setBodyData(buf.str(), "application/xml");
92 //------------------------------------------------------------------------------
93 void Request::setUrl(const std::string& url)
98 //------------------------------------------------------------------------------
99 void Request::requestStart()
101 setReadyState(OPENED);
104 //------------------------------------------------------------------------------
105 Request::HTTPVersion decodeHTTPVersion(const std::string& v)
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;
113 //------------------------------------------------------------------------------
114 void Request::responseStart(const std::string& r)
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");
122 _responseVersion = decodeHTTPVersion(parts[0]);
123 _responseStatus = strutils::to_int(parts[1]);
124 _responseReason = parts[2];
127 //------------------------------------------------------------------------------
128 void Request::responseHeader(const std::string& key, const std::string& value)
130 if( key == "connection" )
131 _willClose = (value.find("close") != std::string::npos);
133 _responseHeaders[key] = value;
136 //------------------------------------------------------------------------------
137 void Request::responseHeadersComplete()
139 setReadyState(HEADERS_RECEIVED);
142 //------------------------------------------------------------------------------
143 void Request::responseComplete()
149 //------------------------------------------------------------------------------
150 void Request::gotBodyData(const char* s, int n)
152 setReadyState(LOADING);
155 //------------------------------------------------------------------------------
156 void Request::onDone()
161 //------------------------------------------------------------------------------
162 void Request::onFail()
168 "request failed:" << url() << " : "
169 << responseCode() << "/" << responseReason()
173 //------------------------------------------------------------------------------
174 void Request::onAlways()
179 //------------------------------------------------------------------------------
180 void Request::processBodyBytes(const char* s, int n)
182 _receivedBodyBytes += n;
186 //------------------------------------------------------------------------------
187 std::string Request::scheme() const
189 int firstColon = url().find(":");
190 if (firstColon > 0) {
191 return url().substr(0, firstColon);
194 return ""; // couldn't parse scheme
197 //------------------------------------------------------------------------------
198 std::string Request::path() const
200 std::string u(url());
201 int schemeEnd = u.find("://");
203 return ""; // couldn't parse scheme
206 int hostEnd = u.find('/', schemeEnd + 3);
208 // couldn't parse host, or URL looks like 'http://foo.com' (no trailing '/')
209 // fixup to root resource path: '/'
213 int query = u.find('?', hostEnd + 1);
215 // all remainder of URL is path
216 return u.substr(hostEnd);
219 return u.substr(hostEnd, query - hostEnd);
222 //------------------------------------------------------------------------------
223 std::string Request::query() const
225 std::string u(url());
226 int query = u.find('?');
228 return ""; //no query string found
231 return u.substr(query); //includes question mark
234 //------------------------------------------------------------------------------
235 std::string Request::host() const
237 std::string hp(hostAndPort());
238 int colonPos = hp.find(':');
240 return hp.substr(0, colonPos); // trim off the colon and port
242 return hp; // no port specifier
246 //------------------------------------------------------------------------------
247 unsigned short Request::port() const
249 std::string hp(hostAndPort());
250 int colonPos = hp.find(':');
252 return (unsigned short) strutils::to_int(hp.substr(colonPos + 1));
254 return DEFAULT_HTTP_PORT;
258 //------------------------------------------------------------------------------
259 std::string Request::hostAndPort() const
261 std::string u(url());
262 int schemeEnd = u.find("://");
264 return ""; // couldn't parse scheme
267 int hostEnd = u.find('/', schemeEnd + 3);
268 if (hostEnd < 0) { // all remainder of URL is host
269 return u.substr(schemeEnd + 3);
272 return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
275 //------------------------------------------------------------------------------
276 void Request::setResponseLength(unsigned int l)
281 //------------------------------------------------------------------------------
282 unsigned int Request::responseLength() const
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;
289 return _responseLength;
292 //------------------------------------------------------------------------------
293 void Request::setFailure(int code, const std::string& reason)
295 _responseStatus = code;
296 _responseReason = reason;
297 setReadyState(FAILED);
300 //------------------------------------------------------------------------------
301 void Request::setReadyState(ReadyState state)
303 _ready_state = state;
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
315 else if( state == FAILED )
330 //------------------------------------------------------------------------------
331 void Request::abort()
333 abort("Request aborted.");
336 //----------------------------------------------------------------------------
337 void Request::abort(const std::string& reason)
342 setFailure(-1, reason);
346 //------------------------------------------------------------------------------
347 bool Request::closeAfterComplete() const
349 // for non HTTP/1.1 connections, assume server closes
350 return _willClose || (_responseVersion != HTTP_1_1);
353 //------------------------------------------------------------------------------
354 bool Request::isComplete() const
356 return _ready_state == DONE || _ready_state == FAILED;
359 //------------------------------------------------------------------------------
360 bool Request::hasBodyData() const
362 return !_request_media_type.empty();
365 //------------------------------------------------------------------------------
366 std::string Request::bodyType() const
368 return _request_media_type;
371 //------------------------------------------------------------------------------
372 size_t Request::bodyLength() const
374 return _request_data.length();
377 //------------------------------------------------------------------------------
378 size_t Request::getBodyData(char* s, size_t offset, size_t max_count) const
380 size_t bytes_available = _request_data.size() - offset;
381 size_t bytes_to_read = std::min(bytes_available, max_count);
383 memcpy(s, _request_data.data() + offset, bytes_to_read);
385 return bytes_to_read;
388 } // of namespace HTTP
389 } // of namespace simgear