WebServer
The WebServer and WebSession classes define a flexible and extensible framework for building multithreaded HTTP and WebSocket servers.
-
WebServer is a base class for implementing custom HTTP/WebSocket servers. It listens for incoming connections, spawns threads, and delegates client handling to WebSession instances.
-
WebSession is an abstract base class that represents an individual client session. It provides core functionality for reading HTTP requests, handling WebSocket upgrades, and managing socket communication with support for timeouts and streaming.
#include <network/webserver/include/TellusimWebServer.h>
Example
This example showcases how to use the WebServer and WebSession framework to implement a simple multithreaded echo server with WebSocket support.
// Custom Session class
class Session : public WebSession {
public:
Session(Socket &socket) : WebSession(socket) { }
virtual bool read() {
// read websocket
if(websocket) {
// read message
Blob read_blob;
if(!read_socket(read_blob)) return false;
TS_LOGF(Message, "Session::read(): %s\n", read_blob.gets().get());
// write response
return write_socket(getSocket(), read_blob, false);
}
// read request
Request request;
if(!read_request(request)) return false;
// server header
String header, type, response;
header = "Server: Tellusim::WebServer\r\n"
"Access-Control-Allow-Origin: *\r\n"
"Cache-Control: no-cache\r\n"
"Connection: keep-alive\r\n"
"Keep-Alive: timeout=8\r\n";
// request extension
String extension = request.url.extension().lower();
// server request
if(request.url == "/" || extension == "html" || extension == "js") {
if(extension == "js") type = "application/javascript";
else type = "text/html; charset=UTF-8";
Source source;
if(source.open("www/" + get_filename(request))) response = source.gets();
else TS_LOGF(Error, "Session::read(): can't open \"%s\" file\n", request.url.get());
}
else if(request.url == "/socket" && request.upgrade == "websocket" && request.websocket) {
websocket = true;
socket.setBlock(true);
response = String::format("HTTP/1.1 101 Switching Protocols\r\n%s"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: %s\r\n"
"\r\n", header.get(), get_handshake(request).get());
}
else {
TS_LOGF(Error, "Session::read(): unknown server request \"%s\"\n", request.url.get());
}
// content type
if(type) header += String::format("Content-Type: %s\r\n", type.get());
// server response
if(!response.begins("HTTP")) {
if(response) response = String::format("HTTP/1.1 200 Ok\r\n%sContent-Length: %u\r\n\r\n", header.get(), response.size()) + response;
else response = String::format("HTTP/1.1 404 Not Found\r\n%sContent-Length: 0\r\n\r\n", header.get());
}
// write response
socket.puts(response);
return true;
}
private:
bool websocket = false;
};
// Custom Server class
class Server : public WebServer {
public:
Server() { }
private:
// create session instance
virtual WebSession *create_session(Socket &socket) {
return new Session(socket);
}
};
// create server
Server server;
// server timeout
server.setTimeout(1);
// initialize server
if(!server.init(8080)) return false;
// run the server
if(!server.run()) return false;
function init() {
let websocket = new WebSocket('ws://' + window.location.host + '/socket');
websocket.onopen = function(event) {
websocket.send('ping');
}
websocket.onmessage = function(event) {
if(event.data == 'ping') websocket.send(event.data + ' pong');
}
}
Session::read(): ping
Session::read(): ping pong