aboutsummaryrefslogtreecommitdiff
path: root/backends
diff options
context:
space:
mode:
Diffstat (limited to 'backends')
-rw-r--r--backends/networking/sdl_net/client.cpp79
-rw-r--r--backends/networking/sdl_net/client.h11
-rw-r--r--backends/networking/sdl_net/localwebserver.cpp14
-rw-r--r--backends/networking/sdl_net/localwebserver.h6
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. */