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 )
42 _cb_done.push_back(cb);
47 //------------------------------------------------------------------------------
48 Request* Request::fail(const Callback& cb)
50 if( _ready_state == FAILED )
53 _cb_fail.push_back(cb);
58 //------------------------------------------------------------------------------
59 Request* Request::always(const Callback& cb)
64 _cb_always.push_back(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 std::string Request::responseMime() const
278 std::string content_type = _responseHeaders.get("content-type");
279 if( content_type.empty() )
280 return "application/octet-stream";
282 return content_type.substr(0, content_type.find(';'));
285 //------------------------------------------------------------------------------
286 void Request::setResponseLength(unsigned int l)
291 //------------------------------------------------------------------------------
292 unsigned int Request::responseLength() const
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;
299 return _responseLength;
302 //------------------------------------------------------------------------------
303 void Request::setFailure(int code, const std::string& reason)
305 _responseStatus = code;
306 _responseReason = reason;
309 setReadyState(FAILED);
312 //------------------------------------------------------------------------------
313 void Request::setReadyState(ReadyState state)
315 _ready_state = state;
318 // Finish C++ part of request to ensure everything is finished (for example
319 // files and streams are closed) before calling any callback (possibly using
326 else if( state == FAILED )
339 //------------------------------------------------------------------------------
340 void Request::abort()
342 abort("Request aborted.");
345 //----------------------------------------------------------------------------
346 void Request::abort(const std::string& reason)
348 setFailure(-1, reason);
352 //------------------------------------------------------------------------------
353 bool Request::closeAfterComplete() const
355 // for non HTTP/1.1 connections, assume server closes
356 return _willClose || (_responseVersion != HTTP_1_1);
359 //------------------------------------------------------------------------------
360 bool Request::isComplete() const
362 return _ready_state == DONE || _ready_state == FAILED;
365 //------------------------------------------------------------------------------
366 bool Request::hasBodyData() const
368 return !_request_media_type.empty();
371 //------------------------------------------------------------------------------
372 std::string Request::bodyType() const
374 return _request_media_type;
377 //------------------------------------------------------------------------------
378 size_t Request::bodyLength() const
380 return _request_data.length();
383 //------------------------------------------------------------------------------
384 size_t Request::getBodyData(char* s, size_t offset, size_t max_count) const
386 size_t bytes_available = _request_data.size() - offset;
387 size_t bytes_to_read = std::min(bytes_available, max_count);
389 memcpy(s, _request_data.data() + offset, bytes_to_read);
391 return bytes_to_read;
394 } // of namespace HTTP
395 } // of namespace simgear