From 622643fabfa384dcff67363f25216948d2f62a68 Mon Sep 17 00:00:00 2001 From: Steffen Pohle Date: Wed, 17 Dec 2025 23:03:49 +0100 Subject: [PATCH] adding missing files --- UDPTCPNetwork-webutils.h | 85 ++++++++++++++++ test-webserver.cc | 97 ++++++++++++++++++ webserver.cc | 74 ++++++++++++++ webutils.cc | 209 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 465 insertions(+) create mode 100644 UDPTCPNetwork-webutils.h create mode 100644 test-webserver.cc create mode 100644 webserver.cc create mode 100644 webutils.cc diff --git a/UDPTCPNetwork-webutils.h b/UDPTCPNetwork-webutils.h new file mode 100644 index 0000000..8e4c176 --- /dev/null +++ b/UDPTCPNetwork-webutils.h @@ -0,0 +1,85 @@ + +#ifndef _UDPTCPNETWORK_WEBUTILS_H_ +#define _UDPTCPNETWORK_WEBUTILS_H_ + +#include "UDPTCPNetwork.h" +#include +#include + +/*********************************************************************************/ +#define WEB_READ_BUFFER_SIZE 0x10000 + +enum { // request types + WEB_REQEST_UNSUPPORTED, + WEB_REQEST_GET, + WEB_REQEST_POST, + WEB_REQEST_MAX // not set +}; + +/*************************************************************************************** + * WebHeaderLine + */ + +class WebHeaderLine { + public: + WebHeaderLine() { Clear(); }; + ~WebHeaderLine() {}; + void Clear () { parameter = ""; value = ""; }; + std::string parameter; + std::string value; +}; + + +/*************************************************************************************** + * WebRequestBuffer + */ + +class WebRequestBuffer { + private: + char *buffer_ptr; + int buffer_size; // allocated size + int buffer_len; // current len + + int request_type; + std::string request; + std::string protocol_version; + std::list headerlines; + int buffer_pos_data; + + public: + WebRequestBuffer(); + ~WebRequestBuffer(); + + int AddBuffer (char *b, int len); + int GetBuffer (char **dstptr, int *max); + bool RequestComplete(); + void Clear() { buffer_len = 0; }; + int GetType() { return request_type; }; + std::string GetRequestRaw() { return request; }; + std::string GetRequest(); + std::string GetProtocolVersion() { return protocol_version; }; + int GetRequestCmdObj(std::string *cmd, std::string *obj, JSONParse *jp); + std::string GetCookie(std::string name); +}; + + +class WebServerClient { + private: + std::string ssl_key; + std::string ssl_cert; + TCP *tcp; + SSLSocket *ssl; // only set if SSL is used + char buffer[WEB_READ_BUFFER_SIZE]; + public: + WebServerClient(); + ~WebServerClient(); + + // will handle the request and send the response + // on error reply with ERROR 500 + int Accept (TCP *socket, std::string _ssl_key, std::string _ssl_cert); + int Accept (TCP *socket); + virtual int HandleRequest (WebRequestBuffer *req) { return 0; }; + virtual int Loop(); +}; + +#endif diff --git a/test-webserver.cc b/test-webserver.cc new file mode 100644 index 0000000..68c10a9 --- /dev/null +++ b/test-webserver.cc @@ -0,0 +1,97 @@ +#include +#include "UDPTCPNetwork.h" + +#define DEFAULT_HTTP_PORT 2222 +#define DEFAULT_HTTPS_PORT 2223 +#define DEFAULT_CERT "./cert.pem" +#define DEFAULT_KEY "./privkey.pem" +#define MAX_CLIENTS 10 + +int running = 1; + +class SimpleWebSrvClient: public WebServerClient { + public: + SimpleWebSrvClient () {}; + ~SimpleWebSrvClient () {}; +}; + + + +int main (int argc, char **argv) { + list webclients; + + TCP http; + TCP https; + + std::string ssl_cert = DEFAULT_CERT; + std::string ssl_key = DEFAULT_KEY; + + 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); + + 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); + + 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; + } + + 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++) { + webclient = *wci; + if (webclient->Loop() == 0) { + // error on loop, remove and delete webclient. + printf ("remove connection\n"); + webclients.remove(webclient); + delete webclient; + wci = webclients.begin(); + if (wci == webclients.end()) continue; + } + } + + usleep (1000); + } +}; + diff --git a/webserver.cc b/webserver.cc new file mode 100644 index 0000000..d02b94a --- /dev/null +++ b/webserver.cc @@ -0,0 +1,74 @@ + +#include "UDPTCPNetwork.h" + +WebServerClient::WebServerClient () { + tcp = NULL; + ssl = NULL; + ssl_key = ""; + ssl_cert = ""; +}; + + +WebServerClient::~WebServerClient () { + if (ssl) delete ssl; + ssl = NULL; + if (tcp) delete tcp; + tcp = NULL; +}; + + +/// @brief +/// @param socket +/// @param _ssl_key +/// @param _ssl_cert +/// @return +int WebServerClient::Accept (TCP *socket, std::string _ssl_key, std::string _ssl_cert) { + if (ssl) delete ssl; + ssl = NULL; + if (tcp) delete tcp; + tcp = NULL; + + tcp = socket; + + ssl_key = _ssl_key; + ssl_cert = _ssl_cert; + ssl = new SSLSocket; + ssl->SetCertificat(ssl_cert, ssl_key); + ssl->Accept(tcp->GetSocket(),200); + + return 1; +}; + + +/// @brief +/// @param socket +/// @return +int WebServerClient::Accept (TCP *socket) { + if (ssl) delete ssl; + ssl = NULL; + if (tcp) delete tcp; + tcp = NULL; + + tcp = socket; + + return 1; +}; + + +int WebServerClient::Loop () { + int len; + + // + // read data + if (ssl) { + len = ssl->Read(buffer, WEB_READ_BUFFER_SIZE); + } + else { + len = tcp->ReadTimeout(buffer, WEB_READ_BUFFER_SIZE, 20); + } + if (len < 0) return 0; + if (len > 0) printf ("%s\n", buffer); + + return 0; +}; + diff --git a/webutils.cc b/webutils.cc new file mode 100644 index 0000000..ee4edcc --- /dev/null +++ b/webutils.cc @@ -0,0 +1,209 @@ + +#include "UDPTCPNetwork.h" + +#include +#include +#include +#include +#include +#include + + +WebRequestBuffer::WebRequestBuffer () { + buffer_pos_data = -1; + buffer_size = 0; + buffer_ptr = NULL; + buffer_len = 0; +} + + +WebRequestBuffer::~WebRequestBuffer () { + if (buffer_ptr) { + free (buffer_ptr); + buffer_ptr = NULL; + buffer_size = 0; + buffer_len = 0; + buffer_pos_data = -1; + } +} + + +/// @brief add text to buffer +/// @param buffer +/// @param len +/// @return 0 on error, 1 on success +int WebRequestBuffer::AddBuffer (char *buffer, int len) { + // + // if needed allocate enought buffer, to the next kB + if (buffer_ptr == NULL || buffer_size <= buffer_len+len) { + buffer_size = 1024 * (1 + ((buffer_len + len) / 1024)); + if (buffer_ptr == NULL) buffer_ptr = (char*) malloc (buffer_size); + else buffer_ptr = (char*) realloc (buffer_ptr, buffer_size); + } + + if (buffer_ptr == NULL || buffer_size < buffer_len+len) { + fprintf (stderr, "%s:%d could not allocate enought memory. buffer_size:%d buffer_len+len:%d \n", + __FILE__, __LINE__, buffer_size, buffer_len + len); + return 0; + } + + memcpy (buffer_ptr + buffer_len, buffer, len); + buffer_len += len; + buffer_ptr[buffer_len] = 0; + + debug("buffer:'%s'\n", buffer_ptr); + + return 1; +}; + + +/// @brief copy the curren buffer to the dstination, resize destination if needed +/// @param dstptr pointer to the destination pointer, in case of a reallocation +/// @param max pointer to current allocated size of the pointer +/// @return > 0 - len in bytes of the buffer, = -1 error occured +int WebRequestBuffer::GetBuffer (char **dstptr, int *max) { + // check: valid pointers + if (dstptr == NULL || max == NULL) { + fprintf (stderr, "%s:%d not pointer to buffer or not pointer to a lenght set\n", + __FILE__, __LINE__); + return -1; + } + + // check: size and reallocate + if (*max < buffer_len || *max < 0) { + char *tb = (char*) realloc (*dstptr, buffer_len); + *dstptr = tb; + *max = buffer_size; + } + + // check: could alloc memory + if (*dstptr == NULL) { + fprintf (stderr, "%s:%d no destination pointer set\n", + __FILE__, __LINE__); + return -1; + } + + memcpy (*dstptr, buffer_ptr, buffer_len); + return buffer_len; +}; + + +/// @brief Check if the Request is Complete. In this case the request_type and the request can +/// @brief be read with GetRequest and GetType(). For a new request Clear() must be called. +/// @return 0 . not complete, 1 . request complete +bool WebRequestBuffer::RequestComplete() { + int i = 0; + int element = 0; // 0. Parameter 1. Value 2. Value n + int line = 0; + WebHeaderLine hl; + + request = ""; + protocol_version = "HTTP/1.0"; + request_type = WEB_REQEST_UNSUPPORTED; + buffer_pos_data = -1; + + if (buffer_ptr == NULL) return false; + + hl.Clear(); + for (i = 0, element = 0; i < buffer_len; i++) { + // + // check if line is complete + if (buffer_ptr[i] == '\n') { + if (hl.value.length() == 0 && hl.parameter.length() == 0) { + buffer_pos_data = i+1; // save position of first data byte + return true; + } + + // the first line holds the request type and the location/path. + if (line == 0) { + hl.parameter = string_to_upper(hl.parameter); + if (hl.parameter.compare("GET") == 0) request_type = WEB_REQEST_GET; + if (hl.parameter.compare("POST") == 0) request_type = WEB_REQEST_POST; + request = string_to_lower(hl.value); + } + + headerlines.push_back(hl); + hl.Clear(); + element = 0; + line++; + continue; + } + + // ignore some elements -> maybe it is not needed + else if (strchr ("\t ", buffer_ptr[i]) != NULL) { + element++; + if (element > 1) hl.value += buffer_ptr[i]; + } + + // ignore last CR + else if (buffer_ptr[i] != '\r') { + if (element == 0) hl.parameter += buffer_ptr[i]; + // first line ignore protocol version + else if (line > 0 || element == 1) hl.value += buffer_ptr[i]; + } + } + + return false; +} + + +/// @brief get some information about the complete request +/// @param cmd propably get or set command +/// @param obj object related with this command +/// @param jp some json data +/// @return +int WebRequestBuffer::GetRequestCmdObj(std::string *cmd, std::string *obj, JSONParse *jp) { + std::string rq = GetRequest(); + int secslash = 0; + + if (cmd == NULL || obj == NULL || jp == NULL) + errorexit ("Fatal Error cmd:%p obj:%p jp:%p none of these should be NULL\n", + cmd, obj, jp); + + secslash = rq.find ('/', 1); + *cmd = rq.substr(1, secslash-1); + *obj = rq.substr(secslash+1, std::string::npos); + if (buffer_pos_data > 0) (*jp).Set(buffer_ptr+buffer_pos_data); + else (*jp).Clear(); + + return 1; +} + + +std::string WebRequestBuffer::GetRequest() { + std::string rq = ""; + char lastc = 0; + char c = 0; + + // + // filter out /. .. ./ + for (int i = 0; i < request.length(); i++) { + c = request[i]; + if (((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || c == '/' || c == '.' || c == '-' ) && + (!((c == '.' || c == '/' ) && (lastc == '/' || lastc == '.')))) { + rq += c; + lastc = c; + } + } + + return rq; +} + + +std::string WebRequestBuffer::GetCookie(std::string name) { + std::list::iterator il; + std::size_t i; + + for (il = headerlines.begin(); il != headerlines.end(); il++) { + if (string_to_lower(il->parameter).compare("cookie:") == 0) { + i = il->value.find('='); + if (i == std::string::npos) continue; + if (il->value.substr(0, i).compare(name) == 0) + return il->value.substr(i+1, std::string::npos); + } + } + return ""; +}; + +