From a2249becba1d574d567791ef8b03ef50b129eb90 Mon Sep 17 00:00:00 2001 From: James Turner Date: Mon, 1 Aug 2011 09:03:12 +0100 Subject: [PATCH] Further HTTP improvements, wget-alike test program to check it all behaves! --- simgear/io/CMakeLists.txt | 8 +- simgear/io/HTTPRequest.cxx | 15 ++-- simgear/io/HTTPRequest.hxx | 4 +- simgear/io/httpget.cxx | 153 +++++++++++++++++++++++++++++++++++ simgear/io/iochannel.hxx | 9 --- simgear/io/sg_file.cxx | 17 +++- simgear/io/sg_file.hxx | 25 ++---- simgear/io/sg_socket.cxx | 1 + simgear/io/sg_socket_udp.cxx | 2 + simgear/io/sg_socket_udp.hxx | 2 +- simgear/io/test_HTTP.cxx | 2 + 11 files changed, 200 insertions(+), 38 deletions(-) create mode 100644 simgear/io/httpget.cxx diff --git a/simgear/io/CMakeLists.txt b/simgear/io/CMakeLists.txt index 136b6e22..a2638e1b 100644 --- a/simgear/io/CMakeLists.txt +++ b/simgear/io/CMakeLists.txt @@ -44,4 +44,10 @@ target_link_libraries(test_http sgio sgstructure sgtiming sgmisc sgdebug ${RT_LIBRARY}) -add_test(http ${EXECUTABLE_OUTPUT_PATH}/test_http) \ No newline at end of file +add_test(http ${EXECUTABLE_OUTPUT_PATH}/test_http) + +add_executable(httpget httpget.cxx) +target_link_libraries(httpget + sgio sgstructure sgtiming sgmisc sgdebug + ${RT_LIBRARY}) + \ No newline at end of file diff --git a/simgear/io/HTTPRequest.cxx b/simgear/io/HTTPRequest.cxx index 50d059e6..68d69d3c 100644 --- a/simgear/io/HTTPRequest.cxx +++ b/simgear/io/HTTPRequest.cxx @@ -7,12 +7,6 @@ using std::string; using std::map; -#include - -using std::cout; -using std::cerr; -using std::endl; - namespace simgear { @@ -51,8 +45,15 @@ string Request::header(const std::string& name) const void Request::responseStart(const string& r) { - const int maxSplit = 2; // HTTP/1.1 nnn status code + 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); + _responseStatus = 400; + _responseReason = "bad HTTP response header"; + return; + } + _responseStatus = strutils::to_int(parts[1]); _responseReason = parts[2]; } diff --git a/simgear/io/HTTPRequest.hxx b/simgear/io/HTTPRequest.hxx index 7cf498cd..8c0fcbac 100644 --- a/simgear/io/HTTPRequest.hxx +++ b/simgear/io/HTTPRequest.hxx @@ -37,14 +37,14 @@ public: virtual int responseCode() const { return _responseStatus; } - virtual std::string resposeReason() const + virtual std::string responseReason() const { return _responseReason; } virtual unsigned int contentLength() const; protected: friend class Connection; - Request(const std::string& url, const std::string method = "get"); + 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); diff --git a/simgear/io/httpget.cxx b/simgear/io/httpget.cxx new file mode 100644 index 00000000..513bb4b2 --- /dev/null +++ b/simgear/io/httpget.cxx @@ -0,0 +1,153 @@ + +#include +#include + +#include // for STDOUT_FILENO +#include +#include + +#include +#include +#include +#include + +using namespace simgear; +using std::cout; +using std::endl; +using std::cerr; +using std::string; + +class ARequest : public HTTP::Request +{ +public: + ARequest(string& url) : + Request(url), + _complete(false), + _file(NULL) + { + + } + + void setFile(SGFile* f) + { + _file = f; + } + + bool complete() const + { return _complete; } + + void addHeader(const string& h) + { + int colonPos = h.find(':'); + if (colonPos < 0) { + cerr << "malformed header: " << h << endl; + return; + } + + string key = h.substr(0, colonPos); + _headers[key] = h.substr(colonPos + 1); + } + + virtual string_list requestHeaders() const + { + string_list r; + std::map::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::const_iterator it = _headers.find(name); + if (it == _headers.end()) { + return string(); + } + + return it->second; + } +protected: + virtual void responseHeadersComplete() + { + } + + virtual void responseComplete() + { + _complete = true; + } + + virtual void gotBodyData(const char* s, int n) + { + _file->write(s, n); + } +private: + bool _complete; + SGFile* _file; + std::map _headers; +}; + +int main(int argc, char* argv[]) +{ + HTTP::Client cl; + SGFile* outFile; + string proxy, proxyAuth; + string_list headers; + string url; + + for (int a=0; aopen(SG_IO_OUT)) { + cerr << "failed to open output for writing:" << outFile->get_file_name() << endl; + return EXIT_FAILURE; + } + } else if (!strcmp(argv[a], "--header")) { + headers.push_back(argv[++a]); + } + } else { // of argument starts with a hyphen + url = argv[a]; + } + } // of arguments iteration + + if (!proxy.empty()) { + cl.setProxy(proxy, proxyAuth); + } + + if (!outFile) { + outFile = new SGFile(STDOUT_FILENO); + } + + if (url.empty()) { + cerr << "no URL argument specificed" << endl; + return EXIT_FAILURE; + } + + ARequest* req = new ARequest(url); + BOOST_FOREACH(string h, headers) { + req->addHeader(h); + } + + req->setFile(outFile); + cl.makeRequest(req); + + while (!req->complete()) { + NetChannel::poll(100); + } + + if (req->responseCode() != 200) { + cerr << "got response:" << req->responseCode() << endl; + cerr << "\treason:" << req->responseReason() << endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/simgear/io/iochannel.hxx b/simgear/io/iochannel.hxx index d8a29608..ea1b0077 100644 --- a/simgear/io/iochannel.hxx +++ b/simgear/io/iochannel.hxx @@ -30,15 +30,6 @@ #include -// #include "protocol.hxx" - -#include -#include - -using std::vector; -using std::string; - - #define SG_IO_MAX_MSG_SIZE 16384 /** diff --git a/simgear/io/sg_file.cxx b/simgear/io/sg_file.cxx index 67fbda3e..250518d8 100644 --- a/simgear/io/sg_file.cxx +++ b/simgear/io/sg_file.cxx @@ -31,6 +31,14 @@ #include +#include +#include +#include + +#if !defined(_MSC_VER) +# include +#endif + #include #include @@ -38,13 +46,20 @@ using std::string; - SGFile::SGFile(const string &file, int repeat_) : file_name(file), fp(-1), eof_flag(true), repeat(repeat_), iteration(0) { set_type( sgFileType ); } +SGFile::SGFile( int existingFd ) : + fp(existingFd), + eof_flag(false), + repeat(1), + iteration(0) +{ + set_type( sgFileType ); +} SGFile::~SGFile() { } diff --git a/simgear/io/sg_file.hxx b/simgear/io/sg_file.hxx index 1bf3800d..7e5854c1 100644 --- a/simgear/io/sg_file.hxx +++ b/simgear/io/sg_file.hxx @@ -26,33 +26,19 @@ #ifndef _SG_FILE_HXX #define _SG_FILE_HXX - -#ifndef __cplusplus -# error This library requires C++ -#endif - #include #include -#include // for open(), read(), write(), close() -#include // for open(), read(), write(), close() -#include // for open(), read(), write(), close() -#if !defined( _MSC_VER ) -# include // for open(), read(), write(), close() -#endif - #include "iochannel.hxx" -using std::string; - /** * A file I/O class based on SGIOChannel. */ class SGFile : public SGIOChannel { - string file_name; + std::string file_name; int fp; bool eof_flag; // Number of repetitions to play. -1 means loop infinitely. @@ -70,7 +56,12 @@ public: * @param file name of file to open * @param repeat On eof restart at the beginning of the file */ - SGFile( const string& file, int repeat_ = 1 ); + SGFile( const std::string& file, int repeat_ = 1 ); + + /** + * Create an SGFile from an existing, open file-descriptor + */ + SGFile( int existingFd ); /** Destructor */ ~SGFile(); @@ -94,7 +85,7 @@ public: bool close(); /** @return the name of the file being manipulated. */ - inline string get_file_name() const { return file_name; } + inline std::string get_file_name() const { return file_name; } /** @return true of eof conditions exists */ inline bool eof() const { return eof_flag; }; diff --git a/simgear/io/sg_socket.cxx b/simgear/io/sg_socket.cxx index 4af401de..4ca13150 100644 --- a/simgear/io/sg_socket.cxx +++ b/simgear/io/sg_socket.cxx @@ -36,6 +36,7 @@ #include "sg_socket.hxx" bool SGSocket::init = false; +using std::string; SGSocket::SGSocket( const string& host, const string& port_, const string& style ) : diff --git a/simgear/io/sg_socket_udp.cxx b/simgear/io/sg_socket_udp.cxx index 2d297154..82b23757 100644 --- a/simgear/io/sg_socket_udp.cxx +++ b/simgear/io/sg_socket_udp.cxx @@ -33,6 +33,8 @@ #include #include // for atoi +using std::string; + SGSocketUDP::SGSocketUDP( const string& host, const string& port ) : hostname(host), port_str(port), diff --git a/simgear/io/sg_socket_udp.hxx b/simgear/io/sg_socket_udp.hxx index 69a5b027..9234c413 100644 --- a/simgear/io/sg_socket_udp.hxx +++ b/simgear/io/sg_socket_udp.hxx @@ -89,7 +89,7 @@ public: * @param host name of host if direction is SG_IO_OUT or SG_IO_BI * @param port port number if we care to choose one. * @param style specify "udp" or "tcp" */ - SGSocketUDP( const string& host, const string& port ); + SGSocketUDP( const std::string& host, const std::string& port ); /** Destructor */ ~SGSocketUDP(); diff --git a/simgear/io/test_HTTP.cxx b/simgear/io/test_HTTP.cxx index 1dce8d29..8aafef2d 100644 --- a/simgear/io/test_HTTP.cxx +++ b/simgear/io/test_HTTP.cxx @@ -275,6 +275,7 @@ int main(int argc, char* argv[]) waitForComplete(tr); COMPARE(tr->responseCode(), 200); + COMPARE(tr->responseReason(), string("OK")); COMPARE(tr->contentLength(), strlen(BODY1)); COMPARE(tr->bodyData, string(BODY1)); } @@ -301,6 +302,7 @@ int main(int argc, char* argv[]) cl.makeRequest(tr); waitForComplete(tr); COMPARE(tr->responseCode(), 404); + COMPARE(tr->responseReason(), string("not found")); COMPARE(tr->contentLength(), 0); } -- 2.39.5