diff options
author | Alexander Tkachev | 2016-06-15 23:54:53 +0600 |
---|---|---|
committer | Alexander Tkachev | 2016-08-24 16:07:55 +0600 |
commit | 13c54f66850226a516da0450b633ebafbc26584e (patch) | |
tree | 5c2582cc47296bee1fd3dc7e8f55bddb102bc61f /backends/networking | |
parent | 99c51380fdc866ce393c52eb41803a9ec119a9ad (diff) | |
download | scummvm-rg350-13c54f66850226a516da0450b633ebafbc26584e.tar.gz scummvm-rg350-13c54f66850226a516da0450b633ebafbc26584e.tar.bz2 scummvm-rg350-13c54f66850226a516da0450b633ebafbc26584e.zip |
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.
Diffstat (limited to 'backends/networking')
-rw-r--r-- | backends/networking/sdl_net/client.cpp | 25 | ||||
-rw-r--r-- | backends/networking/sdl_net/client.h | 25 | ||||
-rw-r--r-- | backends/networking/sdl_net/getclienthandler.cpp | 94 | ||||
-rw-r--r-- | backends/networking/sdl_net/getclienthandler.h | 54 | ||||
-rw-r--r-- | backends/networking/sdl_net/localwebserver.cpp | 21 | ||||
-rw-r--r-- | backends/networking/sdl_net/localwebserver.h | 1 |
6 files changed, 211 insertions, 9 deletions
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index 8ab9ed385e..22bfb60a2f 100644 --- a/backends/networking/sdl_net/client.cpp +++ b/backends/networking/sdl_net/client.cpp @@ -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); } @@ -74,8 +74,7 @@ void Client::checkIfHeadersEnded() { if (position) _state = READ_HEADERS; } -void Client::checkIfBadRequest() { - if (_state != READING_HEADERS) return; +void Client::checkIfBadRequest() { 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 diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h index 7f78947223..8b7a7d5295 100644 --- a/backends/networking/sdl_net/client.h +++ b/backends/networking/sdl_net/client.h @@ -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 diff --git a/backends/networking/sdl_net/getclienthandler.cpp b/backends/networking/sdl_net/getclienthandler.cpp new file mode 100644 index 0000000000..48a03e02f9 --- /dev/null +++ b/backends/networking/sdl_net/getclienthandler.cpp @@ -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 diff --git a/backends/networking/sdl_net/getclienthandler.h b/backends/networking/sdl_net/getclienthandler.h new file mode 100644 index 0000000000..f434df1a45 --- /dev/null +++ b/backends/networking/sdl_net/getclienthandler.h @@ -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 diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp index ee67f787c3..a25dd74121 100644 --- a/backends/networking/sdl_net/localwebserver.cpp +++ b/backends/networking/sdl_net/localwebserver.cpp @@ -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 diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h index 8db05f72e1..ab453988a8 100644 --- a/backends/networking/sdl_net/localwebserver.h +++ b/backends/networking/sdl_net/localwebserver.h @@ -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(); |