Further HTTP improvements, wget-alike test program to check it all behaves!
This commit is contained in:
parent
b53c53d9d8
commit
a2249becba
@ -44,4 +44,10 @@ target_link_libraries(test_http
|
||||
sgio sgstructure sgtiming sgmisc sgdebug
|
||||
${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})
|
||||
|
@ -7,12 +7,6 @@
|
||||
using std::string;
|
||||
using std::map;
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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];
|
||||
}
|
||||
|
@ -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);
|
||||
|
153
simgear/io/httpget.cxx
Normal file
153
simgear/io/httpget.cxx
Normal 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;
|
||||
}
|
@ -30,15 +30,6 @@
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
// #include "protocol.hxx"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
|
||||
#define SG_IO_MAX_MSG_SIZE 16384
|
||||
|
||||
/**
|
||||
|
@ -31,6 +31,14 @@
|
||||
|
||||
#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/debug/logstream.hxx>
|
||||
|
||||
@ -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() {
|
||||
}
|
||||
|
@ -26,33 +26,19 @@
|
||||
#ifndef _SG_FILE_HXX
|
||||
#define _SG_FILE_HXX
|
||||
|
||||
|
||||
#ifndef __cplusplus
|
||||
# error This library requires C++
|
||||
#endif
|
||||
|
||||
#include <simgear/compiler.h>
|
||||
|
||||
#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"
|
||||
|
||||
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; };
|
||||
|
@ -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 ) :
|
||||
|
@ -33,6 +33,8 @@
|
||||
#include <cstring>
|
||||
#include <cstdlib> // for atoi
|
||||
|
||||
using std::string;
|
||||
|
||||
SGSocketUDP::SGSocketUDP( const string& host, const string& port ) :
|
||||
hostname(host),
|
||||
port_str(port),
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user