diff options
Diffstat (limited to 'backends')
-rw-r--r-- | backends/networking/sdl_net/client.cpp | 79 | ||||
-rw-r--r-- | backends/networking/sdl_net/client.h | 11 | ||||
-rw-r--r-- | backends/networking/sdl_net/localwebserver.cpp | 14 | ||||
-rw-r--r-- | backends/networking/sdl_net/localwebserver.h | 6 |
4 files changed, 93 insertions, 17 deletions
diff --git a/backends/networking/sdl_net/client.cpp b/backends/networking/sdl_net/client.cpp index 0e86610e54..60f6be2adc 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), _handler(nullptr) {} +Client::Client() : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) {} -Client::Client(SDLNet_SocketSet set, TCPsocket socket): _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) { +Client::Client(SDLNet_SocketSet set, TCPsocket socket) : _state(INVALID), _set(nullptr), _socket(nullptr), _handler(nullptr) { open(set, socket); } @@ -38,7 +38,7 @@ Client::~Client() { close(); } -void Client::open(SDLNet_SocketSet set, TCPsocket socket) { +void Client::open(SDLNet_SocketSet set, TCPsocket socket) { if (_state != INVALID) close(); _state = READING_HEADERS; _socket = socket; @@ -56,14 +56,14 @@ void Client::readHeaders() { if (!SDLNet_SocketReady(_socket)) return; const uint32 BUFFER_SIZE = 16 * 1024; - char buffer[BUFFER_SIZE]; - int bytes = SDLNet_TCP_Recv(_socket, buffer, BUFFER_SIZE); + char buffer[BUFFER_SIZE]; + int bytes = SDLNet_TCP_Recv(_socket, buffer, BUFFER_SIZE); if (bytes <= 0) { - warning("Client::readHeaders recv fail"); + warning("Client::readHeaders recv fail"); close(); return; } - _headers += Common::String(buffer, bytes); + _headers += Common::String(buffer, bytes); checkIfHeadersEnded(); checkIfBadRequest(); } @@ -74,7 +74,7 @@ void Client::checkIfHeadersEnded() { if (position) _state = READ_HEADERS; } -void Client::checkIfBadRequest() { +void Client::checkIfBadRequest() { uint32 headersSize = _headers.size(); bool bad = false; @@ -92,7 +92,7 @@ void Client::checkIfBadRequest() { if (headersSize > length) headersSize = length; for (uint32 i = 0; i < headersSize; ++i) { if (_headers[i] != ' ') buf += _headers[i]; - if (_headers[i] == ' ' || i == headersSize-1) { + if (_headers[i] == ' ' || i == headersSize - 1) { if (method == "") method = buf; else if (path == "") path = buf; else if (http == "") http = buf; @@ -109,11 +109,35 @@ void Client::checkIfBadRequest() { //check that HTTP/<VERSION> is OK if (!http.hasPrefix("HTTP/")) bad = true; + + _method = method; + parsePathQueryAndAnchor(path); } } } - if (bad) _state = BAD_REQUEST; + if (bad) _state = BAD_REQUEST; +} + +void Client::parsePathQueryAndAnchor(Common::String path) { + //<path>[?query][#anchor] + bool readingPath = true; + bool readingQuery = false; + _path = ""; + _query = ""; + _anchor = ""; + for (uint32 i = 0; i < path.size(); ++i) { + if (readingPath) { + if (path[i] == '?') { + readingPath = false; + readingQuery = true; + } else _path += path[i]; + } else if(readingQuery) { + if (path[i] == '#') { + readingQuery = false; + } else _query += path[i]; + } else _anchor += path[i]; + } } void Client::setHandler(ClientHandler *handler) { @@ -147,9 +171,40 @@ void Client::close() { } -ClientState Client::state() { return _state; } +ClientState Client::state() const { return _state; } + +Common::String Client::headers() const { return _headers; } + +Common::String Client::method() const { return _method; } + +Common::String Client::path() const { return _path; } + +Common::String Client::query() const { return _query; } + +Common::String Client::queryParameter(Common::String name) const { + // this approach is a bit slower than searching for the <name> + // yet I believe it to be the right one, because we probably can have "<name>=" in the value of other key + Common::String key = ""; + Common::String value = ""; + bool readingKey = true; + for (uint32 i = 0; i < _query.size(); ++i) { + if (readingKey) { + if (_query[i] == '=') { + readingKey = false; + value = ""; + } else key += _query[i]; + } else { + if (_query[i] == '&') { + if (key == name) return value; + readingKey = true; + key = ""; + } else value += _query[i]; + } + } + return ""; +} -Common::String Client::headers() { return _headers; } +Common::String Client::anchor() const { return _anchor; } bool Client::socketIsReady() { return SDLNet_SocketReady(_socket); } diff --git a/backends/networking/sdl_net/client.h b/backends/networking/sdl_net/client.h index 8b7a7d5295..eba5dabfba 100644 --- a/backends/networking/sdl_net/client.h +++ b/backends/networking/sdl_net/client.h @@ -52,10 +52,12 @@ class Client { SDLNet_SocketSet _set; TCPsocket _socket; Common::String _headers; + Common::String _method, _path, _query, _anchor; ClientHandler *_handler; void checkIfHeadersEnded(); void checkIfBadRequest(); + void parsePathQueryAndAnchor(Common::String path); public: Client(); @@ -68,8 +70,13 @@ public: void handle(); void close(); - ClientState state(); - Common::String headers(); + ClientState state() const; + Common::String headers() const; + Common::String method() const; + Common::String path() const; + Common::String query() const; + Common::String queryParameter(Common::String name) const; + Common::String anchor() const; /** * Return SDLNet_SocketReady(_socket). diff --git a/backends/networking/sdl_net/localwebserver.cpp b/backends/networking/sdl_net/localwebserver.cpp index a25dd74121..689bab4cb9 100644 --- a/backends/networking/sdl_net/localwebserver.cpp +++ b/backends/networking/sdl_net/localwebserver.cpp @@ -110,6 +110,11 @@ void LocalWebserver::stop() { } } +void LocalWebserver::addPathHandler(Common::String path, ClientHandler handler) { + if (_pathHandlers.contains(path)) warning("LocalWebserver::addPathHandler: path already had a handler"); + _pathHandlers[path] = handler; +} + void LocalWebserver::handle() { int numready = SDLNet_CheckSockets(_set, 0); if (numready == -1) { @@ -127,10 +132,13 @@ void LocalWebserver::handleClient(uint32 i) { case READ_HEADERS: //decide what to do next with that client //if GET, check whether we know a handler for such URL //if PUT, check whether we know a handler for that URL + if (_pathHandlers.contains(_client[i].path())) + (*_pathHandlers[_client[i].path()])(_client[i]); + + if (_client[i].state() == BEING_HANDLED || _client[i].state() == INVALID) break; + //if no handler, answer with default BAD REQUEST - warning("headers %s", _client[i].headers().c_str()); - setClientGetHandler(_client[i], "<html><head><title>ScummVM</title></head><body>Hello, World!</body></html>"); - break; + //fallthrough case BAD_REQUEST: setClientGetHandler(_client[i], "<html><head><title>ScummVM - Bad Request</title></head><body>BAD REQUEST</body></html>", 400); break; diff --git a/backends/networking/sdl_net/localwebserver.h b/backends/networking/sdl_net/localwebserver.h index ab453988a8..bda0bfd7c1 100644 --- a/backends/networking/sdl_net/localwebserver.h +++ b/backends/networking/sdl_net/localwebserver.h @@ -24,6 +24,8 @@ #define BACKENDS_NETWORKING_SDL_NET_LOCALWEBSERVER_H #include "backends/networking/sdl_net/client.h" +#include "common/callback.h" +#include "common/hash-str.h" #include "common/singleton.h" #include "common/scummsys.h" @@ -38,6 +40,8 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> { static const uint32 SERVER_PORT = 12345; static const uint32 MAX_CONNECTIONS = 10; + typedef Common::BaseCallback<Client &> *ClientHandler; + friend void localWebserverTimer(void *); //calls handle() SDLNet_SocketSet _set; @@ -45,6 +49,7 @@ class LocalWebserver : public Common::Singleton<LocalWebserver> { Client _client[MAX_CONNECTIONS]; int _clients; bool _timerStarted; + Common::HashMap<Common::String, ClientHandler> _pathHandlers; void startTimer(int interval = TIMER_INTERVAL); void stopTimer(); @@ -59,6 +64,7 @@ public: void start(); void stop(); + void addPathHandler(Common::String path, ClientHandler handler); }; /** Shortcut for accessing the local webserver. */ |