Files
simgear/simgear/io/test_HTTP.hxx
T

256 lines
6.1 KiB
C++

#ifndef SIMGEAR_IO_TEST_HTTP_HXX
#define SIMGEAR_IO_TEST_HTTP_HXX
#include <algorithm>
#include <sstream>
#include <vector>
#include <simgear/io/sg_netChat.hxx>
#include <simgear/misc/strutils.hxx>
namespace simgear
{
class TestServerChannel : public NetChat
{
public:
enum State
{
STATE_IDLE = 0,
STATE_HEADERS,
STATE_CLOSING,
STATE_REQUEST_BODY
};
TestServerChannel()
{
state = STATE_IDLE;
setTerminator("\r\n");
}
virtual ~TestServerChannel()
{
}
virtual void collectIncomingData(const char* s, int n)
{
buffer += std::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() < 3) {
std::cerr << "malformed request:" << buffer << std::endl;
exit(-1);
}
method = line[0];
path = line[1];
std::string::size_type queryPos = path.find('?');
if (queryPos != std::string::npos) {
parseArgs(path.substr(queryPos + 1));
path = path.substr(0, queryPos);
}
httpVersion = line[2];
requestHeaders.clear();
buffer.clear();
} else if (state == STATE_HEADERS) {
std::string s = strutils::simplify(buffer);
if (s.empty()) {
buffer.clear();
receivedRequestHeaders();
return;
}
std::string::size_type colonPos = buffer.find(':');
if (colonPos == std::string::npos) {
std::cerr << "test malformed HTTP response header:" << buffer << std::endl;
buffer.clear();
return;
}
std::string key = strutils::simplify(buffer.substr(0, colonPos));
std::string value = strutils::strip(buffer.substr(colonPos + 1));
requestHeaders[key] = value;
buffer.clear();
} else if (state == STATE_REQUEST_BODY) {
receivedBody();
setTerminator("\r\n");
} else if (state == STATE_CLOSING) {
// ignore!
}
}
void parseArgs(const std::string& argData)
{
string_list argv = strutils::split(argData, "&");
for (unsigned int a=0; a<argv.size(); ++a) {
std::string::size_type eqPos = argv[a].find('=');
if (eqPos == std::string::npos) {
std::cerr << "malformed HTTP argument:" << argv[a] << std::endl;
continue;
}
std::string key = argv[a].substr(0, eqPos);
std::string value = argv[a].substr(eqPos + 1);
args[key] = value;
}
}
void receivedRequestHeaders()
{
state = STATE_IDLE;
processRequestHeaders();
}
virtual void processRequestHeaders()
{
sendErrorResponse(404, false, "");
}
void closeAfterSending()
{
state = STATE_CLOSING;
closeWhenDone();
}
void receivedBody()
{
state = STATE_IDLE;
if (method == "POST") {
parseArgs(buffer);
}
processRequestBody();
buffer.clear();
}
virtual void processRequestBody()
{
sendErrorResponse(404, false, "");
}
void sendErrorResponse(int code, bool close, std::string content)
{
// std::cerr << "sending error " << code << " for " << path << std::endl;
// std::cerr << "\tcontent:" << content << std::endl;
std::stringstream headerData;
headerData << "HTTP/1.1 " << code << " " << reasonForCode(code) << "\r\n";
headerData << "Content-Length:" << content.size() << "\r\n";
headerData << "\r\n"; // final CRLF to terminate the headers
push(headerData.str().c_str());
push(content.c_str());
if (close) {
closeWhenDone();
}
}
std::string reasonForCode(int code)
{
switch (code) {
case 200: return "OK";
case 201: return "Created";
case 204: return "no content";
case 404: return "not found";
case 407: return "proxy authentication required";
default: return "unknown code";
}
}
virtual void handleClose (void)
{
NetBufferChannel::handleClose();
}
State state;
std::string buffer;
std::string method;
std::string path;
std::string httpVersion;
std::map<std::string, std::string> requestHeaders;
std::map<std::string, std::string> args;
int requestContentLength;
};
template <class T>
class TestServer : public NetChannel
{
simgear::NetChannelPoller _poller;
std::vector<T*> _channels;
public:
TestServer()
{
Socket::initSockets();
open();
bind(NULL, 2000); // localhost, any port
listen(16);
_poller.addChannel(this);
}
virtual ~TestServer()
{
_poller.removeChannel(this);
}
virtual bool writable (void) { return false ; }
virtual void handleAccept (void)
{
simgear::IPAddress addr ;
int handle = accept ( &addr ) ;
T* chan = new T();
chan->setHandle(handle);
_channels.push_back(chan);
_poller.addChannel(chan);
}
void poll()
{
_poller.poll();
auto it = std::remove_if(_channels.begin(), _channels.end(), [&](T* channel) {
if (channel->isClosed()) {
_poller.removeChannel(channel);
delete channel;
return true;
}
return false;
});
_channels.erase(it, _channels.end());
}
int connectCount()
{
return _channels.size();
}
void disconnectAll()
{
typename std::vector<T*>::iterator it;
for (it = _channels.begin(); it != _channels.end(); ++it) {
_poller.removeChannel(*it);
delete *it;
}
_channels.clear();
}
};
} // of namespace simgear
#endif // of SIMGEAR_IO_TEST_HTTP_HXX