added webserver functionality.

master
Steffen Pohle 1 month ago
parent 228c347581
commit 615dc363fb

@ -1,3 +1,6 @@
2026-01-06: Version: 0.3
- Added a simple WebServer functionality.
2023-11-17: 2023-11-17:
- TCP::WebGetFile timeout increased to 20000ms and added another function including an timeout parameter. - TCP::WebGetFile timeout increased to 20000ms and added another function including an timeout parameter.

@ -1,5 +1,5 @@
.SILENT: help .SILENT: help
VERSION=0.2 VERSION=0.3
OBJLIB_NAME=UDPTCPNetwork OBJLIB_NAME=UDPTCPNetwork
-include Makefile.rules -include Makefile.rules

@ -71,6 +71,7 @@ class WebServerClient {
SSLSocket *ssl; // only set if SSL is used SSLSocket *ssl; // only set if SSL is used
char buffer[WEB_READ_BUFFER_SIZE]; char buffer[WEB_READ_BUFFER_SIZE];
std::string DocumentRoot; std::string DocumentRoot;
void *callback_request;
protected: protected:
WebRequestBuffer ReqBuffer; WebRequestBuffer ReqBuffer;
@ -93,8 +94,41 @@ class WebServerClient {
std::string Get_WebFile_FullPath(std::string file); std::string Get_WebFile_FullPath(std::string file);
int SendResponseFile (WebRequestBuffer *request, std::string fname, std::string addheader); int SendResponseFile (WebRequestBuffer *request, std::string fname, std::string addheader);
virtual int Loop(); int SendResponseFileFromMemory (WebRequestBuffer *request, std::string fname, std::string addheader,
virtual int HandleRequest (); void *memptr, int memsize);
WebRequestBuffer * Loop();
}; };
class WebServer {
private:
int running;
int conf_maxclients;
int conf_httpport;
int conf_httpsport;
std::string conf_docroot;
std::string conf_sslkey;
std::string conf_sslcert;
list<WebServerClient*> webclients;
TCP http;
TCP https;
protected:
public:
WebServer();
~WebServer();
int SetupMaxClients(int maxclients);
int SetupDocumentRoot(std::string docroot);
int SetupSSL(std::string sslkeyfile, std::string sslcertfile);
int SetupPorts (int httpport, int httpsport);
int Start();
int Loop();
int Stop();
virtual int HandleRequest (WebRequestBuffer *requestbuffer, WebServerClient *webclient);
};
#endif #endif

@ -1,4 +1,7 @@
#include <unistd.h> #include <unistd.h>
#include <sysexits.h>
#include <signal.h>
#include "UDPTCPNetwork.h" #include "UDPTCPNetwork.h"
#define DEFAULT_HTTP_PORT 2222 #define DEFAULT_HTTP_PORT 2222
@ -9,107 +12,93 @@
int running = 1; int running = 1;
class SimpleWebSrvClient: public WebServerClient { static void sig_int(int);
int SetupSignals();
std::string GenerateHtmlFile();
class SimpleWebServer : public WebServer {
private:
protected:
public: public:
SimpleWebSrvClient () {}; int HandleRequest (WebRequestBuffer *requestbuffer, WebServerClient *webclient);
~SimpleWebSrvClient () {};
virtual int HandleRequest ();
}; };
int SimpleWebServer::HandleRequest (WebRequestBuffer *requestbuffer, WebServerClient *webclient) {
if (requestbuffer == NULL || webclient == NULL) return 0;
int SimpleWebSrvClient::HandleRequest() { std::string request = requestbuffer->GetRequest();
std::string request = ReqBuffer.GetRequest(); printf ("SimpleWebServerClient::HandleRequest() Request:%s\n", request.c_str());
int requesttype = ReqBuffer.GetType();
printf ("SimpleWebSrvClient::HandleRequest() Request:%s Type:%d\n", request.c_str(), requesttype);
if (request.compare ("/") == 0) request = "/index.html"; if (request.compare ("/") == 0) request = "/index.html";
SendResponseFile(&ReqBuffer, request, ""); if (request.find("/test.html") != std::string::npos) {
std::string htmlfile = GenerateHtmlFile();
ReqBuffer.Clear(); if (webclient->SendResponseFileFromMemory(requestbuffer, request, "",
return 1; (void*) htmlfile.c_str(), strlen(htmlfile.c_str())) != 1) return 0;
} }
else
if (webclient->SendResponseFile(requestbuffer, request, "") != 1) return 0;
requestbuffer->Clear();
return 1;
};
int main (int argc, char **argv) {
list<SimpleWebSrvClient*> webclients;
TCP http;
TCP https;
std::string ssl_cert = DEFAULT_CERT;
std::string ssl_key = DEFAULT_KEY;
if (http.Listen(DEFAULT_HTTP_PORT) != 1) { int main (int argc, char **argv) {
printf ("error on listen\n"); SimpleWebServer webserver;
exit (1);
}
printf ("test server is running on http://localhost:%d\n", DEFAULT_HTTP_PORT);
if (https.Listen(DEFAULT_HTTPS_PORT) != 1) { SetupSignals();
printf ("error on listen\n");
exit (1);
}
printf ("test server is running on https://localhost:%d\n", DEFAULT_HTTPS_PORT);
while (running == 1) { webserver.SetupPorts(DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT);
SimpleWebSrvClient *webclient = NULL; webserver.SetupSSL(DEFAULT_KEY, DEFAULT_CERT);
list<SimpleWebSrvClient*>::iterator wci;
TCP *tcpclient = NULL;
int ssl_enabled = 1;
int res;
//
// any new connection?
if ((tcpclient = https.Accept()) == NULL) {
ssl_enabled = 0;
tcpclient = http.Accept();
}
if (tcpclient) { webserver.Start();
printf ("new %s connection from %s\n", ssl_enabled ? "HTTPS" : "HTTP", tcpclient->GetRemoteAddr().c_str()); running = 1;
if (webclients.size() > MAX_CLIENTS) { while (running == 1) {
printf ("max connections reached. closing connection.\n"); webserver.Loop();
tcpclient->Close(); usleep (1000);
delete tcpclient;
continue;
} }
};
webclient = new SimpleWebSrvClient();
if (ssl_enabled) res = webclient->Accept(tcpclient, ssl_key, ssl_cert);
else res = webclient->Accept(tcpclient);
if (res == 0) { int SetupSignals() {
printf ("webclient could not connect. closing connection\n"); if (signal(SIGINT, sig_int) == SIG_ERR) {
delete webclient; errorexit ("%s:%d could not set signal for SIGINT\n", __FILE__, __LINE__);
delete tcpclient; return 0;
continue;
} }
webclient->SetDecoumentRoot("./www"); if (signal(SIGTERM, sig_int) == SIG_ERR) {
webclients.push_back(webclient); errorexit ("%s:%d could not set signal for SIGTERM\n", __FILE__, __LINE__);
printf ("add new connection to client list\n"); return 0;
} }
if (signal(SIGHUP, sig_int) == SIG_ERR) {
// errorexit ("%s:%d could not set signal for SIGHUB\n", __FILE__, __LINE__);
// go through all the clients and check for new requests return 0;
for (wci = webclients.begin(); wci != webclients.end(); wci++) {
int res = 0;
webclient = *wci;
res = webclient->Loop();
if (res == -1) {
// error on loop, remove and delete webclient.
printf ("remove connection\n");
webclients.remove(webclient);
delete webclient;
wci = webclients.begin();
if (wci == webclients.end()) continue;
} }
if (signal(SIGUSR1, sig_int) == SIG_ERR) {
errorexit ("%s:%d could not set signal for SIGHUB\n", __FILE__, __LINE__);
return 0;
} }
return 1;
};
usleep (1000); static void sig_int(int sig) {
if (signal (SIGINT, sig_int) == SIG_ERR) errorexit ("could not set up signal SIGINT\n");
printf ("\n\nSignal Int\n\n");
running = 0;
} }
};
std::string GenerateHtmlFile() {
std::string html;
html = "<html><head><title>Random Text</title></head><body>";
html += "<p>just some randdom number <b>";
html += to_string(rand());
html += "</b>.<p>Return to <a href=\"index.html\">INDEX.HTML</a></body></html>";
return html;
};

@ -12,6 +12,7 @@ WebServerClient::WebServerClient () {
ssl = NULL; ssl = NULL;
ssl_key = ""; ssl_key = "";
ssl_cert = ""; ssl_cert = "";
callback_request = NULL;
}; };
@ -62,9 +63,9 @@ int WebServerClient::Accept (TCP *socket) {
/// @brief Read data from the network and check if the request is complete /// @brief Read data from the network fill in the buffer. The Request must be handled from the server
/// @return -1 on error, 0 no data , 1 request complete /// @return 0 no data , 1 request complete
int WebServerClient::Loop () { WebRequestBuffer *WebServerClient::Loop () {
int len; int len;
if (ssl) { if (ssl) {
@ -73,28 +74,10 @@ int WebServerClient::Loop () {
else { else {
len = tcp->ReadTimeout(buffer, WEB_READ_BUFFER_SIZE, 20); len = tcp->ReadTimeout(buffer, WEB_READ_BUFFER_SIZE, 20);
} }
if (len < 0) return -1; if (len < 0) return NULL;
if (len == 0) return 0; if (len == 0) return &ReqBuffer;
ReqBuffer.AddBuffer (buffer, len); ReqBuffer.AddBuffer (buffer, len);
if (ReqBuffer.RequestComplete()) { return &ReqBuffer;
HandleRequest ();
return 1;
}
return 0;
};
int WebServerClient::HandleRequest() {
std::string request = ReqBuffer.GetRequest();
printf ("WebServerClient::HandleRequest() Request:%s\n", request.c_str());
if (request.compare ("/") == 0) request = "/index.html";
SendResponseFile(&ReqBuffer, request, "");
ReqBuffer.Clear();
return 1;
}; };
@ -203,8 +186,53 @@ std::string WebServerClient::GetFileType (std::string fname) {
if (suffix.compare (".jpeg") == 0) return "image/jpeg"; if (suffix.compare (".jpeg") == 0) return "image/jpeg";
return res; return res;
};
int WebServerClient::SendResponseFileFromMemory (WebRequestBuffer *request, std::string fname, std::string addheader,
void *memptr, int memsize) {
char *outbuffer = NULL;
int outbufferpos = 0;
int error = 200;
long int l;
std::string filetype = "text/html;charset=utf-8";
std::string header;
struct stat filestat;
int filefd;
ssize_t bytestosend;
//
// determine file type and size
filetype = GetFileType(fname);
//
// send header and file
header = CreateHeader (request, error, filetype, memsize, addheader);
outbufferpos = header.length();
outbuffer = (char*) malloc (outbufferpos + memsize + 1);
memcpy (outbuffer, header.c_str(), outbufferpos);
//
// append data to send
memcpy (outbuffer+outbufferpos, memptr, memsize);
bytestosend = outbufferpos + memsize;
if (ssl) l = ssl->Write(outbuffer, bytestosend);
else l = tcp->Write(outbuffer, bytestosend);
// debug ("send file %s with %ld bytes\n", fname.c_str(), filereadbytes);
if (l != bytestosend) {
debug ("could not send all data. Error:%s\n", strerror(errno));
free (outbuffer);
return 0;
} }
free (outbuffer);
return 1;
};
/// @brief send a file as response /// @brief send a file as response
@ -283,7 +311,7 @@ int WebServerClient::SendResponseFile (WebRequestBuffer *request, std::string fn
free (outbuffer); free (outbuffer);
return 1; return 1;
} };
std::string WebServerClient::Get_WebFile_FullPath(std::string file) { std::string WebServerClient::Get_WebFile_FullPath(std::string file) {
@ -302,3 +330,183 @@ std::string WebServerClient::Get_WebFile_FullPath(std::string file) {
return ret; return ret;
}; };
WebServer::WebServer() {
running = 0;
conf_maxclients = 10;
conf_httpport = 8080;
conf_httpsport = 8081;
conf_docroot = "./www";
conf_sslcert = "./cert.pem";
conf_sslkey = "./privkey.pem";
};
WebServer::~WebServer() {
Stop();
};
/// @brief Setup new ports. Restart needed.
/// @param httpport
/// @param httpsport
/// @return
int WebServer::SetupPorts(int httpport, int httpsport) {
int wasrunning = running;
if (wasrunning) Stop();
conf_httpport = httpport;
conf_httpsport = httpsport;
if (wasrunning) Start();
return 1;
};
/// @brief Setup document root. Restart needed.
/// @param httpport
/// @param httpsport
/// @return
int WebServer::SetupDocumentRoot(std::string docroot) {
int wasrunning = running;
if (wasrunning) Stop();
conf_docroot = docroot;
if (wasrunning) Start();
return 1;
};
/// @brief Setup SSL key and SSL certificate. Reconnect needed
/// @param sslkeyfile
/// @param sslcertfile
/// @return
int WebServer::SetupSSL(std::string sslkeyfile, std::string sslcertfile) {
conf_sslkey = sslkeyfile;
conf_sslcert = sslcertfile;
return 1;
};
int WebServer::Start() {
if (http.Listen(conf_httpport) != 1) {
printf ("error on listen\n");
return 0;
}
printf ("test server is running on port: %d\n", conf_httpport);
if (https.Listen(conf_httpsport) != 1) {
printf ("error on listen\n");
return 0;
}
printf ("test server is running on port: %d\n", conf_httpsport);
running = 1;
return 1;
};
int WebServer::Stop() {
http.Close();
https.Close();
list<WebServerClient*>::iterator wci;
for (wci = webclients.begin(); wci != webclients.end(); wci++) {
delete (WebServerClient*) *wci;
webclients.remove (*wci);
wci = webclients.begin();
if (wci == webclients.end()) continue;
}
return 1;
};
/// @brief Checks for new connections and for data on any connected webclient
/// @return -2 client could not connect, -1 on max connections, 0 on error, 1 no error
int WebServer::Loop() {
list<WebServerClient*>::iterator wci;
WebServerClient *webclient;
TCP *tcpclient = NULL;
int ssl_enabled = 1;
int res;
//
// any new connection?
if ((tcpclient = https.Accept()) == NULL) {
ssl_enabled = 0;
tcpclient = http.Accept();
}
if (tcpclient) {
printf ("new %s connection from %s\n", ssl_enabled ? "HTTPS" : "HTTP", tcpclient->GetRemoteAddr().c_str());
if (webclients.size() > conf_maxclients) {
printf ("max connections reached. closing connection.\n");
tcpclient->Close();
delete tcpclient;
return -1;
}
webclient = new WebServerClient();
if (ssl_enabled) res = webclient->Accept(tcpclient, conf_sslkey, conf_sslcert);
else res = webclient->Accept(tcpclient);
if (res == 0) {
printf ("webclient could not connect. closing connection\n");
delete webclient;
delete tcpclient;
return -2;
}
webclient->SetDecoumentRoot("./www");
webclients.push_back(webclient);
printf ("add new connection to client list\n");
}
//
// go through all the clients and check for new requests
for (wci = webclients.begin(); wci != webclients.end(); wci++) {
WebRequestBuffer *reqbuffer = NULL;
webclient = *wci;
reqbuffer = webclient->Loop();
if (reqbuffer == NULL) {
// error on loop, remove and delete webclient.
printf ("remove connection\n");
webclients.remove(webclient);
delete webclient;
wci = webclients.begin();
if (wci == webclients.end()) continue;
}
else if (reqbuffer->RequestComplete()) {
if (HandleRequest(reqbuffer, *wci) == 1) continue;
// error handling request, remove and delete webclient.
printf ("remove connection\n");
webclients.remove(webclient);
delete webclient;
wci = webclients.begin();
if (wci == webclients.end()) continue;
}
}
return 1;
};
int WebServer::HandleRequest (WebRequestBuffer *requestbuffer, WebServerClient *webclient) {
if (requestbuffer == NULL || webclient == NULL) return 0;
std::string request = requestbuffer->GetRequest();
printf ("WebServerClient::HandleRequest() Request:%s\n", request.c_str());
if (request.compare ("/") == 0) request = "/index.html";
if (webclient->SendResponseFile(requestbuffer, request, "") != 1) return 0;
requestbuffer->Clear();
return 1;
};

@ -0,0 +1,13 @@
<html>
<head>
<title>404 - TestWebserver - ERROR404</title>
</head>
<body style="background-color: black; color: red;">
<h1>ERROR404</h1>
<p>
the file specified could not been found.
</p>
</body>
</html>

@ -6,7 +6,8 @@
<h1>Testpage</h1> <h1>Testpage</h1>
<p> <p>
This is only a test for more detail see at <a href="https://steffen.gulpe.de/gitea/steffen/libUDPTCPNetwork">https://steffen.gulpe.de/gitea/steffen/libUDPTCPNetwork</a> This is only a test for more detail see at <a href="https://steffen.gulpe.de/gitea/steffen/libUDPTCPNetwork">https://steffen.gulpe.de/gitea/steffen/libUDPTCPNetwork</a>
</p> <p>
<a href="test.html">test.html</a> is a hard coded HTML page.
</body> </body>
</html> </html>

Loading…
Cancel
Save