HTTP engine tweaks for SVN support.

This commit is contained in:
James Turner
2013-06-09 19:16:46 +01:00
parent 9bfa6ac1a1
commit 323d0d5ba0
6 changed files with 174 additions and 127 deletions

View File

@@ -53,6 +53,8 @@ const int GZIP_HEADER_FNAME = 1 << 3;
const int GZIP_HEADER_COMMENT = 1 << 4;
const int GZIP_HEADER_CRC = 1 << 1;
typedef std::list<Request_ptr> RequestList;
class Connection : public NetChat
{
public:
@@ -117,13 +119,27 @@ public:
virtual void handleClose()
{
NetChat::handleClose();
if ((state == STATE_GETTING_BODY) && activeRequest) {
// closing of the connection from the server side when getting the body,
bool canCloseState = (state == STATE_GETTING_BODY);
if (canCloseState && activeRequest) {
// force state here, so responseComplete can avoid closing the
// socket again
state = STATE_CLOSED;
responseComplete();
} else {
if (activeRequest) {
activeRequest->setFailure(500, "server closed connection");
// remove the failed request from sentRequests, so it does
// not get restored
RequestList::iterator it = std::find(sentRequests.begin(),
sentRequests.end(), activeRequest);
if (it != sentRequests.end()) {
sentRequests.erase(it);
}
activeRequest = NULL;
}
state = STATE_CLOSED;
}
@@ -186,6 +202,7 @@ public:
}
Request_ptr r = queuedRequests.front();
r->requestStart();
requestBodyBytesToSend = r->requestBodyLength();
stringstream headerData;
@@ -229,6 +246,7 @@ public:
bool ok = push(headerData.str().c_str());
if (!ok) {
SG_LOG(SG_IO, SG_WARN, "HTTPClient: over-stuffed the socket");
// we've over-stuffed the socket, give up for now, let things
// drain down before trying to start any more requests.
return;
@@ -236,8 +254,7 @@ public:
while (requestBodyBytesToSend > 0) {
char buf[4096];
int len = 4096;
r->getBodyData(buf, len);
int len = r->getBodyData(buf, 4096);
if (len > 0) {
requestBodyBytesToSend -= len;
if (!bufferSend(buf, len)) {
@@ -245,13 +262,16 @@ public:
state = STATE_SOCKET_ERROR;
return;
}
// SG_LOG(SG_IO, SG_INFO, "sent body:\n" << string(buf, len) << "\n%%%%%%%%%");
} else {
SG_LOG(SG_IO, SG_WARN, "asynchronous request body generation is unsupported");
SG_LOG(SG_IO, SG_WARN, "HTTP asynchronous request body generation is unsupported");
break;
}
}
//std::cout << "did send request:" << r->url() << std::endl;
SG_LOG(SG_IO, SG_DEBUG, "did start request:" << r->url() <<
"\n\t @ " << reinterpret_cast<void*>(r.ptr()) <<
"\n\t on connection " << this);
// successfully sent, remove from queue, and maybe send the next
queuedRequests.pop_front();
sentRequests.push_back(r);
@@ -308,7 +328,7 @@ public:
if (result == Z_OK || result == Z_STREAM_END) {
// nothing to do
} else {
SG_LOG(SG_IO, SG_WARN, "got Zlib error:" << result);
SG_LOG(SG_IO, SG_WARN, "HTTP: got Zlib error:" << result);
return;
}
@@ -607,7 +627,7 @@ private:
void responseComplete()
{
//std::cout << "responseComplete:" << activeRequest->url() << std::endl;
// SG_LOG(SG_IO, SG_INFO, "*** responseComplete:" << activeRequest->url());
activeRequest->responseComplete();
client->requestFinished(this);
@@ -666,8 +686,8 @@ private:
unsigned char* zlibOutputBuffer;
bool contentGZip, contentDeflate;
std::list<Request_ptr> queuedRequests;
std::list<Request_ptr> sentRequests;
RequestList queuedRequests;
RequestList sentRequests;
};
Client::Client()
@@ -677,7 +697,7 @@ Client::Client()
void Client::update(int waitTimeout)
{
NetChannel::poll(waitTimeout);
_poller.poll(waitTimeout);
ConnectionDict::iterator it = _connections.begin();
for (; it != _connections.end(); ) {
@@ -715,6 +735,7 @@ void Client::makeRequest(const Request_ptr& r)
if (_connections.find(connectionId) == _connections.end()) {
Connection* con = new Connection(this);
con->setServer(host, port);
_poller.addChannel(con);
_connections[connectionId] = con;
}

View File

@@ -4,6 +4,7 @@
#include <map>
#include <simgear/io/HTTPRequest.hxx>
#include <simgear/io/sg_netChannel.hxx>
namespace simgear
{
@@ -49,6 +50,7 @@ private:
std::string _proxy;
int _proxyPort;
std::string _proxyAuth;
NetChannelPoller _poller;
// connections by host
typedef std::map<std::string, Connection*> ConnectionDict;

View File

@@ -48,6 +48,11 @@ string Request::header(const std::string& name) const
return string();
}
void Request::requestStart()
{
}
void Request::responseStart(const string& r)
{
const int maxSplit = 2; // HTTP/1.1 nnn reason-string
@@ -232,10 +237,9 @@ std::string Request::requestBodyType() const
return "text/plain";
}
void Request::getBodyData(char*, int& count) const
int Request::getBodyData(char*, int maxCount) const
{
count = 0;
return;
return 0;
}
} // of namespace HTTP

View File

@@ -52,10 +52,10 @@ public:
/**
* Retrieve the body data bytes. Will be passed the maximum body bytes
* to return in the buffer, and should update count with the actual number
* to return in the buffer, and must return the actual number
* of bytes written.
*/
virtual void getBodyData(char* s, int& count) const;
virtual int getBodyData(char* s, int count) const;
/**
* retrieve the request body content type. Default is text/plain
@@ -86,6 +86,7 @@ public:
protected:
Request(const std::string& url, const std::string method = "GET");
virtual void requestStart();
virtual void responseStart(const std::string& r);
virtual void responseHeader(const std::string& key, const std::string& value);
virtual void responseHeadersComplete();

View File

@@ -41,8 +41,6 @@
namespace simgear {
static NetChannel* channels = 0 ;
NetChannel::NetChannel ()
{
closed = true ;
@@ -51,31 +49,14 @@ NetChannel::NetChannel ()
accepting = false ;
write_blocked = false ;
should_delete = false ;
next_channel = channels ;
channels = this ;
poller = NULL;
}
NetChannel::~NetChannel ()
{
close();
NetChannel* prev = NULL ;
for ( NetChannel* ch = channels; ch != NULL;
ch = ch -> next_channel )
{
if (ch == this)
{
ch = ch -> next_channel ;
if ( prev != NULL )
prev -> next_channel = ch ;
else
channels = ch ;
next_channel = 0 ;
break;
}
prev = ch ;
if (poller) {
poller->removeChannel(this);
}
}
@@ -232,89 +213,6 @@ NetChannel::handleResolve()
}
}
bool
NetChannel::poll (unsigned int timeout)
{
if (!channels)
return false ;
enum { MAX_SOCKETS = 256 } ;
Socket* reads [ MAX_SOCKETS+1 ] ;
Socket* writes [ MAX_SOCKETS+1 ] ;
Socket* deletes [ MAX_SOCKETS+1 ] ;
int nreads = 0 ;
int nwrites = 0 ;
int ndeletes = 0 ;
int nopen = 0 ;
NetChannel* ch;
for ( ch = channels; ch != NULL; ch = ch -> next_channel )
{
if ( ch -> should_delete )
{
assert(ndeletes<MAX_SOCKETS);
deletes[ndeletes++] = ch ;
}
else if ( ! ch -> closed )
{
if (ch -> resolving_host )
{
ch -> handleResolve();
continue;
}
nopen++ ;
if (ch -> readable()) {
assert(nreads<MAX_SOCKETS);
reads[nreads++] = ch ;
}
if (ch -> writable()) {
assert(nwrites<MAX_SOCKETS);
writes[nwrites++] = ch ;
}
}
}
reads[nreads] = NULL ;
writes[nwrites] = NULL ;
deletes[ndeletes] = NULL ;
int i ;
for ( i=0; deletes[i]; i++ )
{
ch = (NetChannel*)deletes[i];
delete ch ;
}
if (!nopen)
return false ;
if (!nreads && !nwrites)
return true ; //hmmm- should we shutdown?
Socket::select (reads, writes, timeout) ;
for ( i=0; reads[i]; i++ )
{
ch = (NetChannel*)reads[i];
if ( ! ch -> closed )
ch -> handleReadEvent();
}
for ( i=0; writes[i]; i++ )
{
ch = (NetChannel*)writes[i];
if ( ! ch -> closed )
ch -> handleWriteEvent();
}
return true ;
}
void
NetChannel::loop (unsigned int timeout)
{
while ( poll (timeout) ) ;
}
void NetChannel::handleRead (void) {
SG_LOG(SG_IO, SG_WARN, "Network:" << getHandle() << ": unhandled read");
}
@@ -336,4 +234,112 @@ void NetChannel::handleError (int error)
}
}
void
NetChannelPoller::addChannel(NetChannel* channel)
{
assert(channel);
assert(channel->poller == NULL);
channel->poller = this;
channels.push_back(channel);
}
void
NetChannelPoller::removeChannel(NetChannel* channel)
{
assert(channel);
assert(channel->poller == this);
channel->poller = NULL;
ChannelList::iterator it = channels.begin();
for (; it != channels.end(); ++it) {
if (*it == channel) {
channels.erase(it);
return;
}
}
}
bool
NetChannelPoller::poll(unsigned int timeout)
{
if (channels.empty()) {
return false;
}
enum { MAX_SOCKETS = 256 } ;
Socket* reads [ MAX_SOCKETS+1 ] ;
Socket* writes [ MAX_SOCKETS+1 ] ;
int nreads = 0 ;
int nwrites = 0 ;
int nopen = 0 ;
NetChannel* ch;
ChannelList::iterator it = channels.begin();
while( it != channels.end() )
{
NetChannel* ch = *it;
if ( ch -> should_delete )
{
delete ch;
it = channels.erase(it);
continue;
}
++it; // we've copied the pointer into ch
if ( ch->closed ) {
continue;
}
if (ch -> resolving_host )
{
ch -> handleResolve();
continue;
}
nopen++ ;
if (ch -> readable()) {
assert(nreads<MAX_SOCKETS);
reads[nreads++] = ch ;
}
if (ch -> writable()) {
assert(nwrites<MAX_SOCKETS);
writes[nwrites++] = ch ;
}
} // of array-filling pass
reads[nreads] = NULL ;
writes[nwrites] = NULL ;
if (!nopen)
return false ;
if (!nreads && !nwrites)
return true ; //hmmm- should we shutdown?
Socket::select (reads, writes, timeout) ;
for ( int i=0; reads[i]; i++ )
{
ch = (NetChannel*)reads[i];
if ( ! ch -> closed )
ch -> handleReadEvent();
}
for ( int i=0; writes[i]; i++ )
{
ch = (NetChannel*)writes[i];
if ( ! ch -> closed )
ch -> handleWriteEvent();
}
return true ;
}
void
NetChannelPoller::loop (unsigned int timeout)
{
while ( poll (timeout) ) ;
}
} // of namespace simgear

View File

@@ -54,20 +54,23 @@
#define SG_NET_CHANNEL_H
#include <simgear/io/raw_socket.hxx>
#include <string>
#include <vector>
namespace simgear
{
class NetChannelPoller;
class NetChannel : public Socket
{
bool closed, connected, accepting, write_blocked, should_delete, resolving_host ;
NetChannel* next_channel ;
std::string host;
int port;
friend bool netPoll (unsigned int timeout);
friend class NetChannelPoller;
NetChannelPoller* poller;
public:
NetChannel () ;
@@ -109,9 +112,19 @@ public:
virtual void handleWrite (void);
virtual void handleAccept (void);
virtual void handleError (int error);
static bool poll (unsigned int timeout = 0 ) ;
static void loop (unsigned int timeout = 0 ) ;
};
class NetChannelPoller
{
typedef std::vector<NetChannel*> ChannelList;
ChannelList channels;
public:
void addChannel(NetChannel* channel);
void removeChannel(NetChannel* channel);
bool poll(unsigned int timeout = 0);
void loop(unsigned int timeout = 0);
};
} // of namespace simgear