+// Copyright (C) 2011 James Turner <zakalawe@mac.com>
+// Copyright (C) 2013 Thomas Geymayer <tomgey@gmail.com>
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU Library General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
#include "HTTPRequest.hxx"
#include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/props/props_io.hxx>
+#include <simgear/structure/exception.hxx>
namespace simgear
{
//------------------------------------------------------------------------------
Request::Request(const std::string& url, const std::string method):
+ _client(0),
_method(method),
_url(url),
_responseVersion(HTTP_VERSION_UNKNOWN),
_responseLength(0),
_receivedBodyBytes(0),
_ready_state(UNSENT),
- _willClose(false)
+ _willClose(false),
+ _connectionCloseHeader(false)
{
}
if( _ready_state == DONE )
cb(this);
else
- _cb_done = cb;
+ _cb_done.push_back(cb);
return this;
}
if( _ready_state == FAILED )
cb(this);
else
- _cb_fail = cb;
+ _cb_fail.push_back(cb);
return this;
}
if( isComplete() )
cb(this);
else
- _cb_always = cb;
+ _cb_always.push_back(cb);
return this;
}
const int maxSplit = 2; // HTTP/1.1 nnn reason-string
string_list parts = strutils::split(r, NULL, maxSplit);
if (parts.size() != 3) {
- SG_LOG(SG_IO, SG_WARN, "HTTP::Request: malformed response start:" << r);
- setFailure(400, "malformed HTTP response header");
- return;
+ throw sg_io_exception("bad HTTP response:" + r);
}
-
+
_responseVersion = decodeHTTPVersion(parts[0]);
_responseStatus = strutils::to_int(parts[1]);
_responseReason = parts[2];
+
+ setReadyState(STATUS_RECEIVED);
}
//------------------------------------------------------------------------------
void Request::responseHeader(const std::string& key, const std::string& value)
{
- if( key == "connection" )
- _willClose = (value.find("close") != std::string::npos);
+ if( key == "connection" ) {
+ _connectionCloseHeader = (value.find("close") != std::string::npos);
+ // track willClose seperately because other conditions (abort, for
+ // example) can also set it
+ _willClose = _connectionCloseHeader;
+ } else if (key == "content-length") {
+ int sz = strutils::to_int(value);
+ setResponseLength(sz);
+ }
_responseHeaders[key] = value;
}
if (firstColon > 0) {
return url().substr(0, firstColon);
}
-
+
return ""; // couldn't parse scheme
}
if (schemeEnd < 0) {
return ""; // couldn't parse scheme
}
-
+
int hostEnd = u.find('/', schemeEnd + 3);
if (hostEnd < 0) {
-// couldn't parse host, or URL looks like 'http://foo.com' (no trailing '/')
-// fixup to root resource path: '/'
- return "/";
+// couldn't parse host, or URL looks like 'http://foo.com' (no trailing '/')
+// fixup to root resource path: '/'
+ return "/";
}
-
+
int query = u.find('?', hostEnd + 1);
if (query < 0) {
// all remainder of URL is path
return u.substr(hostEnd);
}
-
+
return u.substr(hostEnd, query - hostEnd);
}
if (query < 0) {
return ""; //no query string found
}
-
+
return u.substr(query); //includes question mark
}
return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
}
+//------------------------------------------------------------------------------
+std::string Request::responseMime() const
+{
+ std::string content_type = _responseHeaders.get("content-type");
+ if( content_type.empty() )
+ return "application/octet-stream";
+
+ return content_type.substr(0, content_type.find(';'));
+}
+
//------------------------------------------------------------------------------
void Request::setResponseLength(unsigned int l)
{
//------------------------------------------------------------------------------
void Request::setFailure(int code, const std::string& reason)
{
+ SG_LOG(SG_IO, SG_WARN, "HTTP request: set failure:" << code << " reason " << reason);
_responseStatus = code;
_responseReason = reason;
- setReadyState(FAILED);
+
+ if( !isComplete() )
+ setReadyState(FAILED);
}
//------------------------------------------------------------------------------
_ready_state = state;
if( state == DONE )
{
- if( _cb_done )
- _cb_done(this);
+ // Finish C++ part of request to ensure everything is finished (for example
+ // files and streams are closed) before calling any callback (possibly using
+ // such files)
onDone();
+ onAlways();
+
+ _cb_done(this);
}
else if( state == FAILED )
{
- if( _cb_fail )
- _cb_fail(this);
onFail();
+ onAlways();
+
+ _cb_fail(this);
}
else
return;
- if( _cb_always )
- _cb_always(this);
- onAlways();
+ _cb_always(this);
}
//------------------------------------------------------------------------------
-void Request::abort()
+bool Request::closeAfterComplete() const
{
- abort("Request aborted.");
+ // for non HTTP/1.1 connections, assume server closes
+ return _willClose || (_responseVersion != HTTP_1_1);
}
-//----------------------------------------------------------------------------
-void Request::abort(const std::string& reason)
-{
- if( isComplete() )
- return;
+//------------------------------------------------------------------------------
- setFailure(-1, reason);
- _willClose = true;
+void Request::setCloseAfterComplete()
+{
+ _willClose = true;
}
//------------------------------------------------------------------------------
-bool Request::closeAfterComplete() const
+bool Request::serverSupportsPipelining() const
{
- // for non HTTP/1.1 connections, assume server closes
- return _willClose || (_responseVersion != HTTP_1_1);
+ return (_responseVersion == HTTP_1_1) && !_connectionCloseHeader;
}
//------------------------------------------------------------------------------