Further HTTP improvements, wget-alike test program to check it all behaves!

This commit is contained in:
James Turner 2011-08-01 09:03:12 +01:00
parent b53c53d9d8
commit a2249becba
11 changed files with 200 additions and 38 deletions

View File

@ -45,3 +45,9 @@ target_link_libraries(test_http
${RT_LIBRARY}) ${RT_LIBRARY})
add_test(http ${EXECUTABLE_OUTPUT_PATH}/test_http) add_test(http ${EXECUTABLE_OUTPUT_PATH}/test_http)
add_executable(httpget httpget.cxx)
target_link_libraries(httpget
sgio sgstructure sgtiming sgmisc sgdebug
${RT_LIBRARY})

View File

@ -7,12 +7,6 @@
using std::string; using std::string;
using std::map; using std::map;
#include <iostream>
using std::cout;
using std::cerr;
using std::endl;
namespace simgear namespace simgear
{ {
@ -51,8 +45,15 @@ string Request::header(const std::string& name) const
void Request::responseStart(const string& r) 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); 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]); _responseStatus = strutils::to_int(parts[1]);
_responseReason = parts[2]; _responseReason = parts[2];
} }

View File

@ -37,14 +37,14 @@ public:
virtual int responseCode() const virtual int responseCode() const
{ return _responseStatus; } { return _responseStatus; }
virtual std::string resposeReason() const virtual std::string responseReason() const
{ return _responseReason; } { return _responseReason; }
virtual unsigned int contentLength() const; virtual unsigned int contentLength() const;
protected: protected:
friend class Connection; 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 responseStart(const std::string& r);
virtual void responseHeader(const std::string& key, const std::string& value); virtual void responseHeader(const std::string& key, const std::string& value);

153
simgear/io/httpget.cxx Normal file
View File

@ -0,0 +1,153 @@
#include <cstdio>
#include <cstring>
#include <unistd.h> // for STDOUT_FILENO
#include <iostream>
#include <boost/foreach.hpp>
#include <simgear/io/sg_file.hxx>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/io/HTTPRequest.hxx>
#include <simgear/io/sg_netChannel.hxx>
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<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()
{
_complete = true;
}
virtual void gotBodyData(const char* s, int n)
{
_file->write(s, n);
}
private:
bool _complete;
SGFile* _file;
std::map<string, string> _headers;
};
int main(int argc, char* argv[])
{
HTTP::Client cl;
SGFile* outFile;
string proxy, proxyAuth;
string_list headers;
string url;
for (int a=0; a<argc;++a) {
if (argv[a][0] == '-') {
if (!strcmp(argv[a], "--user-agent")) {
cl.setUserAgent(argv[++a]);
} else if (!strcmp(argv[a], "--proxy")) {
proxy = argv[++a];
} else if (!strcmp(argv[a], "--auth")) {
proxyAuth = argv[++a];
} else if (!strcmp(argv[a], "-f") || !strcmp(argv[a], "--file")) {
outFile = new SGFile(argv[++a]);
if (!outFile->open(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;
}

View File

@ -30,15 +30,6 @@
#include <simgear/compiler.h> #include <simgear/compiler.h>
// #include "protocol.hxx"
#include <string>
#include <vector>
using std::vector;
using std::string;
#define SG_IO_MAX_MSG_SIZE 16384 #define SG_IO_MAX_MSG_SIZE 16384
/** /**

View File

@ -31,6 +31,14 @@
#include <cstring> #include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#if !defined(_MSC_VER)
# include <unistd.h>
#endif
#include <simgear/misc/stdint.hxx> #include <simgear/misc/stdint.hxx>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
@ -38,13 +46,20 @@
using std::string; using std::string;
SGFile::SGFile(const string &file, int repeat_) SGFile::SGFile(const string &file, int repeat_)
: file_name(file), fp(-1), eof_flag(true), repeat(repeat_), iteration(0) : file_name(file), fp(-1), eof_flag(true), repeat(repeat_), iteration(0)
{ {
set_type( sgFileType ); set_type( sgFileType );
} }
SGFile::SGFile( int existingFd ) :
fp(existingFd),
eof_flag(false),
repeat(1),
iteration(0)
{
set_type( sgFileType );
}
SGFile::~SGFile() { SGFile::~SGFile() {
} }

View File

@ -26,33 +26,19 @@
#ifndef _SG_FILE_HXX #ifndef _SG_FILE_HXX
#define _SG_FILE_HXX #define _SG_FILE_HXX
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/compiler.h> #include <simgear/compiler.h>
#include <string> #include <string>
#include <sys/types.h> // for open(), read(), write(), close()
#include <sys/stat.h> // for open(), read(), write(), close()
#include <fcntl.h> // for open(), read(), write(), close()
#if !defined( _MSC_VER )
# include <unistd.h> // for open(), read(), write(), close()
#endif
#include "iochannel.hxx" #include "iochannel.hxx"
using std::string;
/** /**
* A file I/O class based on SGIOChannel. * A file I/O class based on SGIOChannel.
*/ */
class SGFile : public SGIOChannel { class SGFile : public SGIOChannel {
string file_name; std::string file_name;
int fp; int fp;
bool eof_flag; bool eof_flag;
// Number of repetitions to play. -1 means loop infinitely. // Number of repetitions to play. -1 means loop infinitely.
@ -70,7 +56,12 @@ public:
* @param file name of file to open * @param file name of file to open
* @param repeat On eof restart at the beginning of the file * @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 */ /** Destructor */
~SGFile(); ~SGFile();
@ -94,7 +85,7 @@ public:
bool close(); bool close();
/** @return the name of the file being manipulated. */ /** @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 */ /** @return true of eof conditions exists */
inline bool eof() const { return eof_flag; }; inline bool eof() const { return eof_flag; };

View File

@ -36,6 +36,7 @@
#include "sg_socket.hxx" #include "sg_socket.hxx"
bool SGSocket::init = false; bool SGSocket::init = false;
using std::string;
SGSocket::SGSocket( const string& host, const string& port_, SGSocket::SGSocket( const string& host, const string& port_,
const string& style ) : const string& style ) :

View File

@ -33,6 +33,8 @@
#include <cstring> #include <cstring>
#include <cstdlib> // for atoi #include <cstdlib> // for atoi
using std::string;
SGSocketUDP::SGSocketUDP( const string& host, const string& port ) : SGSocketUDP::SGSocketUDP( const string& host, const string& port ) :
hostname(host), hostname(host),
port_str(port), port_str(port),

View File

@ -89,7 +89,7 @@ public:
* @param host name of host if direction is SG_IO_OUT or SG_IO_BI * @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 port port number if we care to choose one.
* @param style specify "udp" or "tcp" */ * @param style specify "udp" or "tcp" */
SGSocketUDP( const string& host, const string& port ); SGSocketUDP( const std::string& host, const std::string& port );
/** Destructor */ /** Destructor */
~SGSocketUDP(); ~SGSocketUDP();

View File

@ -275,6 +275,7 @@ int main(int argc, char* argv[])
waitForComplete(tr); waitForComplete(tr);
COMPARE(tr->responseCode(), 200); COMPARE(tr->responseCode(), 200);
COMPARE(tr->responseReason(), string("OK"));
COMPARE(tr->contentLength(), strlen(BODY1)); COMPARE(tr->contentLength(), strlen(BODY1));
COMPARE(tr->bodyData, string(BODY1)); COMPARE(tr->bodyData, string(BODY1));
} }
@ -301,6 +302,7 @@ int main(int argc, char* argv[])
cl.makeRequest(tr); cl.makeRequest(tr);
waitForComplete(tr); waitForComplete(tr);
COMPARE(tr->responseCode(), 404); COMPARE(tr->responseCode(), 404);
COMPARE(tr->responseReason(), string("not found"));
COMPARE(tr->contentLength(), 0); COMPARE(tr->contentLength(), 0);
} }