CLOUD: Add GetClientHandler
That ClientHandler is made for responding GET requests. It calculates stream's length, it allows to specify response code and headers, it can be used to transfer any ReadStream.
This commit is contained in:
parent
99c51380fd
commit
13c54f6685
7 changed files with 212 additions and 9 deletions
|
@ -61,6 +61,7 @@ endif
|
|||
ifdef USE_SDL_NET
|
||||
MODULE_OBJS += \
|
||||
networking/sdl_net/client.o \
|
||||
networking/sdl_net/getclienthandler.o \
|
||||
networking/sdl_net/localwebserver.o
|
||||
endif
|
||||
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
|
||||
namespace Networking {
|
||||
|
||||
Client::Client(): _state(INVALID), _set(nullptr), _socket(nullptr) {}
|
||||
Client::Client(): _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {}
|
||||
|
||||
Client::Client(SDLNet_SocketSet set, TCPsocket socket): _state(INVALID), _set(nullptr), _socket(nullptr) {
|
||||
Client::Client(SDLNet_SocketSet set, TCPsocket socket): _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {
|
||||
open(set, socket);
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,6 @@ void Client::checkIfHeadersEnded() {
|
|||
}
|
||||
|
||||
void Client::checkIfBadRequest() {
|
||||
if (_state != READING_HEADERS) return;
|
||||
uint32 headersSize = _headers.size();
|
||||
bool bad = false;
|
||||
|
||||
|
@ -114,6 +113,18 @@ void Client::checkIfBadRequest() {
|
|||
if (bad) _state = BAD_REQUEST;
|
||||
}
|
||||
|
||||
void Client::setHandler(ClientHandler *handler) {
|
||||
if (_handler) delete _handler;
|
||||
_state = BEING_HANDLED;
|
||||
_handler = handler;
|
||||
}
|
||||
|
||||
void Client::handle() {
|
||||
if (_state != BEING_HANDLED) warning("handle() called in a wrong Client's state");
|
||||
if (!_handler) warning("Client doesn't have handler to be handled by");
|
||||
if (_handler) _handler->handle(this);
|
||||
}
|
||||
|
||||
void Client::close() {
|
||||
if (_set) {
|
||||
if (_socket) {
|
||||
|
@ -137,4 +148,10 @@ ClientState Client::state() { return _state; }
|
|||
|
||||
Common::String Client::headers() { return _headers; }
|
||||
|
||||
bool Client::socketIsReady() { return SDLNet_SocketReady(_socket); }
|
||||
|
||||
int Client::recv(void *data, int maxlen) { return SDLNet_TCP_Recv(_socket, data, maxlen); }
|
||||
|
||||
int Client::send(void *data, int len) { return SDLNet_TCP_Send(_socket, data, len); }
|
||||
|
||||
} // End of namespace Networking
|
||||
|
|
|
@ -35,7 +35,16 @@ enum ClientState {
|
|||
INVALID,
|
||||
READING_HEADERS,
|
||||
READ_HEADERS,
|
||||
BAD_REQUEST
|
||||
BAD_REQUEST,
|
||||
BEING_HANDLED
|
||||
};
|
||||
|
||||
class Client;
|
||||
|
||||
class ClientHandler {
|
||||
public:
|
||||
virtual ~ClientHandler() {};
|
||||
virtual void handle(Client *client) = 0;
|
||||
};
|
||||
|
||||
class Client {
|
||||
|
@ -43,6 +52,7 @@ class Client {
|
|||
SDLNet_SocketSet _set;
|
||||
TCPsocket _socket;
|
||||
Common::String _headers;
|
||||
ClientHandler *_handler;
|
||||
|
||||
void checkIfHeadersEnded();
|
||||
void checkIfBadRequest();
|
||||
|
@ -54,10 +64,23 @@ public:
|
|||
|
||||
void open(SDLNet_SocketSet set, TCPsocket socket);
|
||||
void readHeaders();
|
||||
void setHandler(ClientHandler *handler);
|
||||
void handle();
|
||||
void close();
|
||||
|
||||
ClientState state();
|
||||
Common::String headers();
|
||||
|
||||
/**
|
||||
* Return SDLNet_SocketReady(_socket).
|
||||
*
|
||||
* It's "ready" when it has something
|
||||
* to read (recv()). You can send()
|
||||
* when this is false.
|
||||
*/
|
||||
bool socketIsReady();
|
||||
int recv(void *data, int maxlen);
|
||||
int send(void *data, int len);
|
||||
};
|
||||
|
||||
} // End of namespace Networking
|
||||
|
|
94
backends/networking/sdl_net/getclienthandler.cpp
Normal file
94
backends/networking/sdl_net/getclienthandler.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "backends/networking/sdl_net/getclienthandler.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Networking {
|
||||
|
||||
GetClientHandler::GetClientHandler(Common::SeekableReadStream *stream): _responseCode(200), _headersPrepared(false), _stream(stream) {}
|
||||
|
||||
GetClientHandler::~GetClientHandler() { delete _stream; }
|
||||
|
||||
const char *GetClientHandler::responseMessage(long responseCode) {
|
||||
switch (responseCode) {
|
||||
case 200: return "OK";
|
||||
case 400: return "Bad Request";
|
||||
case 404: return "Not Found";
|
||||
case 500: return "Internal Server Error";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
void GetClientHandler::prepareHeaders() {
|
||||
if (!_specialHeaders.contains("Content-Type"))
|
||||
setHeader("Content-Type", "text/html");
|
||||
|
||||
if (!_specialHeaders.contains("Content-Length") && _stream)
|
||||
setHeader("Content-Length", Common::String::format("%u", _stream->size()));
|
||||
|
||||
_headers = Common::String::format("HTTP/1.1 %d %s\r\n", _responseCode, responseMessage(_responseCode));
|
||||
for (Common::HashMap<Common::String, Common::String>::iterator i = _specialHeaders.begin(); i != _specialHeaders.end(); ++i)
|
||||
_headers += i->_key + ": " + i->_value + "\r\n";
|
||||
_headers += "\r\n";
|
||||
|
||||
_headersPrepared = true;
|
||||
}
|
||||
|
||||
void GetClientHandler::handle(Client *client) {
|
||||
if (!client) return;
|
||||
if (!_headersPrepared) prepareHeaders();
|
||||
|
||||
const int kBufSize = 16 * 1024;
|
||||
char buf[kBufSize];
|
||||
uint32 readBytes;
|
||||
|
||||
// send headers first
|
||||
if (_headers.size() > 0) {
|
||||
readBytes = _headers.size();
|
||||
if (readBytes > kBufSize) readBytes = kBufSize;
|
||||
memcpy(buf, _headers.c_str(), readBytes);
|
||||
_headers.erase(0, readBytes);
|
||||
} else {
|
||||
if (!_stream) {
|
||||
client->close();
|
||||
return;
|
||||
}
|
||||
|
||||
readBytes = _stream->read(buf, kBufSize);
|
||||
}
|
||||
|
||||
if (readBytes != 0)
|
||||
if (client->send(buf, readBytes) != readBytes) {
|
||||
warning("GetClientHandler: unable to send all bytes to the client");
|
||||
client->close();
|
||||
return;
|
||||
}
|
||||
|
||||
// we're done here!
|
||||
if (_stream->eos()) client->close();
|
||||
}
|
||||
|
||||
void GetClientHandler::setHeader(Common::String name, Common::String value) { _specialHeaders[name] = value; }
|
||||
void GetClientHandler::setResponseCode(long code) { _responseCode = code; }
|
||||
|
||||
} // End of namespace Networking
|
54
backends/networking/sdl_net/getclienthandler.h
Normal file
54
backends/networking/sdl_net/getclienthandler.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef BACKENDS_NETWORKING_SDL_NET_GETCLIENTHANDLER_H
|
||||
#define BACKENDS_NETWORKING_SDL_NET_GETCLIENTHANDLER_H
|
||||
|
||||
#include "backends/networking/sdl_net/client.h"
|
||||
#include "common/hashmap.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/hash-str.h"
|
||||
|
||||
namespace Networking {
|
||||
|
||||
class GetClientHandler: public ClientHandler {
|
||||
Common::HashMap<Common::String, Common::String> _specialHeaders;
|
||||
long _responseCode;
|
||||
bool _headersPrepared;
|
||||
Common::String _headers;
|
||||
Common::SeekableReadStream *_stream;
|
||||
|
||||
static const char *responseMessage(long responseCode);
|
||||
void prepareHeaders();
|
||||
|
||||
public:
|
||||
GetClientHandler(Common::SeekableReadStream *stream);
|
||||
virtual ~GetClientHandler();
|
||||
|
||||
virtual void handle(Client *client);
|
||||
void setHeader(Common::String name, Common::String value);
|
||||
void setResponseCode(long code);
|
||||
};
|
||||
|
||||
} // End of namespace Networking
|
||||
|
||||
#endif
|
|
@ -23,6 +23,8 @@
|
|||
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||
|
||||
#include "backends/networking/sdl_net/localwebserver.h"
|
||||
#include "backends/networking/sdl_net/getclienthandler.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/str.h"
|
||||
#include "common/system.h"
|
||||
#include "common/timer.h"
|
||||
|
@ -30,6 +32,7 @@
|
|||
#include <SDL/SDL_net.h>
|
||||
|
||||
namespace Common {
|
||||
class MemoryReadWriteStream;
|
||||
|
||||
DECLARE_SINGLETON(Networking::LocalWebserver);
|
||||
|
||||
|
@ -126,16 +129,17 @@ void LocalWebserver::handleClient(uint32 i) {
|
|||
//if PUT, check whether we know a handler for that URL
|
||||
//if no handler, answer with default BAD REQUEST
|
||||
warning("headers %s", _client[i].headers().c_str());
|
||||
_client[i].close();
|
||||
setClientGetHandler(_client[i], "<html><head><title>ScummVM</title></head><body>Hello, World!</body></html>");
|
||||
break;
|
||||
case BAD_REQUEST:
|
||||
//TODO: answer with BAD REQUEST
|
||||
_client[i].close();
|
||||
setClientGetHandler(_client[i], "<html><head><title>ScummVM - Bad Request</title></head><body>BAD REQUEST</body></html>", 400);
|
||||
break;
|
||||
case BEING_HANDLED:
|
||||
_client[i].handle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LocalWebserver::acceptClient() {
|
||||
if (!SDLNet_SocketReady(_serverSocket)) return;
|
||||
|
||||
|
@ -150,4 +154,13 @@ void LocalWebserver::acceptClient() {
|
|||
_client[_clients++].open(_set, client);
|
||||
}
|
||||
|
||||
void LocalWebserver::setClientGetHandler(Client &client, Common::String response, long code) {
|
||||
byte *data = new byte[response.size()];
|
||||
memcpy(data, response.c_str(), response.size());
|
||||
Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, response.size(), DisposeAfterUse::YES);
|
||||
GetClientHandler *handler = new GetClientHandler(stream);
|
||||
handler->setResponseCode(code);
|
||||
client.setHandler(handler);
|
||||
}
|
||||
|
||||
} // End of namespace Networking
|
||||
|
|
|
@ -51,6 +51,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> {
|
|||
void handle();
|
||||
void handleClient(uint32 i);
|
||||
void acceptClient();
|
||||
void setClientGetHandler(Client &client, Common::String response, long code = 200);
|
||||
|
||||
public:
|
||||
LocalWebserver();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue