sg_socket.hxx
sg_socket_udp.hxx
HTTPClient.hxx
+ HTTPFileRequest.hxx
+ HTTPMemoryRequest.hxx
HTTPRequest.hxx
HTTPContentDecode.hxx
DAVMultiStatus.hxx
sg_socket.cxx
sg_socket_udp.cxx
HTTPClient.cxx
+ HTTPFileRequest.cxx
+ HTTPMemoryRequest.cxx
HTTPRequest.cxx
HTTPContentDecode.cxx
DAVMultiStatus.cxx
//
#include "HTTPClient.hxx"
+#include "HTTPFileRequest.hxx"
#include <sstream>
#include <cassert>
#include <cstdlib> // rand()
#include <list>
-#include <iostream>
#include <errno.h>
#include <map>
#include <stdexcept>
# endif
#endif
-using std::string;
-using std::stringstream;
-using std::vector;
-
namespace simgear
{
virtual ~Connection()
{
}
+
+ virtual void handleBufferRead (NetBuffer& buffer)
+ {
+ if( !activeRequest || !activeRequest->isComplete() )
+ return NetChat::handleBufferRead(buffer);
+
+ // Request should be aborted (signaled by setting its state to complete).
+
+ // force the state to GETTING_BODY, to simplify logic in
+ // responseComplete and handleClose
+ state = STATE_GETTING_BODY;
+ responseComplete();
+ }
- void setServer(const string& h, short p)
+ void setServer(const std::string& h, short p)
{
host = h;
port = p;
void tryStartNextRequest()
{
+ while( !queuedRequests.empty()
+ && queuedRequests.front()->isComplete() )
+ queuedRequests.pop_front();
+
if (queuedRequests.empty()) {
idleTime.stamp();
return;
Request_ptr r = queuedRequests.front();
r->requestStart();
- requestBodyBytesToSend = r->requestBodyLength();
-
- stringstream headerData;
- string path = r->path();
+
+ std::stringstream headerData;
+ std::string path = r->path();
assert(!path.empty());
- string query = r->query();
- string bodyData;
+ std::string query = r->query();
+ std::string bodyData;
if (!client->proxyHost().empty()) {
path = r->scheme() + "://" + r->host() + r->path();
}
- if (r->requestBodyType() == CONTENT_TYPE_URL_ENCODED) {
+ if (r->bodyType() == CONTENT_TYPE_URL_ENCODED) {
headerData << r->method() << " " << path << " HTTP/1.1\r\n";
bodyData = query.substr(1); // URL-encode, drop the leading '?'
headerData << "Content-Type:" << CONTENT_TYPE_URL_ENCODED << "\r\n";
headerData << "Content-Length:" << bodyData.size() << "\r\n";
} else {
headerData << r->method() << " " << path << query << " HTTP/1.1\r\n";
- if (requestBodyBytesToSend >= 0) {
- headerData << "Content-Length:" << requestBodyBytesToSend << "\r\n";
- headerData << "Content-Type:" << r->requestBodyType() << "\r\n";
+ if( r->hasBodyData() )
+ {
+ headerData << "Content-Length:" << r->bodyLength() << "\r\n";
+ headerData << "Content-Type:" << r->bodyType() << "\r\n";
}
}
headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n";
}
- BOOST_FOREACH(string h, r->requestHeaders()) {
- headerData << h << ": " << r->header(h) << "\r\n";
+ BOOST_FOREACH(const StringMap::value_type& h, r->requestHeaders()) {
+ headerData << h.first << ": " << h.second << "\r\n";
}
headerData << "\r\n"; // final CRLF to terminate the headers
// drain down before trying to start any more requests.
return;
}
-
- while (requestBodyBytesToSend > 0) {
- char buf[4096];
- int len = r->getBodyData(buf, 4096);
- if (len > 0) {
- requestBodyBytesToSend -= len;
- if (!bufferSend(buf, len)) {
- SG_LOG(SG_IO, SG_WARN, "overflow the HTTP::Connection output buffer");
- state = STATE_SOCKET_ERROR;
- return;
+
+ if( r->hasBodyData() )
+ for(size_t body_bytes_sent = 0; body_bytes_sent < r->bodyLength();)
+ {
+ char buf[4096];
+ size_t len = r->getBodyData(buf, body_bytes_sent, 4096);
+ if( len )
+ {
+ if( !bufferSend(buf, len) )
+ {
+ SG_LOG(SG_IO,
+ SG_WARN,
+ "overflow the HTTP::Connection output buffer");
+ state = STATE_SOCKET_ERROR;
+ return;
+ }
+ body_bytes_sent += len;
+ }
+ else
+ {
+ SG_LOG(SG_IO,
+ SG_WARN,
+ "HTTP asynchronous request body generation is unsupported");
+ break;
}
- // SG_LOG(SG_IO, SG_INFO, "sent body:\n" << string(buf, len) << "\n%%%%%%%%%");
- } else {
- SG_LOG(SG_IO, SG_WARN, "HTTP asynchronous request body generation is unsupported");
- break;
}
- }
- // SG_LOG(SG_IO, SG_INFO, "did start request:" << r->url() <<
- // "\n\t @ " << reinterpret_cast<void*>(r.ptr()) <<
- // "\n\t on connection " << this);
- // successfully sent, remove from queue, and maybe send the next
+ // SG_LOG(SG_IO, SG_INFO, "did start request:" << r->url() <<
+ // "\n\t @ " << reinterpret_cast<void*>(r.ptr()) <<
+ // "\n\t on connection " << this);
+ // successfully sent, remove from queue, and maybe send the next
queuedRequests.pop_front();
sentRequests.push_back(r);
- state = STATE_WAITING_FOR_RESPONSE;
+ state = STATE_WAITING_FOR_RESPONSE;
- // pipelining, let's maybe send the next request right away
+ // pipelining, let's maybe send the next request right away
tryStartNextRequest();
}
{
idleTime.stamp();
client->receivedBytes(static_cast<unsigned int>(n));
-
- if ((state == STATE_GETTING_BODY) || (state == STATE_GETTING_CHUNKED_BYTES)) {
- _contentDecoder.receivedBytes(s, n);
- } else {
- buffer += string(s, n);
- }
+
+ if( (state == STATE_GETTING_BODY)
+ || (state == STATE_GETTING_CHUNKED_BYTES) )
+ _contentDecoder.receivedBytes(s, n);
+ else
+ buffer.append(s, n);
}
virtual void foundTerminator(void)
void processHeader()
{
- string h = strutils::simplify(buffer);
+ std::string h = strutils::simplify(buffer);
if (h.empty()) { // blank line terminates headers
headersComplete();
return;
return;
}
- string key = strutils::simplify(buffer.substr(0, colonPos));
- string lkey = boost::to_lower_copy(key);
- string value = strutils::strip(buffer.substr(colonPos + 1));
+ std::string key = strutils::simplify(buffer.substr(0, colonPos));
+ std::string lkey = boost::to_lower_copy(key);
+ std::string value = strutils::strip(buffer.substr(colonPos + 1));
// only consider these if getting headers (as opposed to trailers
// of a chunked transfer)
activeRequest->responseHeader(lkey, value);
}
- void processTransferEncoding(const string& te)
+ void processTransferEncoding(const std::string& te)
{
if (te == "chunked") {
chunkedTransfer = true;
void responseComplete()
{
- Request_ptr completedRequest = activeRequest;
+ Request_ptr completedRequest = activeRequest;
_contentDecoder.finish();
assert(sentRequests.front() == activeRequest);
Client* client;
Request_ptr activeRequest;
ConnectionState state;
- string host;
+ std::string host;
short port;
std::string buffer;
int bodyTransferSize;
SGTimeStamp idleTime;
bool chunkedTransfer;
bool noMessageBody;
- int requestBodyBytesToSend;
RequestList queuedRequests;
RequestList sentRequests;
void Client::makeRequest(const Request_ptr& r)
{
+ if( r->isComplete() )
+ return;
+
if( r->url().find("://") == std::string::npos ) {
r->setFailure(EINVAL, "malformed URL");
return;
return;
}
- string host = r->host();
+ std::string host = r->host();
int port = r->port();
if (!d->proxy.empty()) {
host = d->proxy;
}
Connection* con = NULL;
- stringstream ss;
+ std::stringstream ss;
ss << host << "-" << port;
- string connectionId = ss.str();
+ std::string connectionId = ss.str();
bool havePending = !d->pendingRequests.empty();
bool atConnectionsLimit = d->connections.size() >= d->maxConnections;
ConnectionDict::iterator consEnd = d->connections.end();
con->queueRequest(r);
}
+//------------------------------------------------------------------------------
+FileRequestRef Client::urlretrieve( const std::string& url,
+ const std::string& filename )
+{
+ FileRequestRef req = new FileRequest(url, filename);
+ makeRequest(req);
+ return req;
+}
+
+//------------------------------------------------------------------------------
+MemoryRequestRef Client::urlload(const std::string& url)
+{
+ MemoryRequestRef req = new MemoryRequest(url);
+ makeRequest(req);
+ return req;
+}
+
void Client::requestFinished(Connection* con)
{
}
-void Client::setUserAgent(const string& ua)
+void Client::setUserAgent(const std::string& ua)
{
d->userAgent = ua;
}
return d->proxyAuth;
}
-void Client::setProxy(const string& proxy, int port, const string& auth)
+void Client::setProxy( const std::string& proxy,
+ int port,
+ const std::string& auth )
{
d->proxy = proxy;
d->proxyPort = port;
#include <memory> // for std::auto_ptr
#include <stdint.h> // for uint_64t
-#include <simgear/io/HTTPRequest.hxx>
+#include <simgear/io/HTTPFileRequest.hxx>
+#include <simgear/io/HTTPMemoryRequest.hxx>
namespace simgear
{
void update(int waitTimeout = 0);
void makeRequest(const Request_ptr& r);
-
+
+ /**
+ * Download a resource and save it to a file.
+ *
+ * @param url The resource to download
+ * @param filename Path to the target file
+ * @param data Data for POST request
+ */
+ FileRequestRef urlretrieve( const std::string& url,
+ const std::string& filename );
+
+ /**
+ * Request a resource and keep it in memory.
+ *
+ * @param url The resource to download
+ */
+ MemoryRequestRef urlload(const std::string& url);
+
void setUserAgent(const std::string& ua);
void setProxy(const std::string& proxy, int port, const std::string& auth = "");
--- /dev/null
+// HTTP request writing response to a file.
+//
+// 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 "HTTPFileRequest.hxx"
+#include <simgear/debug/logstream.hxx>
+
+namespace simgear
+{
+namespace HTTP
+{
+
+ //----------------------------------------------------------------------------
+ FileRequest::FileRequest(const std::string& url, const std::string& path):
+ Request(url, "GET"),
+ _filename(path)
+ {
+
+ }
+
+ //----------------------------------------------------------------------------
+ void FileRequest::responseHeadersComplete()
+ {
+ Request::responseHeadersComplete();
+
+ if( !_filename.empty() )
+ // TODO validate path? (would require to expose fgValidatePath somehow to
+ // simgear)
+ _file.open(_filename.c_str(), std::ios::binary | std::ios::trunc);
+
+ if( !_file )
+ {
+ SG_LOG
+ (
+ SG_IO,
+ SG_WARN,
+ "HTTP::FileRequest: failed to open file '" << _filename << "'"
+ );
+
+ abort("Failed to open file.");
+ }
+ }
+
+ //----------------------------------------------------------------------------
+ void FileRequest::gotBodyData(const char* s, int n)
+ {
+ if( !_file )
+ {
+ SG_LOG
+ (
+ SG_IO,
+ SG_WARN,
+ "HTTP::FileRequest: error writing to '" << _filename << "'"
+ );
+ return;
+ }
+
+ _file.write(s, n);
+ }
+
+ //----------------------------------------------------------------------------
+ void FileRequest::onAlways()
+ {
+ _file.close();
+ }
+
+} // namespace HTTP
+} // namespace simgear
--- /dev/null
+///@file HTTP request writing response to a file.
+//
+// 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
+
+#ifndef SG_HTTP_FILEREQUEST_HXX_
+#define SG_HTTP_FILEREQUEST_HXX_
+
+#include "HTTPRequest.hxx"
+#include <fstream>
+
+namespace simgear
+{
+namespace HTTP
+{
+
+ class FileRequest:
+ public Request
+ {
+ public:
+
+ /**
+ *
+ * @param url Adress to download from
+ * @param path Path to file for saving response
+ */
+ FileRequest(const std::string& url, const std::string& path);
+
+ protected:
+ std::string _filename;
+ std::ofstream _file;
+
+ virtual void responseHeadersComplete();
+ virtual void gotBodyData(const char* s, int n);
+ virtual void onAlways();
+ };
+
+ typedef SGSharedPtr<FileRequest> FileRequestRef;
+
+} // namespace HTTP
+} // namespace simgear
+
+#endif /* SG_HTTP_FILEREQUEST_HXX_ */
--- /dev/null
+// HTTP request keeping response in memory.
+//
+// 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 "HTTPMemoryRequest.hxx"
+
+namespace simgear
+{
+namespace HTTP
+{
+
+ //----------------------------------------------------------------------------
+ MemoryRequest::MemoryRequest(const std::string& url):
+ Request(url, "GET")
+ {
+
+ }
+
+ //----------------------------------------------------------------------------
+ const std::string& MemoryRequest::responseBody() const
+ {
+ return _response;
+ }
+
+ //----------------------------------------------------------------------------
+ void MemoryRequest::responseHeadersComplete()
+ {
+ Request::responseHeadersComplete();
+
+ if( responseLength() )
+ _response.reserve( responseLength() );
+ }
+
+ //----------------------------------------------------------------------------
+ void MemoryRequest::gotBodyData(const char* s, int n)
+ {
+ _response.append(s, n);
+ }
+
+} // namespace HTTP
+} // namespace simgear
--- /dev/null
+///@file HTTP request keeping response in memory.
+//
+// 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
+
+#ifndef SG_HTTP_MEMORYREQUEST_HXX_
+#define SG_HTTP_MEMORYREQUEST_HXX_
+
+#include "HTTPRequest.hxx"
+#include <fstream>
+
+namespace simgear
+{
+namespace HTTP
+{
+
+ class MemoryRequest:
+ public Request
+ {
+ public:
+
+ /**
+ *
+ * @param url Adress to download from
+ */
+ MemoryRequest(const std::string& url);
+
+ /**
+ * Body contents of server response.
+ */
+ const std::string& responseBody() const;
+
+ protected:
+ std::string _response;
+
+ virtual void responseHeadersComplete();
+ virtual void gotBodyData(const char* s, int n);
+ };
+
+ typedef SGSharedPtr<MemoryRequest> MemoryRequestRef;
+
+} // namespace HTTP
+} // namespace simgear
+
+#endif /* SG_HTTP_MEMORYREQUEST_HXX_ */
#include "HTTPRequest.hxx"
-#include <simgear/misc/strutils.hxx>
#include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
-
-using std::string;
-using std::map;
+#include <simgear/misc/strutils.hxx>
+#include <simgear/props/props_io.hxx>
namespace simgear
{
-
namespace HTTP
{
extern const int DEFAULT_HTTP_PORT;
-Request::Request(const string& url, const string method) :
- _method(method),
- _url(url),
- _responseVersion(HTTP_VERSION_UNKNOWN),
- _responseStatus(0),
- _responseLength(0),
- _receivedBodyBytes(0),
- _willClose(false)
+//------------------------------------------------------------------------------
+Request::Request(const std::string& url, const std::string method):
+ _method(method),
+ _url(url),
+ _responseVersion(HTTP_VERSION_UNKNOWN),
+ _responseStatus(0),
+ _responseLength(0),
+ _receivedBodyBytes(0),
+ _ready_state(UNSENT),
+ _willClose(false)
{
-
+
}
+//------------------------------------------------------------------------------
Request::~Request()
{
-
+
}
-void Request::setUrl(const string& url)
+//------------------------------------------------------------------------------
+Request* Request::done(const Callback& cb)
{
- _url = url;
+ if( _ready_state == DONE )
+ cb(this);
+ else
+ _cb_done = cb;
+
+ return this;
}
-string_list Request::requestHeaders() const
+//------------------------------------------------------------------------------
+Request* Request::fail(const Callback& cb)
{
- string_list r;
- return r;
+ if( _ready_state == FAILED )
+ cb(this);
+ else
+ _cb_fail = cb;
+
+ return this;
+}
+
+//------------------------------------------------------------------------------
+Request* Request::always(const Callback& cb)
+{
+ if( isComplete() )
+ cb(this);
+ else
+ _cb_always = cb;
+
+ return this;
+}
+
+//------------------------------------------------------------------------------
+void Request::setBodyData( const std::string& data,
+ const std::string& type )
+{
+ _request_data = data;
+ _request_media_type = type;
+
+ if( !data.empty() && _method == "GET" )
+ _method = "POST";
+}
+
+//----------------------------------------------------------------------------
+void Request::setBodyData(const SGPropertyNode* data)
+{
+ if( !data )
+ setBodyData("");
+
+ std::stringstream buf;
+ writeProperties(buf, data, true);
+
+ setBodyData(buf.str(), "application/xml");
}
-string Request::header(const std::string& name) const
+//------------------------------------------------------------------------------
+void Request::setUrl(const std::string& url)
{
- return string();
+ _url = url;
}
+//------------------------------------------------------------------------------
void Request::requestStart()
{
-
+ setReadyState(OPENED);
+}
+
+//------------------------------------------------------------------------------
+Request::HTTPVersion decodeHTTPVersion(const std::string& v)
+{
+ if( v == "HTTP/1.1" ) return Request::HTTP_1_1;
+ if( v == "HTTP/1.0" ) return Request::HTTP_1_0;
+ if( strutils::starts_with(v, "HTTP/0.") ) return Request::HTTP_0_x;
+ return Request::HTTP_VERSION_UNKNOWN;
}
-void Request::responseStart(const string& r)
+//------------------------------------------------------------------------------
+void Request::responseStart(const std::string& r)
{
const int maxSplit = 2; // HTTP/1.1 nnn reason-string
string_list parts = strutils::split(r, NULL, maxSplit);
return;
}
- _responseVersion = decodeVersion(parts[0]);
+ _responseVersion = decodeHTTPVersion(parts[0]);
_responseStatus = strutils::to_int(parts[1]);
_responseReason = parts[2];
}
-void Request::responseHeader(const string& key, const string& value)
+//------------------------------------------------------------------------------
+void Request::responseHeader(const std::string& key, const std::string& value)
{
- if (key == "connection") {
- _willClose = (value.find("close") != string::npos);
- }
-
- _responseHeaders[key] = value;
+ if( key == "connection" )
+ _willClose = (value.find("close") != std::string::npos);
+
+ _responseHeaders[key] = value;
}
+//------------------------------------------------------------------------------
void Request::responseHeadersComplete()
{
- // no op
+ setReadyState(HEADERS_RECEIVED);
}
-void Request::processBodyBytes(const char* s, int n)
+//------------------------------------------------------------------------------
+void Request::responseComplete()
{
- _receivedBodyBytes += n;
- gotBodyData(s, n);
+ if( !isComplete() )
+ setReadyState(DONE);
}
+//------------------------------------------------------------------------------
void Request::gotBodyData(const char* s, int n)
+{
+ setReadyState(LOADING);
+}
+
+//------------------------------------------------------------------------------
+void Request::onDone()
{
}
-void Request::responseComplete()
+//------------------------------------------------------------------------------
+void Request::onFail()
{
-
+ SG_LOG
+ (
+ SG_IO,
+ SG_INFO,
+ "request failed:" << url() << " : "
+ << responseCode() << "/" << responseReason()
+ );
}
-
-string Request::scheme() const
+
+//------------------------------------------------------------------------------
+void Request::onAlways()
+{
+
+}
+
+//------------------------------------------------------------------------------
+void Request::processBodyBytes(const char* s, int n)
+{
+ _receivedBodyBytes += n;
+ gotBodyData(s, n);
+}
+
+//------------------------------------------------------------------------------
+std::string Request::scheme() const
{
int firstColon = url().find(":");
if (firstColon > 0) {
return ""; // couldn't parse scheme
}
-
-string Request::path() const
+
+//------------------------------------------------------------------------------
+std::string Request::path() const
{
- string u(url());
+ std::string u(url());
int schemeEnd = u.find("://");
if (schemeEnd < 0) {
return ""; // couldn't parse scheme
return u.substr(hostEnd, query - hostEnd);
}
-
-string Request::query() const
+//------------------------------------------------------------------------------
+std::string Request::query() const
{
- string u(url());
+ std::string u(url());
int query = u.find('?');
if (query < 0) {
return ""; //no query string found
return u.substr(query); //includes question mark
}
-
-
-string Request::host() const
+//------------------------------------------------------------------------------
+std::string Request::host() const
{
- string hp(hostAndPort());
- int colonPos = hp.find(':');
- if (colonPos >= 0) {
- return hp.substr(0, colonPos); // trim off the colon and port
- } else {
- return hp; // no port specifier
- }
+ std::string hp(hostAndPort());
+ int colonPos = hp.find(':');
+ if (colonPos >= 0) {
+ return hp.substr(0, colonPos); // trim off the colon and port
+ } else {
+ return hp; // no port specifier
+ }
}
+//------------------------------------------------------------------------------
unsigned short Request::port() const
{
- string hp(hostAndPort());
- int colonPos = hp.find(':');
- if (colonPos >= 0) {
- return (unsigned short) strutils::to_int(hp.substr(colonPos + 1));
- } else {
- return DEFAULT_HTTP_PORT;
- }
+ std::string hp(hostAndPort());
+ int colonPos = hp.find(':');
+ if (colonPos >= 0) {
+ return (unsigned short) strutils::to_int(hp.substr(colonPos + 1));
+ } else {
+ return DEFAULT_HTTP_PORT;
+ }
}
-string Request::hostAndPort() const
+//------------------------------------------------------------------------------
+std::string Request::hostAndPort() const
{
- string u(url());
- int schemeEnd = u.find("://");
- if (schemeEnd < 0) {
- return ""; // couldn't parse scheme
- }
-
- int hostEnd = u.find('/', schemeEnd + 3);
- if (hostEnd < 0) { // all remainder of URL is host
- return u.substr(schemeEnd + 3);
- }
-
- return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
+ std::string u(url());
+ int schemeEnd = u.find("://");
+ if (schemeEnd < 0) {
+ return ""; // couldn't parse scheme
+ }
+
+ int hostEnd = u.find('/', schemeEnd + 3);
+ if (hostEnd < 0) { // all remainder of URL is host
+ return u.substr(schemeEnd + 3);
+ }
+
+ return u.substr(schemeEnd + 3, hostEnd - (schemeEnd + 3));
}
+//------------------------------------------------------------------------------
void Request::setResponseLength(unsigned int l)
{
- _responseLength = l;
+ _responseLength = l;
}
+//------------------------------------------------------------------------------
unsigned int Request::responseLength() const
{
-// if the server didn't supply a content length, use the number
-// of bytes we actually received (so far)
- if ((_responseLength == 0) && (_receivedBodyBytes > 0)) {
- return _receivedBodyBytes;
- }
-
- return _responseLength;
+ // if the server didn't supply a content length, use the number
+ // of bytes we actually received (so far)
+ if( (_responseLength == 0) && (_receivedBodyBytes > 0) )
+ return _receivedBodyBytes;
+
+ return _responseLength;
}
+//------------------------------------------------------------------------------
void Request::setFailure(int code, const std::string& reason)
{
- _responseStatus = code;
- _responseReason = reason;
- failed();
+ _responseStatus = code;
+ _responseReason = reason;
+ setReadyState(FAILED);
}
-void Request::failed()
+//------------------------------------------------------------------------------
+void Request::setReadyState(ReadyState state)
{
- SG_LOG(SG_IO, SG_INFO, "request failed:" << url() << " : "
- << responseCode() << "/" << responseReason());
+ _ready_state = state;
+ if( state == DONE )
+ {
+ if( _cb_done )
+ _cb_done(this);
+ onDone();
+ }
+ else if( state == FAILED )
+ {
+ if( _cb_fail )
+ _cb_fail(this);
+ onFail();
+ }
+ else
+ return;
+
+ if( _cb_always )
+ _cb_always(this);
+ onAlways();
}
-Request::HTTPVersion Request::decodeVersion(const string& v)
+//------------------------------------------------------------------------------
+void Request::abort()
{
- if (v == "HTTP/1.1") return HTTP_1_1;
- if (v == "HTTP/1.0") return HTTP_1_0;
- if (strutils::starts_with(v, "HTTP/0.")) return HTTP_0_x;
- return HTTP_VERSION_UNKNOWN;
+ abort("Request aborted.");
}
+//----------------------------------------------------------------------------
+void Request::abort(const std::string& reason)
+{
+ if( isComplete() )
+ return;
+
+ setFailure(-1, reason);
+ _willClose = true;
+}
+
+//------------------------------------------------------------------------------
bool Request::closeAfterComplete() const
{
-// for non HTTP/1.1 connections, assume server closes
- return _willClose || (_responseVersion != HTTP_1_1);
+ // for non HTTP/1.1 connections, assume server closes
+ return _willClose || (_responseVersion != HTTP_1_1);
}
-
-int Request::requestBodyLength() const
+
+//------------------------------------------------------------------------------
+bool Request::isComplete() const
{
- return -1;
+ return _ready_state == DONE || _ready_state == FAILED;
}
-std::string Request::requestBodyType() const
+//------------------------------------------------------------------------------
+bool Request::hasBodyData() const
{
- return "text/plain";
+ return !_request_media_type.empty();
}
-
-int Request::getBodyData(char*, int maxCount) const
+
+//------------------------------------------------------------------------------
+std::string Request::bodyType() const
{
- return 0;
+ return _request_media_type;
}
-} // of namespace HTTP
+//------------------------------------------------------------------------------
+size_t Request::bodyLength() const
+{
+ return _request_data.length();
+}
+//------------------------------------------------------------------------------
+size_t Request::getBodyData(char* s, size_t offset, size_t max_count) const
+{
+ size_t bytes_available = _request_data.size() - offset;
+ size_t bytes_to_read = std::min(bytes_available, max_count);
+
+ memcpy(s, _request_data.data() + offset, bytes_to_read);
+
+ return bytes_to_read;
+}
+
+} // of namespace HTTP
} // of namespace simgear
#include <map>
+#include <simgear/structure/map.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
#include <simgear/math/sg_types.hxx>
+#include <boost/function.hpp>
+
+class SGPropertyNode;
+
namespace simgear
{
-
namespace HTTP
{
-class Request : public SGReferenced
+class Request:
+ public SGReferenced
{
public:
+ typedef boost::function<void(Request*)> Callback;
+
+ enum ReadyState
+ {
+ UNSENT = 0,
+ OPENED,
+ HEADERS_RECEIVED,
+ LOADING,
+ DONE,
+ FAILED
+ };
+
virtual ~Request();
-
+
+ /**
+ *
+ */
+ StringMap& requestHeaders() { return _request_headers; }
+ const StringMap& requestHeaders() const { return _request_headers; }
+ std::string& requestHeader(const std::string& key)
+ { return _request_headers[key]; }
+ const std::string requestHeader(const std::string& key) const
+ { return _request_headers.get(key); }
+
+ /**
+ * Set the handler to be called when the request successfully completes.
+ *
+ * @note If the request is already complete, the handler is called
+ * immediately.
+ */
+ Request* done(const Callback& cb);
+
+ /**
+ * Set the handler to be called when the request completes or aborts with an
+ * error.
+ *
+ * @note If the request has already failed, the handler is called
+ * immediately.
+ */
+ Request* fail(const Callback& cb);
+
+ /**
+ * Set the handler to be called when the request either successfully
+ * completes or fails.
+ *
+ * @note If the request is already complete or has already failed, the
+ * handler is called immediately.
+ */
+ Request* always(const Callback& cb);
+
+ /**
+ * Set the data for the body of the request. The request is automatically
+ * send using the POST method.
+ *
+ * @param data Body data
+ * @param type Media Type (aka MIME) of the body data
+ */
+ void setBodyData( const std::string& data,
+ const std::string& type = "text/plain" );
+ void setBodyData( const SGPropertyNode* data );
+
virtual void setUrl(const std::string& url);
virtual std::string method() const
virtual unsigned short port() const;
virtual std::string query() const;
- virtual string_list requestHeaders() const;
- virtual std::string header(const std::string& name) const;
+ StringMap const& responseHeaders() const
+ { return _responseHeaders; }
virtual int responseCode() const
{ return _responseStatus; }
virtual std::string responseReason() const
{ return _responseReason; }
- void setResponseLength(unsigned int l);
+ void setResponseLength(unsigned int l);
virtual unsigned int responseLength() const;
/**
- * Query the size of the request body. -1 (the default value) means no
- * request body
+ * Check if request contains body data.
+ */
+ virtual bool hasBodyData() const;
+
+ /**
+ * Retrieve the request body content type.
+ */
+ virtual std::string bodyType() const;
+
+ /**
+ * Retrieve the size of the request body.
*/
- virtual int requestBodyLength() const;
+ virtual size_t bodyLength() const;
/**
* Retrieve the body data bytes. Will be passed the maximum body bytes
* to return in the buffer, and must return the actual number
* of bytes written.
*/
- virtual int getBodyData(char* s, int count) const;
-
- /**
- * retrieve the request body content type. Default is text/plain
- */
- virtual std::string requestBodyType() const;
+ virtual size_t getBodyData(char* s, size_t offset, size_t max_count) const;
/**
* running total of body bytes received so far. Can be used
HTTPVersion responseVersion() const
{ return _responseVersion; }
- static HTTPVersion decodeVersion(const std::string& v);
-
+ ReadyState readyState() const { return _ready_state; }
+
+ /**
+ * Request aborting this request.
+ */
+ void abort();
+
+ /**
+ * Request aborting this request and specify the reported reaseon for it.
+ */
+ void abort(const std::string& reason);
+
bool closeAfterComplete() const;
+ bool isComplete() const;
+
protected:
Request(const std::string& url, const std::string method = "GET");
virtual void responseHeader(const std::string& key, const std::string& value);
virtual void responseHeadersComplete();
virtual void responseComplete();
- virtual void failed();
virtual void gotBodyData(const char* s, int n);
+
+ virtual void onDone();
+ virtual void onFail();
+ virtual void onAlways();
+
private:
friend class Client;
friend class Connection;
friend class ContentDecoder;
+ Request(const Request&); // = delete;
+ Request& operator=(const Request&); // = delete;
+
void processBodyBytes(const char* s, int n);
void setFailure(int code, const std::string& reason);
+ void setReadyState(ReadyState state);
- std::string _method;
- std::string _url;
- HTTPVersion _responseVersion;
- int _responseStatus;
- std::string _responseReason;
- unsigned int _responseLength;
- unsigned int _receivedBodyBytes;
- bool _willClose;
-
- typedef std::map<std::string, std::string> HeaderDict;
- HeaderDict _responseHeaders;
+ std::string _method;
+ std::string _url;
+ StringMap _request_headers;
+ std::string _request_data;
+ std::string _request_media_type;
+
+ HTTPVersion _responseVersion;
+ int _responseStatus;
+ std::string _responseReason;
+ StringMap _responseHeaders;
+ unsigned int _responseLength;
+ unsigned int _receivedBodyBytes;
+
+ Callback _cb_done,
+ _cb_fail,
+ _cb_always;
+
+ ReadyState _ready_state;
+ bool _willClose;
};
typedef SGSharedPtr<Request> Request_ptr;
} // of namespace HTTP
-
} // of namespace simgear
#endif // of SG_HTTP_REQUEST_HXX
-
Request(repo->baseUrl, "PROPFIND"),
_repo(repo)
{
- }
-
- virtual string_list requestHeaders() const
- {
- string_list r;
- r.push_back("Depth");
- return r;
- }
-
- virtual string header(const string& name) const
- {
- if (name == "Depth") {
- return "0";
- }
-
- return string();
- }
-
- virtual string requestBodyType() const
- {
- return "application/xml; charset=\"utf-8\"";
- }
-
- virtual int requestBodyLength() const
- {
- return strlen(PROPFIND_REQUEST_BODY);
- }
-
- virtual int getBodyData(char* buf, int count) const
- {
- int bodyLen = strlen(PROPFIND_REQUEST_BODY);
- assert(count >= bodyLen);
- memcpy(buf, PROPFIND_REQUEST_BODY, bodyLen);
- return bodyLen;
+ requestHeader("Depth") = "0";
+ setBodyData( PROPFIND_REQUEST_BODY,
+ "application/xml; charset=\"utf-8\"" );
}
protected:
}
}
- virtual void responseComplete()
+ virtual void onDone()
{
if (responseCode() == 207) {
_davStatus.finishParse();
_davStatus.parseXML(s, n);
}
- virtual void failed()
+ virtual void onFail()
{
- HTTP::Request::failed();
+ HTTP::Request::onFail();
_repo->propFindFailed(this, SVNRepository::SVN_ERROR_SOCKET);
}
DAVMultiStatus _davStatus;
};
-class UpdateReportRequest : public HTTP::Request
+class UpdateReportRequest:
+ public HTTP::Request
{
public:
UpdateReportRequest(SVNRepoPrivate* repo,
const std::string& aVersionName,
bool startEmpty) :
HTTP::Request("", "REPORT"),
- _requestSent(0),
_parser(repo->p),
_repo(repo),
_failed(false)
{
setUrl(repo->vccUrl);
-
- _request =
+ std::string request =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
"<S:update-report send-all=\"true\" xmlns:S=\"svn:\">\n"
"<S:src-path>" + repo->baseUrl + "</S:src-path>\n"
- "<S:depth>unknown</S:depth>\n";
-
- _request += "<S:entry rev=\"" + aVersionName + "\" depth=\"infinity\" start-empty=\"true\"/>\n";
-
- if (!startEmpty) {
- string_list entries;
- _repo->rootCollection->mergeUpdateReportDetails(0, entries);
- BOOST_FOREACH(string e, entries) {
- _request += e + "\n";
- }
- }
+ "<S:depth>unknown</S:depth>\n"
+ "<S:entry rev=\"" + aVersionName + "\" depth=\"infinity\" start-empty=\"true\"/>\n";
- _request += "</S:update-report>";
- }
-
- virtual string requestBodyType() const
- {
- return "application/xml; charset=\"utf-8\"";
- }
+ if( !startEmpty )
+ {
+ string_list entries;
+ _repo->rootCollection->mergeUpdateReportDetails(0, entries);
+ BOOST_FOREACH(string e, entries)
+ {
+ request += e + "\n";
+ }
+ }
- virtual int requestBodyLength() const
- {
- return _request.size();
- }
+ request += "</S:update-report>";
- virtual int getBodyData(char* buf, int count) const
- {
- int len = std::min(count, requestBodyLength() - _requestSent);
- memcpy(buf, _request.c_str() + _requestSent, len);
- _requestSent += len;
- return len;
+ setBodyData(request, "application/xml; charset=\"utf-8\"");
}
protected:
- virtual void responseHeadersComplete()
- {
-
- }
-
- virtual void responseComplete()
+ virtual void onDone()
{
if (_failed) {
return;
}
}
- virtual void failed()
+ virtual void onFail()
{
- HTTP::Request::failed();
+ HTTP::Request::onFail();
_repo->updateFailed(this, SVNRepository::SVN_ERROR_SOCKET);
}
private:
- string _request;
- mutable int _requestSent;
SVNReportParser _parser;
SVNRepoPrivate* _repo;
bool _failed;
}
string key = h.substr(0, colonPos);
- _headers[key] = h.substr(colonPos + 1);
+ requestHeader(key) = h.substr(colonPos + 1);
}
- virtual string_list requestHeaders() const
- {
- string_list r;
- std::map<string, string>::const_iterator it;
- for (it = _headers.begin(); it != _headers.end(); ++it) {
- r.push_back(it->first);
- }
-
- return r;
- }
-
- virtual string header(const string& name) const
- {
- std::map<string, string>::const_iterator it = _headers.find(name);
- if (it == _headers.end()) {
- return string();
- }
-
- return it->second;
- }
protected:
- virtual void responseHeadersComplete()
- {
- }
-
- virtual void responseComplete()
+ virtual void onDone()
{
_complete = true;
}
private:
bool _complete;
SGFile* _file;
- std::map<string, string> _headers;
};
int main(int argc, char* argv[])
{
std::string terminator;
int bytesToCollect;
+
+protected:
virtual void handleBufferRead (NetBuffer& buffer) ;
public:
bool complete;
bool failed;
string bodyData;
- string bodyContentType;
-
+
TestRequest(const std::string& url, const std::string method = "GET") :
HTTP::Request(url, method),
complete(false)
{
- bodyContentType = "text/plain";
+
}
- std::map<string, string> sendHeaders;
std::map<string, string> headers;
protected:
- string_list requestHeaders() const
- {
- string_list r;
- std::map<string, string>::const_iterator it;
- for (it = sendHeaders.begin(); it != sendHeaders.end(); ++it) {
- r.push_back(it->first);
- }
- return r;
- }
- string header(const string& name) const
- {
- std::map<string, string>::const_iterator it = sendHeaders.find(name);
- if (it == sendHeaders.end()) {
- return string();
- }
-
- return it->second;
- }
-
- virtual void responseHeadersComplete()
- {
- }
-
- virtual void responseComplete()
+ virtual void onDone()
{
complete = true;
}
- virtual void failure()
+ virtual void onFail()
{
failed = true;
}
bodyData += string(s, n);
}
- virtual std::string requestBodyType() const
- {
- return bodyContentType;
- }
-
virtual void responseHeader(const string& header, const string& value)
{
headers[header] = value;
{
TestRequest* tr = new TestRequest("http://localhost:2000/test_headers");
HTTP::Request_ptr own(tr);
- tr->sendHeaders["X-Foo"] = "Bar";
- tr->sendHeaders["X-AnotherHeader"] = "A longer value";
+ tr->requestHeader("X-Foo") = "Bar";
+ tr->requestHeader("X-AnotherHeader") = "A longer value";
cl.makeRequest(tr);
waitForComplete(&cl, tr);
{
cout << "POST" << endl;
TestRequest* tr = new TestRequest("http://localhost:2000/test_post?foo=abc&bar=1234&username=johndoe", "POST");
- tr->bodyContentType = "application/x-www-form-urlencoded";
+ tr->setBodyData("", "application/x-www-form-urlencoded");
HTTP::Request_ptr own(tr);
cl.makeRequest(tr);
}
protected:
- virtual void responseHeadersComplete()
- {
-
- }
-
virtual void gotBodyData(const char* s, int n)
{
m_buffer += std::string(s, n);
}
- virtual void responseComplete()
+ virtual void onDone()
{
if (responseCode() != 200) {
SG_LOG(SG_GENERAL, SG_ALERT, "catalog download failure:" << m_owner->url());
m_owner->installProgress(m_buffer.size(), responseLength());
}
- virtual void responseComplete()
+ virtual void onDone()
{
if (responseCode() != 200) {
SG_LOG(SG_GENERAL, SG_ALERT, "download failure");