aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Tkachev2016-06-15 23:54:53 +0600
committerAlexander Tkachev2016-08-24 16:07:55 +0600
commit13c54f66850226a516da0450b633ebafbc26584e (patch)
tree5c2582cc47296bee1fd3dc7e8f55bddb102bc61f
parent99c51380fdc866ce393c52eb41803a9ec119a9ad (diff)
downloadscummvm-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.
-rw-r--r--backends/module.mk1
-rw-r--r--backends/networking/sdl_net/client.cpp25
-rw-r--r--backends/networking/sdl_net/client.h25
-rw-r--r--backends/networking/sdl_net/getclienthandler.cpp94
-rw-r--r--backends/networking/sdl_net/getclienthandler.h54
-rw-r--r--backends/networking/sdl_net/localwebserver.cpp21
-rw-r--r--backends/networking/sdl_net/localwebserver.h1
7 files changed, 212 insertions, 9 deletions
diff --git a/backends/module.mk b/backends/module.mk
index 74412a792a..0f1eea0090 100644
--- a/backends/module.mk
+++ b/backends/module.mk
@@ -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
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();