diff --git a/Changelog b/Changelog index 3ef160b..f3ee055 100644 --- a/Changelog +++ b/Changelog @@ -1,3 +1,6 @@ +2026-01-06: Version: 0.3 +- Added a simple WebServer functionality. + 2023-11-17: - TCP::WebGetFile timeout increased to 20000ms and added another function including an timeout parameter. diff --git a/Makefile b/Makefile index c338512..d52f0ee 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ .SILENT: help -VERSION=0.2 +VERSION=0.3 OBJLIB_NAME=UDPTCPNetwork -include Makefile.rules diff --git a/UDPTCPNetwork-webutils.h b/UDPTCPNetwork-webutils.h index 8320486..9e78419 100644 --- a/UDPTCPNetwork-webutils.h +++ b/UDPTCPNetwork-webutils.h @@ -71,6 +71,7 @@ class WebServerClient { SSLSocket *ssl; // only set if SSL is used char buffer[WEB_READ_BUFFER_SIZE]; std::string DocumentRoot; + void *callback_request; protected: WebRequestBuffer ReqBuffer; @@ -93,8 +94,41 @@ class WebServerClient { std::string Get_WebFile_FullPath(std::string file); int SendResponseFile (WebRequestBuffer *request, std::string fname, std::string addheader); - virtual int Loop(); - virtual int HandleRequest (); + int SendResponseFileFromMemory (WebRequestBuffer *request, std::string fname, std::string addheader, + 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 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 diff --git a/test-webserver.cc b/test-webserver.cc index 70f4d53..cc560d4 100644 --- a/test-webserver.cc +++ b/test-webserver.cc @@ -1,4 +1,7 @@ #include +#include +#include + #include "UDPTCPNetwork.h" #define DEFAULT_HTTP_PORT 2222 @@ -9,107 +12,93 @@ int running = 1; -class SimpleWebSrvClient: public WebServerClient { +static void sig_int(int); +int SetupSignals(); +std::string GenerateHtmlFile(); + +class SimpleWebServer : public WebServer { + private: + protected: public: - SimpleWebSrvClient () {}; - ~SimpleWebSrvClient () {}; - virtual int HandleRequest (); + int HandleRequest (WebRequestBuffer *requestbuffer, WebServerClient *webclient); }; +int SimpleWebServer::HandleRequest (WebRequestBuffer *requestbuffer, WebServerClient *webclient) { + if (requestbuffer == NULL || webclient == NULL) return 0; -int SimpleWebSrvClient::HandleRequest() { - std::string request = ReqBuffer.GetRequest(); - int requesttype = ReqBuffer.GetType(); - - printf ("SimpleWebSrvClient::HandleRequest() Request:%s Type:%d\n", request.c_str(), requesttype); + std::string request = requestbuffer->GetRequest(); + printf ("SimpleWebServerClient::HandleRequest() Request:%s\n", request.c_str()); 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, "", + (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 webclients; - - TCP http; - TCP https; + SimpleWebServer webserver; - std::string ssl_cert = DEFAULT_CERT; - std::string ssl_key = DEFAULT_KEY; + SetupSignals(); - if (http.Listen(DEFAULT_HTTP_PORT) != 1) { - printf ("error on listen\n"); - exit (1); - } - printf ("test server is running on http://localhost:%d\n", DEFAULT_HTTP_PORT); + webserver.SetupPorts(DEFAULT_HTTP_PORT, DEFAULT_HTTPS_PORT); + webserver.SetupSSL(DEFAULT_KEY, DEFAULT_CERT); - if (https.Listen(DEFAULT_HTTPS_PORT) != 1) { - printf ("error on listen\n"); - exit (1); - } - printf ("test server is running on https://localhost:%d\n", DEFAULT_HTTPS_PORT); + webserver.Start(); + running = 1; while (running == 1) { - SimpleWebSrvClient *webclient = NULL; - list::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) { - printf ("new %s connection from %s\n", ssl_enabled ? "HTTPS" : "HTTP", tcpclient->GetRemoteAddr().c_str()); - - if (webclients.size() > MAX_CLIENTS) { - printf ("max connections reached. closing connection.\n"); - tcpclient->Close(); - 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) { - printf ("webclient could not connect. closing connection\n"); - delete webclient; - delete tcpclient; - continue; - } - 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++) { - 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; - } - } - + webserver.Loop(); usleep (1000); } }; + +int SetupSignals() { + if (signal(SIGINT, sig_int) == SIG_ERR) { + errorexit ("%s:%d could not set signal for SIGINT\n", __FILE__, __LINE__); + return 0; + } + if (signal(SIGTERM, sig_int) == SIG_ERR) { + errorexit ("%s:%d could not set signal for SIGTERM\n", __FILE__, __LINE__); + return 0; + } + if (signal(SIGHUP, sig_int) == SIG_ERR) { + errorexit ("%s:%d could not set signal for SIGHUB\n", __FILE__, __LINE__); + return 0; + } + if (signal(SIGUSR1, sig_int) == SIG_ERR) { + errorexit ("%s:%d could not set signal for SIGHUB\n", __FILE__, __LINE__); + return 0; + } + return 1; +}; + +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 = "Random Text"; + html += "

just some randdom number "; + html += to_string(rand()); + html += ".

Return to INDEX.HTML"; + + return html; +}; diff --git a/webserver.cc b/webserver.cc index 459de6d..9583b9c 100644 --- a/webserver.cc +++ b/webserver.cc @@ -12,6 +12,7 @@ WebServerClient::WebServerClient () { ssl = NULL; ssl_key = ""; 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 -/// @return -1 on error, 0 no data , 1 request complete -int WebServerClient::Loop () { +/// @brief Read data from the network fill in the buffer. The Request must be handled from the server +/// @return 0 no data , 1 request complete +WebRequestBuffer *WebServerClient::Loop () { int len; if (ssl) { @@ -73,28 +74,10 @@ int WebServerClient::Loop () { else { len = tcp->ReadTimeout(buffer, WEB_READ_BUFFER_SIZE, 20); } - if (len < 0) return -1; - if (len == 0) return 0; - + if (len < 0) return NULL; + if (len == 0) return &ReqBuffer; ReqBuffer.AddBuffer (buffer, len); - if (ReqBuffer.RequestComplete()) { - 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; + return &ReqBuffer; }; @@ -203,7 +186,52 @@ std::string WebServerClient::GetFileType (std::string fname) { if (suffix.compare (".jpeg") == 0) return "image/jpeg"; 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; +}; @@ -283,7 +311,7 @@ int WebServerClient::SendResponseFile (WebRequestBuffer *request, std::string fn free (outbuffer); return 1; -} +}; std::string WebServerClient::Get_WebFile_FullPath(std::string file) { @@ -302,3 +330,183 @@ std::string WebServerClient::Get_WebFile_FullPath(std::string file) { 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::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::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; +}; + diff --git a/www/error404.html b/www/error404.html new file mode 100644 index 0000000..cca60a4 --- /dev/null +++ b/www/error404.html @@ -0,0 +1,13 @@ + + + 404 - TestWebserver - ERROR404 + + +

ERROR404

+

+ the file specified could not been found. +

+ + + + diff --git a/www/index.html b/www/index.html index 2cadd00..754c61d 100644 --- a/www/index.html +++ b/www/index.html @@ -6,7 +6,8 @@

Testpage

This is only a test for more detail see at https://steffen.gulpe.de/gitea/steffen/libUDPTCPNetwork -

+

+ test.html is a hard coded HTML page.