Merge branch 'next' of git://gitorious.org/fg/simgear into next
This commit is contained in:
@@ -92,6 +92,31 @@ check_function_exists(ftime HAVE_FTIME)
|
|||||||
check_function_exists(timegm HAVE_TIMEGM)
|
check_function_exists(timegm HAVE_TIMEGM)
|
||||||
check_function_exists(rint HAVE_RINT)
|
check_function_exists(rint HAVE_RINT)
|
||||||
|
|
||||||
|
|
||||||
|
if(HAVE_UNISTD_H)
|
||||||
|
set(CMAKE_REQUIRED_INCLUDES ${CMAKE_INCLUDE_PATH})
|
||||||
|
check_cxx_source_compiles(
|
||||||
|
"#include <unistd.h>
|
||||||
|
#if !defined(_POSIX_TIMERS) || (0 >= _POSIX_TIMERS)
|
||||||
|
#error clock_gettime is not supported
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main() { return 0; }
|
||||||
|
"
|
||||||
|
HAVE_CLOCK_GETTIME)
|
||||||
|
endif(HAVE_UNISTD_H)
|
||||||
|
|
||||||
|
set(RT_LIBRARY "")
|
||||||
|
if(HAVE_CLOCK_GETTIME)
|
||||||
|
check_function_exists(clock_gettime CLOCK_GETTIME_IN_LIBC)
|
||||||
|
if(NOT CLOCK_GETTIME_IN_LIBC)
|
||||||
|
check_library_exists(rt clock_gettime "" HAVE_RT)
|
||||||
|
if(HAVE_RT)
|
||||||
|
set(RT_LIBRARY rt)
|
||||||
|
endif(HAVE_RT)
|
||||||
|
endif(NOT CLOCK_GETTIME_IN_LIBC)
|
||||||
|
endif(HAVE_CLOCK_GETTIME)
|
||||||
|
|
||||||
SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually d on windows")
|
SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "add a postfix, usually d on windows")
|
||||||
SET(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
SET(CMAKE_RELEASE_POSTFIX "" CACHE STRING "add a postfix, usually empty on windows")
|
||||||
SET(CMAKE_RELWITHDEBINFO_POSTFIX "rd" CACHE STRING "add a postfix, usually empty on windows")
|
SET(CMAKE_RELWITHDEBINFO_POSTFIX "rd" CACHE STRING "add a postfix, usually empty on windows")
|
||||||
@@ -142,6 +167,11 @@ configure_file (
|
|||||||
"${PROJECT_BINARY_DIR}/simgear/simgear_config.h"
|
"${PROJECT_BINARY_DIR}/simgear/simgear_config.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# enable CTest / make test target
|
||||||
|
|
||||||
|
include (Dart)
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
|
install (FILES ${PROJECT_BINARY_DIR}/simgear/simgear_config.h DESTINATION include/simgear/)
|
||||||
add_subdirectory(simgear)
|
add_subdirectory(simgear)
|
||||||
|
|
||||||
@@ -156,3 +186,4 @@ ADD_CUSTOM_TARGET(uninstall
|
|||||||
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
"${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -527,6 +527,10 @@
|
|||||||
RelativePath="..\..\simgear\io\sg_socket_udp.hxx"
|
RelativePath="..\..\simgear\io\sg_socket_udp.hxx"
|
||||||
>
|
>
|
||||||
</File>
|
</File>
|
||||||
|
<File RelativePath="..\..\simgear\io\HTTPClient.cxx" ></File>
|
||||||
|
<File RelativePath="..\..\simgear\io\HTTPClient.hxx" ></File>
|
||||||
|
<File RelativePath="..\..\simgear\io\HTTPRequest.cxx" ></File>
|
||||||
|
<File RelativePath="..\..\simgear\io\HTTPRequest.hxx" ></File>
|
||||||
</Filter>
|
</Filter>
|
||||||
<Filter
|
<Filter
|
||||||
Name="Lib_sgmagvar"
|
Name="Lib_sgmagvar"
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ set(HEADERS
|
|||||||
sg_serial.hxx
|
sg_serial.hxx
|
||||||
sg_socket.hxx
|
sg_socket.hxx
|
||||||
sg_socket_udp.hxx
|
sg_socket_udp.hxx
|
||||||
|
HTTPClient.hxx
|
||||||
|
HTTPRequest.hxx
|
||||||
)
|
)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
@@ -28,6 +30,18 @@ set(SOURCES
|
|||||||
sg_serial.cxx
|
sg_serial.cxx
|
||||||
sg_socket.cxx
|
sg_socket.cxx
|
||||||
sg_socket_udp.cxx
|
sg_socket_udp.cxx
|
||||||
|
HTTPClient.cxx
|
||||||
|
HTTPRequest.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
simgear_component(io io "${SOURCES}" "${HEADERS}")
|
simgear_component(io io "${SOURCES}" "${HEADERS}")
|
||||||
|
|
||||||
|
add_executable(test_sock socktest.cxx)
|
||||||
|
target_link_libraries(test_sock sgio sgstructure sgdebug)
|
||||||
|
|
||||||
|
add_executable(test_http test_HTTP.cxx)
|
||||||
|
target_link_libraries(test_http
|
||||||
|
sgio sgstructure sgtiming sgmisc sgdebug
|
||||||
|
${RT_LIBRARY})
|
||||||
|
|
||||||
|
add_test(http ${EXECUTABLE_OUTPUT_PATH}/test_http)
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
#include "HTTPClient.hxx"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <cassert>
|
||||||
|
#include <list>
|
||||||
|
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
|
|
||||||
|
#include <simgear/io/sg_netChat.hxx>
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
|
#include <simgear/compiler.h>
|
||||||
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::stringstream;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace HTTP
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
class Connection : public NetChat
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Connection(Client* pr) :
|
||||||
|
client(pr),
|
||||||
|
state(STATE_IDLE)
|
||||||
|
{
|
||||||
|
setTerminator("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void connectToHost(const string& host)
|
||||||
|
{
|
||||||
|
open();
|
||||||
|
|
||||||
|
int colonPos = host.find(':');
|
||||||
|
if (colonPos > 0) {
|
||||||
|
string h = host.substr(0, colonPos);
|
||||||
|
int port = strutils::to_int(host.substr(colonPos + 1));
|
||||||
|
connect(h.c_str(), port);
|
||||||
|
} else {
|
||||||
|
connect(host.c_str(), 80 /* default port */);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void queueRequest(const Request_ptr& r)
|
||||||
|
{
|
||||||
|
if (!activeRequest) {
|
||||||
|
startRequest(r);
|
||||||
|
} else {
|
||||||
|
queuedRequests.push_back(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startRequest(const Request_ptr& r)
|
||||||
|
{
|
||||||
|
activeRequest = r;
|
||||||
|
state = STATE_IDLE;
|
||||||
|
bodyTransferSize = 0;
|
||||||
|
|
||||||
|
stringstream headerData;
|
||||||
|
string path = r->path();
|
||||||
|
if (!client->proxyHost().empty()) {
|
||||||
|
path = "http://" + r->host() + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
int requestTime = 0;
|
||||||
|
headerData << r->method() << " " << path << " HTTP/1.1 " << client->userAgent() << "\r\n";
|
||||||
|
headerData << "Host: " << r->host() << "\r\n";
|
||||||
|
headerData << "X-Time: " << requestTime << "\r\n";
|
||||||
|
|
||||||
|
if (!client->proxyAuth().empty()) {
|
||||||
|
headerData << "Proxy-Authorization: " << client->proxyAuth() << "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOST_FOREACH(string h, r->requestHeaders()) {
|
||||||
|
headerData << h << ": " << r->header(h) << "\r\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
headerData << "\r\n"; // final CRLF to terminate the headers
|
||||||
|
|
||||||
|
// TODO - add request body support for PUT, etc operations
|
||||||
|
|
||||||
|
push(headerData.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void collectIncomingData(const char* s, int n)
|
||||||
|
{
|
||||||
|
if (state == STATE_GETTING_BODY) {
|
||||||
|
activeRequest->gotBodyData(s, n);
|
||||||
|
} else {
|
||||||
|
buffer += string(s, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void foundTerminator(void)
|
||||||
|
{
|
||||||
|
switch (state) {
|
||||||
|
case STATE_IDLE:
|
||||||
|
activeRequest->responseStart(buffer);
|
||||||
|
state = STATE_GETTING_HEADERS;
|
||||||
|
buffer.clear();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_GETTING_HEADERS:
|
||||||
|
processHeader();
|
||||||
|
buffer.clear();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STATE_GETTING_BODY:
|
||||||
|
responseComplete();
|
||||||
|
state = STATE_IDLE;
|
||||||
|
setTerminator("\r\n");
|
||||||
|
|
||||||
|
if (!queuedRequests.empty()) {
|
||||||
|
Request_ptr next = queuedRequests.front();
|
||||||
|
queuedRequests.pop_front();
|
||||||
|
startRequest(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void processHeader()
|
||||||
|
{
|
||||||
|
string h = strutils::simplify(buffer);
|
||||||
|
if (h.empty()) { // blank line terminates headers
|
||||||
|
headersComplete();
|
||||||
|
|
||||||
|
if (bodyTransferSize > 0) {
|
||||||
|
state = STATE_GETTING_BODY;
|
||||||
|
setByteCount(bodyTransferSize);
|
||||||
|
} else {
|
||||||
|
responseComplete();
|
||||||
|
state = STATE_IDLE; // no response body, we're done
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int colonPos = buffer.find(':');
|
||||||
|
if (colonPos < 0) {
|
||||||
|
SG_LOG(SG_IO, SG_WARN, "malformed HTTP response header:" << h);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string key = strutils::simplify(buffer.substr(0, colonPos));
|
||||||
|
string lkey = boost::to_lower_copy(key);
|
||||||
|
string value = strutils::strip(buffer.substr(colonPos + 1));
|
||||||
|
|
||||||
|
if (lkey == "content-length" && (bodyTransferSize <= 0)) {
|
||||||
|
bodyTransferSize = strutils::to_int(value);
|
||||||
|
} else if (lkey == "transfer-length") {
|
||||||
|
bodyTransferSize = strutils::to_int(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
activeRequest->responseHeader(lkey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void headersComplete()
|
||||||
|
{
|
||||||
|
activeRequest->responseHeadersComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
void responseComplete()
|
||||||
|
{
|
||||||
|
activeRequest->responseComplete();
|
||||||
|
client->requestFinished(this);
|
||||||
|
activeRequest = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ConnectionState {
|
||||||
|
STATE_IDLE = 0,
|
||||||
|
STATE_GETTING_HEADERS,
|
||||||
|
STATE_GETTING_BODY
|
||||||
|
};
|
||||||
|
|
||||||
|
Client* client;
|
||||||
|
Request_ptr activeRequest;
|
||||||
|
ConnectionState state;
|
||||||
|
std::string buffer;
|
||||||
|
int bodyTransferSize;
|
||||||
|
|
||||||
|
std::list<Request_ptr> queuedRequests;
|
||||||
|
};
|
||||||
|
|
||||||
|
Client::Client()
|
||||||
|
{
|
||||||
|
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::makeRequest(const Request_ptr& r)
|
||||||
|
{
|
||||||
|
string host = r->host();
|
||||||
|
if (!_proxy.empty()) {
|
||||||
|
host = _proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_connections.find(host) == _connections.end()) {
|
||||||
|
Connection* con = new Connection(this);
|
||||||
|
con->connectToHost(host);
|
||||||
|
_connections[host] = con;
|
||||||
|
}
|
||||||
|
|
||||||
|
_connections[host]->queueRequest(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::requestFinished(Connection* con)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::setUserAgent(const string& ua)
|
||||||
|
{
|
||||||
|
_userAgent = ua;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::setProxy(const string& proxy, const string& auth)
|
||||||
|
{
|
||||||
|
_proxy = proxy;
|
||||||
|
_proxyAuth = auth;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // of namespace HTTP
|
||||||
|
|
||||||
|
} // of namespace simgear
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#ifndef SG_HTTP_CLIENT_HXX
|
||||||
|
#define SG_HTTP_CLIENT_HXX
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <simgear/io/HTTPRequest.hxx>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace HTTP
|
||||||
|
{
|
||||||
|
|
||||||
|
class Connection;
|
||||||
|
|
||||||
|
class Client
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Client();
|
||||||
|
|
||||||
|
void makeRequest(const Request_ptr& r);
|
||||||
|
|
||||||
|
void setUserAgent(const std::string& ua);
|
||||||
|
void setProxy(const std::string& proxy, const std::string& auth = "");
|
||||||
|
|
||||||
|
const std::string& userAgent() const
|
||||||
|
{ return _userAgent; }
|
||||||
|
|
||||||
|
const std::string& proxyHost() const
|
||||||
|
{ return _proxy; }
|
||||||
|
|
||||||
|
const std::string& proxyAuth() const
|
||||||
|
{ return _proxyAuth; }
|
||||||
|
private:
|
||||||
|
void requestFinished(Connection* con);
|
||||||
|
|
||||||
|
friend class Connection;
|
||||||
|
|
||||||
|
std::string _userAgent;
|
||||||
|
std::string _proxy;
|
||||||
|
std::string _proxyAuth;
|
||||||
|
|
||||||
|
// connections by host
|
||||||
|
std::map<std::string, Connection*> _connections;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // of namespace HTTP
|
||||||
|
|
||||||
|
} // of namespace simgear
|
||||||
|
|
||||||
|
#endif // of SG_HTTP_CLIENT_HXX
|
||||||
@@ -0,0 +1,133 @@
|
|||||||
|
#include "HTTPRequest.hxx"
|
||||||
|
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
|
#include <simgear/compiler.h>
|
||||||
|
#include <simgear/debug/logstream.hxx>
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::map;
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using std::cout;
|
||||||
|
using std::cerr;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace HTTP
|
||||||
|
{
|
||||||
|
|
||||||
|
Request::Request(const string& url, const string method) :
|
||||||
|
_method(method),
|
||||||
|
_url(url)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Request::~Request()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
string_list Request::requestHeaders() const
|
||||||
|
{
|
||||||
|
string_list r;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
string Request::header(const std::string& name) const
|
||||||
|
{
|
||||||
|
return string();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::responseStart(const string& r)
|
||||||
|
{
|
||||||
|
const int maxSplit = 2; // HTTP/1.1 nnn status code
|
||||||
|
string_list parts = strutils::split(r, NULL, maxSplit);
|
||||||
|
_responseStatus = strutils::to_int(parts[1]);
|
||||||
|
_responseReason = parts[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::responseHeader(const string& key, const string& value)
|
||||||
|
{
|
||||||
|
_responseHeaders[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::responseHeadersComplete()
|
||||||
|
{
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::gotBodyData(const char* s, int n)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::responseComplete()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
string Request::scheme() const
|
||||||
|
{
|
||||||
|
int firstColon = url().find(":");
|
||||||
|
if (firstColon > 0) {
|
||||||
|
return url().substr(0, firstColon);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""; // couldn't parse scheme
|
||||||
|
}
|
||||||
|
|
||||||
|
string Request::path() 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) {
|
||||||
|
return ""; // couldn't parse host
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
string Request::host() 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
int Request::contentLength() const
|
||||||
|
{
|
||||||
|
HeaderDict::const_iterator it = _responseHeaders.find("content-length");
|
||||||
|
if (it == _responseHeaders.end()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return strutils::to_int(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // of namespace HTTP
|
||||||
|
|
||||||
|
} // of namespace simgear
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#ifndef SG_HTTP_REQUEST_HXX
|
||||||
|
#define SG_HTTP_REQUEST_HXX
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include <simgear/structure/SGReferenced.hxx>
|
||||||
|
#include <simgear/structure/SGSharedPtr.hxx>
|
||||||
|
#include <simgear/math/sg_types.hxx>
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace HTTP
|
||||||
|
{
|
||||||
|
|
||||||
|
class Request : public SGReferenced
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Request();
|
||||||
|
|
||||||
|
virtual std::string method() const
|
||||||
|
{ return _method; }
|
||||||
|
virtual std::string url() const
|
||||||
|
{ return _url; }
|
||||||
|
|
||||||
|
virtual std::string scheme() const;
|
||||||
|
virtual std::string path() const;
|
||||||
|
virtual std::string host() const; // host, including port
|
||||||
|
|
||||||
|
virtual string_list requestHeaders() const;
|
||||||
|
virtual std::string header(const std::string& name) const;
|
||||||
|
|
||||||
|
virtual int responseCode() const
|
||||||
|
{ return _responseStatus; }
|
||||||
|
|
||||||
|
virtual std::string resposeReason() const
|
||||||
|
{ return _responseReason; }
|
||||||
|
|
||||||
|
virtual int contentLength() const;
|
||||||
|
protected:
|
||||||
|
friend class Connection;
|
||||||
|
|
||||||
|
Request(const std::string& url, const std::string method = "get");
|
||||||
|
|
||||||
|
virtual void responseStart(const std::string& r);
|
||||||
|
virtual void responseHeader(const std::string& key, const std::string& value);
|
||||||
|
virtual void responseHeadersComplete();
|
||||||
|
virtual void responseComplete();
|
||||||
|
|
||||||
|
virtual void gotBodyData(const char* s, int n);
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::string _method;
|
||||||
|
std::string _url;
|
||||||
|
int _responseStatus;
|
||||||
|
std::string _responseReason;
|
||||||
|
|
||||||
|
typedef std::map<std::string, std::string> HeaderDict;
|
||||||
|
HeaderDict _responseHeaders;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef SGSharedPtr<Request> Request_ptr;
|
||||||
|
|
||||||
|
} // of namespace HTTP
|
||||||
|
|
||||||
|
} // of namespace simgear
|
||||||
|
|
||||||
|
#endif // of SG_HTTP_REQUEST_HXX
|
||||||
|
|
||||||
@@ -13,7 +13,9 @@ include_HEADERS = \
|
|||||||
raw_socket.hxx \
|
raw_socket.hxx \
|
||||||
sg_netBuffer.hxx \
|
sg_netBuffer.hxx \
|
||||||
sg_netChannel.hxx \
|
sg_netChannel.hxx \
|
||||||
sg_netChat.hxx
|
sg_netChat.hxx \
|
||||||
|
HTTPClient.hxx \
|
||||||
|
HTTPRequest.hxx
|
||||||
|
|
||||||
libsgio_a_SOURCES = \
|
libsgio_a_SOURCES = \
|
||||||
iochannel.cxx \
|
iochannel.cxx \
|
||||||
@@ -26,7 +28,9 @@ libsgio_a_SOURCES = \
|
|||||||
raw_socket.cxx \
|
raw_socket.cxx \
|
||||||
sg_netBuffer.cxx \
|
sg_netBuffer.cxx \
|
||||||
sg_netChannel.cxx \
|
sg_netChannel.cxx \
|
||||||
sg_netChat.cxx
|
sg_netChat.cxx \
|
||||||
|
HTTPClient.cxx \
|
||||||
|
HTTPRequest.cxx
|
||||||
|
|
||||||
INCLUDES = -I$(top_srcdir)
|
INCLUDES = -I$(top_srcdir)
|
||||||
|
|
||||||
|
|||||||
@@ -26,14 +26,16 @@
|
|||||||
#include <simgear/io/sg_netChat.hxx>
|
#include <simgear/io/sg_netChat.hxx>
|
||||||
|
|
||||||
#include <cstring> // for strdup
|
#include <cstring> // for strdup
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
namespace simgear {
|
namespace simgear {
|
||||||
|
|
||||||
void
|
void
|
||||||
NetChat::setTerminator (const char* t)
|
NetChat::setTerminator (const char* t)
|
||||||
{
|
{
|
||||||
if (terminator) delete[] terminator;
|
if (terminator) free(terminator);
|
||||||
terminator = strdup(t);
|
terminator = strdup(t);
|
||||||
|
bytesToCollect = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char*
|
const char*
|
||||||
@@ -42,6 +44,15 @@ NetChat::getTerminator (void)
|
|||||||
return terminator;
|
return terminator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
NetChat::setByteCount(int count)
|
||||||
|
{
|
||||||
|
if (terminator) free(terminator);
|
||||||
|
terminator = NULL;
|
||||||
|
bytesToCollect = count;
|
||||||
|
}
|
||||||
|
|
||||||
// return the size of the largest prefix of needle at the end
|
// return the size of the largest prefix of needle at the end
|
||||||
// of haystack
|
// of haystack
|
||||||
|
|
||||||
@@ -90,11 +101,21 @@ NetChat::handleBufferRead (NetBuffer& in_buffer)
|
|||||||
// with a single recv().
|
// with a single recv().
|
||||||
|
|
||||||
while (in_buffer.getLength()) {
|
while (in_buffer.getLength()) {
|
||||||
|
|
||||||
// special case where we're not using a terminator
|
// special case where we're not using a terminator
|
||||||
if (terminator == 0 || *terminator == 0) {
|
if (terminator == 0 || *terminator == 0) {
|
||||||
collectIncomingData (in_buffer.getData(),in_buffer.getLength());
|
if ( bytesToCollect > 0) {
|
||||||
in_buffer.remove ();
|
const int toRead = std::min(in_buffer.getLength(), bytesToCollect);
|
||||||
|
collectIncomingData(in_buffer.getData(), toRead);
|
||||||
|
in_buffer.remove(0, toRead);
|
||||||
|
bytesToCollect -= toRead;
|
||||||
|
if (bytesToCollect == 0) { // read all requested bytes
|
||||||
|
foundTerminator();
|
||||||
|
}
|
||||||
|
} else { // read the whole lot
|
||||||
|
collectIncomingData (in_buffer.getData(),in_buffer.getLength());
|
||||||
|
in_buffer.remove ();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,8 @@
|
|||||||
#ifndef SG_NET_CHAT_H
|
#ifndef SG_NET_CHAT_H
|
||||||
#define SG_NET_CHAT_H
|
#define SG_NET_CHAT_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <cstdlib>
|
||||||
#include <simgear/io/sg_netBuffer.hxx>
|
#include <simgear/io/sg_netBuffer.hxx>
|
||||||
|
|
||||||
namespace simgear
|
namespace simgear
|
||||||
@@ -69,16 +71,25 @@ namespace simgear
|
|||||||
class NetChat : public NetBufferChannel
|
class NetChat : public NetBufferChannel
|
||||||
{
|
{
|
||||||
char* terminator;
|
char* terminator;
|
||||||
|
int bytesToCollect;
|
||||||
virtual void handleBufferRead (NetBuffer& buffer) ;
|
virtual void handleBufferRead (NetBuffer& buffer) ;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
NetChat () : terminator (0) {}
|
NetChat () :
|
||||||
|
terminator (NULL),
|
||||||
|
bytesToCollect(-1)
|
||||||
|
{}
|
||||||
|
|
||||||
void setTerminator (const char* t);
|
void setTerminator (const char* t);
|
||||||
const char* getTerminator (void);
|
const char* getTerminator (void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set byte count to collect - 'foundTerminator' will be called once
|
||||||
|
* this many bytes have been collected
|
||||||
|
*/
|
||||||
|
void setByteCount(int bytes);
|
||||||
|
|
||||||
bool push (const char* s);
|
bool push (const char* s);
|
||||||
|
|
||||||
virtual void collectIncomingData (const char* s, int n) {}
|
virtual void collectIncomingData (const char* s, int n) {}
|
||||||
|
|||||||
@@ -0,0 +1,274 @@
|
|||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
|
|
||||||
|
#include "HTTPClient.hxx"
|
||||||
|
#include "HTTPRequest.hxx"
|
||||||
|
|
||||||
|
#include <simgear/io/sg_netChat.hxx>
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
|
#include <simgear/timing/timestamp.hxx>
|
||||||
|
|
||||||
|
using std::cout;
|
||||||
|
using std::cerr;
|
||||||
|
using std::endl;
|
||||||
|
using std::string;
|
||||||
|
using std::stringstream;
|
||||||
|
|
||||||
|
using namespace simgear;
|
||||||
|
|
||||||
|
const char* BODY1 = "The quick brown fox jumps over a lazy dog.";
|
||||||
|
|
||||||
|
const int body2Size = 8 * 1024;
|
||||||
|
char body2[body2Size];
|
||||||
|
|
||||||
|
#define COMPARE(a, b) \
|
||||||
|
if ((a) != (b)) { \
|
||||||
|
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||||
|
cerr << "\tgot:" << a << endl; \
|
||||||
|
exit(1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VERIFY(a) \
|
||||||
|
if (!(a)) { \
|
||||||
|
cerr << "failed:" << #a << endl; \
|
||||||
|
exit(1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestRequest : public HTTP::Request
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool complete;
|
||||||
|
string bodyData;
|
||||||
|
|
||||||
|
TestRequest(const std::string& url) :
|
||||||
|
HTTP::Request(url),
|
||||||
|
complete(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void responseHeadersComplete()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void responseComplete()
|
||||||
|
{
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void gotBodyData(const char* s, int n)
|
||||||
|
{
|
||||||
|
bodyData += string(s, n);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestServerChannel : public NetChat
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum State
|
||||||
|
{
|
||||||
|
STATE_IDLE = 0,
|
||||||
|
STATE_HEADERS,
|
||||||
|
STATE_REQUEST_BODY
|
||||||
|
};
|
||||||
|
|
||||||
|
TestServerChannel()
|
||||||
|
{
|
||||||
|
state = STATE_IDLE;
|
||||||
|
setTerminator("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void collectIncomingData(const char* s, int n)
|
||||||
|
{
|
||||||
|
buffer += string(s, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void foundTerminator(void)
|
||||||
|
{
|
||||||
|
if (state == STATE_IDLE) {
|
||||||
|
state = STATE_HEADERS;
|
||||||
|
string_list line = strutils::split(buffer, NULL, 3);
|
||||||
|
if (line.size() < 4) {
|
||||||
|
cerr << "malformed request:" << buffer << endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
method = line[0];
|
||||||
|
path = line[1];
|
||||||
|
httpVersion = line[2];
|
||||||
|
userAgent = line[3];
|
||||||
|
requestHeaders.clear();
|
||||||
|
buffer.clear();
|
||||||
|
} else if (state == STATE_HEADERS) {
|
||||||
|
string s = strutils::simplify(buffer);
|
||||||
|
if (s.empty()) {
|
||||||
|
buffer.clear();
|
||||||
|
receivedRequestHeaders();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int colonPos = buffer.find(':');
|
||||||
|
if (colonPos < 0) {
|
||||||
|
cerr << "malformed HTTP response header:" << buffer << endl;
|
||||||
|
buffer.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string key = strutils::simplify(buffer.substr(0, colonPos));
|
||||||
|
string lkey = boost::to_lower_copy(key);
|
||||||
|
string value = strutils::strip(buffer.substr(colonPos + 1));
|
||||||
|
requestHeaders[lkey] = value;
|
||||||
|
buffer.clear();
|
||||||
|
} else if (state == STATE_REQUEST_BODY) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void receivedRequestHeaders()
|
||||||
|
{
|
||||||
|
state = STATE_IDLE;
|
||||||
|
if (path == "/test1") {
|
||||||
|
string contentStr(BODY1);
|
||||||
|
stringstream d;
|
||||||
|
d << "HTTP1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
|
||||||
|
d << "Content-Length:" << contentStr.size() << "\r\n";
|
||||||
|
d << "\r\n"; // final CRLF to terminate the headers
|
||||||
|
d << contentStr;
|
||||||
|
push(d.str().c_str());
|
||||||
|
} else if (path == "/test2") {
|
||||||
|
stringstream d;
|
||||||
|
d << "HTTP1.1 " << 200 << " " << reasonForCode(200) << "\r\n";
|
||||||
|
d << "Content-Length:" << body2Size << "\r\n";
|
||||||
|
d << "\r\n"; // final CRLF to terminate the headers
|
||||||
|
push(d.str().c_str());
|
||||||
|
bufferSend(body2, body2Size);
|
||||||
|
cout << "sent body2" << endl;
|
||||||
|
} else {
|
||||||
|
sendErrorResponse(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendErrorResponse(int code)
|
||||||
|
{
|
||||||
|
cerr << "sending error " << code << " for " << path << endl;
|
||||||
|
stringstream headerData;
|
||||||
|
headerData << "HTTP1.1 " << code << " " << reasonForCode(code) << "\r\n";
|
||||||
|
headerData << "\r\n"; // final CRLF to terminate the headers
|
||||||
|
push(headerData.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
string reasonForCode(int code)
|
||||||
|
{
|
||||||
|
switch (code) {
|
||||||
|
case 200: return "OK";
|
||||||
|
case 404: return "not found";
|
||||||
|
default: return "unknown code";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
State state;
|
||||||
|
string buffer;
|
||||||
|
string method;
|
||||||
|
string path;
|
||||||
|
string httpVersion;
|
||||||
|
string userAgent;
|
||||||
|
std::map<string, string> requestHeaders;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestServer : public NetChannel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TestServer()
|
||||||
|
{
|
||||||
|
open();
|
||||||
|
bind(NULL, 2000); // localhost, any port
|
||||||
|
listen(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~TestServer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool writable (void) { return false ; }
|
||||||
|
|
||||||
|
virtual void handleAccept (void)
|
||||||
|
{
|
||||||
|
simgear::IPAddress addr ;
|
||||||
|
int handle = accept ( &addr ) ;
|
||||||
|
|
||||||
|
TestServerChannel* chan = new TestServerChannel();
|
||||||
|
chan->setHandle(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void waitForComplete(TestRequest* tr)
|
||||||
|
{
|
||||||
|
SGTimeStamp start(SGTimeStamp::now());
|
||||||
|
while (start.elapsedMSec() < 1000) {
|
||||||
|
NetChannel::poll(10);
|
||||||
|
if (tr->complete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cerr << "timed out" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
TestServer s;
|
||||||
|
|
||||||
|
HTTP::Client cl;
|
||||||
|
|
||||||
|
// test URL parsing
|
||||||
|
TestRequest* tr1 = new TestRequest("http://localhost:2000/test1?foo=bar");
|
||||||
|
COMPARE(tr1->scheme(), "http");
|
||||||
|
COMPARE(tr1->host(), "localhost:2000");
|
||||||
|
COMPARE(tr1->path(), "/test1");
|
||||||
|
|
||||||
|
// basic get request
|
||||||
|
{
|
||||||
|
TestRequest* tr = new TestRequest("http://localhost:2000/test1");
|
||||||
|
HTTP::Request_ptr own(tr);
|
||||||
|
cl.makeRequest(tr);
|
||||||
|
|
||||||
|
waitForComplete(tr);
|
||||||
|
COMPARE(tr->responseCode(), 200);
|
||||||
|
COMPARE(tr->contentLength(), strlen(BODY1));
|
||||||
|
COMPARE(tr->bodyData, string(BODY1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// larger get request
|
||||||
|
for (int i=0; i<body2Size; ++i) {
|
||||||
|
body2[i] = (i << 4) | (i >> 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TestRequest* tr = new TestRequest("http://localhost:2000/test2");
|
||||||
|
HTTP::Request_ptr own(tr);
|
||||||
|
cl.makeRequest(tr);
|
||||||
|
waitForComplete(tr);
|
||||||
|
COMPARE(tr->responseCode(), 200);
|
||||||
|
COMPARE(tr->contentLength(), body2Size);
|
||||||
|
COMPARE(tr->bodyData, string(body2, body2Size));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test 404
|
||||||
|
{
|
||||||
|
TestRequest* tr = new TestRequest("http://localhost:2000/not-found");
|
||||||
|
HTTP::Request_ptr own(tr);
|
||||||
|
cl.makeRequest(tr);
|
||||||
|
waitForComplete(tr);
|
||||||
|
COMPARE(tr->responseCode(), 404);
|
||||||
|
COMPARE(tr->contentLength(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
cout << "all tests passed ok" << endl;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@@ -5,3 +5,8 @@ set(HEADERS magvar.hxx coremag.hxx)
|
|||||||
set(SOURCES magvar.cxx coremag.cxx)
|
set(SOURCES magvar.cxx coremag.cxx)
|
||||||
|
|
||||||
simgear_component(magvar magvar "${SOURCES}" "${HEADERS}")
|
simgear_component(magvar magvar "${SOURCES}" "${HEADERS}")
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(test_magvar testmagvar.cxx )
|
||||||
|
target_link_libraries(test_magvar sgmagvar)
|
||||||
|
|
||||||
|
|||||||
@@ -45,3 +45,10 @@ set(SOURCES
|
|||||||
|
|
||||||
simgear_component(math math "${SOURCES}" "${HEADERS}")
|
simgear_component(math math "${SOURCES}" "${HEADERS}")
|
||||||
|
|
||||||
|
add_executable(math_test SGMathTest.cxx)
|
||||||
|
target_link_libraries(math_test sgmath sgstructure sgdebug)
|
||||||
|
add_test(math ${EXECUTABLE_OUTPUT_PATH}/math_test)
|
||||||
|
|
||||||
|
add_executable(geometry_test SGGeometryTest.cxx)
|
||||||
|
target_link_libraries(geometry_test sgmath sgstructure sgdebug)
|
||||||
|
add_test(geometry ${EXECUTABLE_OUTPUT_PATH}/geometry_test)
|
||||||
|
|||||||
@@ -22,8 +22,6 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <plib/sg.h>
|
|
||||||
|
|
||||||
#include "SGMath.hxx"
|
#include "SGMath.hxx"
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@@ -33,3 +33,11 @@ set(SOURCES
|
|||||||
)
|
)
|
||||||
|
|
||||||
simgear_component(misc misc "${SOURCES}" "${HEADERS}")
|
simgear_component(misc misc "${SOURCES}" "${HEADERS}")
|
||||||
|
|
||||||
|
add_executable(test_tabbed_values tabbed_values_test.cxx)
|
||||||
|
add_test(tabbed_values ${EXECUTABLE_OUTPUT_PATH}/test_tabbed_values)
|
||||||
|
target_link_libraries(test_tabbed_values sgmisc)
|
||||||
|
|
||||||
|
add_executable(test_strings strutils_test.cxx )
|
||||||
|
add_test(test_strings ${EXECUTABLE_OUTPUT_PATH}/test_strings)
|
||||||
|
target_link_libraries(test_strings sgmisc)
|
||||||
|
|||||||
@@ -213,5 +213,41 @@ namespace simgear {
|
|||||||
return (n != string::npos) && (n == s.length() - substr.length());
|
return (n != string::npos) && (n == s.length() - substr.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string simplify(const string& s)
|
||||||
|
{
|
||||||
|
string result; // reserve size of 's'?
|
||||||
|
string::const_iterator it = s.begin(),
|
||||||
|
end = s.end();
|
||||||
|
|
||||||
|
// advance to first non-space char - simplifes logic in main loop,
|
||||||
|
// since we can always prepend a single space when we see a
|
||||||
|
// space -> non-space transition
|
||||||
|
for (; (it != end) && isspace(*it); ++it) { /* nothing */ }
|
||||||
|
|
||||||
|
bool lastWasSpace = false;
|
||||||
|
for (; it != end; ++it) {
|
||||||
|
char c = *it;
|
||||||
|
if (isspace(c)) {
|
||||||
|
lastWasSpace = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastWasSpace) {
|
||||||
|
result.push_back(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
lastWasSpace = false;
|
||||||
|
result.push_back(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int to_int(const std::string& s)
|
||||||
|
{
|
||||||
|
return atoi(s.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace strutils
|
} // end namespace strutils
|
||||||
|
|
||||||
} // end namespace simgear
|
} // end namespace simgear
|
||||||
|
|||||||
@@ -116,6 +116,14 @@ namespace simgear {
|
|||||||
*/
|
*/
|
||||||
bool ends_with( const std::string & s, const std::string & substr );
|
bool ends_with( const std::string & s, const std::string & substr );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strip all leading/trailing whitespace, and transform all interal
|
||||||
|
* whitespace into a single ' ' character - i.e newlines/carriage returns/
|
||||||
|
* tabs/multiple spaces will be condensed.
|
||||||
|
*/
|
||||||
|
std::string simplify(const std::string& s);
|
||||||
|
|
||||||
|
int to_int(const std::string& s);
|
||||||
} // end namespace strutils
|
} // end namespace strutils
|
||||||
} // end namespace simgear
|
} // end namespace simgear
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
// Test harness.
|
||||||
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include <simgear/compiler.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "strutils.hxx"
|
||||||
|
|
||||||
|
using std::cout;
|
||||||
|
using std::cerr;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
|
using namespace simgear::strutils;
|
||||||
|
|
||||||
|
#define COMPARE(a, b) \
|
||||||
|
if ((a) != (b)) { \
|
||||||
|
cerr << "failed:" << #a << " != " << #b << endl; \
|
||||||
|
exit(1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VERIFY(a) \
|
||||||
|
if (!(a)) { \
|
||||||
|
cerr << "failed:" << #a << endl; \
|
||||||
|
exit(1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
int main (int ac, char ** av)
|
||||||
|
{
|
||||||
|
std::string a("abcd");
|
||||||
|
COMPARE(strip(a), a);
|
||||||
|
COMPARE(strip(" a "), "a");
|
||||||
|
COMPARE(lstrip(" a "), "a ");
|
||||||
|
COMPARE(rstrip("\ta "), "\ta");
|
||||||
|
// check internal spacing is preserved
|
||||||
|
COMPARE(strip("\t \na \t b\r \n "), "a \t b");
|
||||||
|
|
||||||
|
|
||||||
|
VERIFY(starts_with("banana", "ban"));
|
||||||
|
VERIFY(!starts_with("abanana", "ban"));
|
||||||
|
VERIFY(starts_with("banana", "banana")); // pass - string starts with itself
|
||||||
|
VERIFY(!starts_with("ban", "banana")); // fail - original string is prefix of
|
||||||
|
|
||||||
|
VERIFY(ends_with("banana", "ana"));
|
||||||
|
VERIFY(ends_with("foo.text", ".text"));
|
||||||
|
VERIFY(!ends_with("foo.text", ".html"));
|
||||||
|
|
||||||
|
COMPARE(simplify("\ta\t b \nc\n\r \r\n"), "a b c");
|
||||||
|
COMPARE(simplify("The quick - brown dog!"), "The quick - brown dog!");
|
||||||
|
COMPARE(simplify("\r\n \r\n \t \r"), "");
|
||||||
|
|
||||||
|
COMPARE(to_int("999"), 999);
|
||||||
|
COMPARE(to_int("0000000"), 0);
|
||||||
|
COMPARE(to_int("-10000"), -10000);
|
||||||
|
|
||||||
|
cout << "all tests passed successfully!" << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -20,3 +20,11 @@ set(SOURCES
|
|||||||
)
|
)
|
||||||
|
|
||||||
simgear_component(props props "${SOURCES}" "${HEADERS}")
|
simgear_component(props props "${SOURCES}" "${HEADERS}")
|
||||||
|
|
||||||
|
add_executable(test_props props_test.cxx)
|
||||||
|
target_link_libraries(test_props sgprops sgxml sgstructure sgmisc sgdebug)
|
||||||
|
add_test(test_props ${EXECUTABLE_OUTPUT_PATH}/test_props)
|
||||||
|
|
||||||
|
add_executable(test_propertyObject propertyObject_test.cxx)
|
||||||
|
target_link_libraries(test_propertyObject sgprops sgstructure sgdebug)
|
||||||
|
add_test(test_propertyObject ${EXECUTABLE_OUTPUT_PATH}/test_propertyObject)
|
||||||
|
|||||||
@@ -569,16 +569,18 @@ int SGTerraSync::SvnThread::svnClientSetup(void)
|
|||||||
// not supported under msvc 7.1 ( code inside svn_cmdline_init )
|
// not supported under msvc 7.1 ( code inside svn_cmdline_init )
|
||||||
if (svn_cmdline_init("terrasync", 0) != EXIT_SUCCESS)
|
if (svn_cmdline_init("terrasync", 0) != EXIT_SUCCESS)
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
// revert locale setting
|
||||||
|
setlocale(LC_ALL,"C");
|
||||||
#else
|
#else
|
||||||
|
/* svn_cmdline_init configures the locale. Setup environment to ensure the
|
||||||
|
* default "C" locale remains active, since fgfs isn't locale aware - especially
|
||||||
|
* requires "." as decimal point in strings containing floating point varibales. */
|
||||||
|
setenv("LC_ALL", "C", 1);
|
||||||
|
|
||||||
if (svn_cmdline_init("terrasync", stderr) != EXIT_SUCCESS)
|
if (svn_cmdline_init("terrasync", stderr) != EXIT_SUCCESS)
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
#endif
|
#endif
|
||||||
/* Oh no! svn_cmdline_init configures the locale - affecting numeric output
|
|
||||||
* formats (i.e. sprintf("%f", ...)). fgfs relies on "C" locale in many places
|
|
||||||
* (including assumptions on required sprintf buffer sizes). Things go horribly
|
|
||||||
* wrong when the locale is changed to anything else but "C". Might be enough to
|
|
||||||
* revert LC_NUMERIC locale - but we'll do a complete revert for now...*/
|
|
||||||
setlocale(LC_ALL,"C");
|
|
||||||
|
|
||||||
apr_pool_t *pool;
|
apr_pool_t *pool;
|
||||||
apr_pool_create(&pool, NULL);
|
apr_pool_create(&pool, NULL);
|
||||||
|
|||||||
@@ -104,3 +104,10 @@ void SGTimeStamp::stamp() {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int SGTimeStamp::elapsedMSec() const
|
||||||
|
{
|
||||||
|
SGTimeStamp now;
|
||||||
|
now.stamp();
|
||||||
|
|
||||||
|
return static_cast<int>((now - *this).toMSecs());
|
||||||
|
}
|
||||||
|
|||||||
@@ -195,6 +195,10 @@ public:
|
|||||||
static SGTimeStamp now()
|
static SGTimeStamp now()
|
||||||
{ SGTimeStamp ts; ts.stamp(); return ts; }
|
{ SGTimeStamp ts; ts.stamp(); return ts; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* elapsed time since the stamp was taken, in msec
|
||||||
|
*/
|
||||||
|
int elapsedMSec() const;
|
||||||
private:
|
private:
|
||||||
SGTimeStamp(sec_type sec, nsec_type nsec)
|
SGTimeStamp(sec_type sec, nsec_type nsec)
|
||||||
{ setTime(sec, nsec); }
|
{ setTime(sec, nsec); }
|
||||||
|
|||||||
Reference in New Issue
Block a user