Further HTTP improvements, wget-alike test program to check it all behaves!
This commit is contained in:
parent
b53c53d9d8
commit
a2249becba
@ -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})
|
||||||
|
|
@ -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];
|
||||||
}
|
}
|
||||||
|
@ -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
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 <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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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() {
|
||||||
}
|
}
|
||||||
|
@ -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; };
|
||||||
|
@ -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 ) :
|
||||||
|
@ -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),
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user