diff --git a/UDPTCPNetwork-webutils.h b/UDPTCPNetwork-webutils.h index 8e4c176..086c4e4 100644 --- a/UDPTCPNetwork-webutils.h +++ b/UDPTCPNetwork-webutils.h @@ -10,10 +10,10 @@ #define WEB_READ_BUFFER_SIZE 0x10000 enum { // request types - WEB_REQEST_UNSUPPORTED, - WEB_REQEST_GET, - WEB_REQEST_POST, - WEB_REQEST_MAX // not set + WEB_REQUEST_UNSUPPORTED, + WEB_REQUEST_GET, + WEB_REQUEST_POST, + WEB_REQUEST_MAX // not set }; /*************************************************************************************** @@ -70,16 +70,32 @@ class WebServerClient { TCP *tcp; SSLSocket *ssl; // only set if SSL is used char buffer[WEB_READ_BUFFER_SIZE]; + std::string DocumentRoot; + + protected: + WebRequestBuffer ReqBuffer; + public: WebServerClient(); ~WebServerClient(); + void SetDecoumentRoot(std::string path) { DocumentRoot = path; }; + // 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); + std::string CreateHeader(WebRequestBuffer *request, int errcode, + std::string doc_type, int doc_size, std::string addheader); + int SendResponseData (WebRequestBuffer *request, std::string data, + std::string addheader); + std::string GetFileType (std::string fname); + std::string Get_WebFile_FullPath(std::string file); + + int SendResponseFile (WebRequestBuffer *request, std::string fname, std::string addheader); virtual int HandleRequest (WebRequestBuffer *req) { return 0; }; virtual int Loop(); + virtual int HandleRequest (); }; #endif diff --git a/test-webserver.cc b/test-webserver.cc index 68c10a9..4b2d073 100644 --- a/test-webserver.cc +++ b/test-webserver.cc @@ -72,7 +72,7 @@ int main (int argc, char **argv) { delete tcpclient; continue; } - + webclient->SetDecoumentRoot("./www"); webclients.push_back(webclient); printf ("add new connection to client list\n"); } @@ -80,8 +80,11 @@ int main (int argc, char **argv) { // // go through all the clients and check for new requests for (wci = webclients.begin(); wci != webclients.end(); wci++) { + int res = 0; webclient = *wci; - if (webclient->Loop() == 0) { + + res = webclient->Loop(); + if (res == -1) { // error on loop, remove and delete webclient. printf ("remove connection\n"); webclients.remove(webclient); diff --git a/webserver.cc b/webserver.cc index d02b94a..459de6d 100644 --- a/webserver.cc +++ b/webserver.cc @@ -1,5 +1,11 @@ #include "UDPTCPNetwork.h" +#include +#include +#include +#include +#include +#include WebServerClient::WebServerClient () { tcp = NULL; @@ -55,20 +61,244 @@ 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 () { 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); + if (len < 0) return -1; + if (len == 0) return 0; + 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; +}; + + +#define TIME_TEXT_SIZE 128 +std::string WebServerClient::CreateHeader(WebRequestBuffer *request, int errcode, + std::string doc_type, int doc_size, std::string addheader) { + long int l; + long bytes; + std::string errortext; + std::string response; + char timebuf[TIME_TEXT_SIZE]; + struct tm tm; + time_t t = time(NULL); + + // debug ("errcode:%d cont_type:%s doc_size:%d\n", errcode, doc_type.c_str(), doc_size); + + // + // read the current time int the timebuf + if (localtime_r (&t, &tm) != &tm) { + fprintf (stderr, "%s:%d %s could not get localtime. Error:%s\n", + __FILE__, __LINE__, __FUNCTION__, strerror(errno)); + return ""; + } + if (asctime_r (&tm, timebuf) != timebuf) { + fprintf (stderr, "%s:%d %s could not get asctime. Error:%s\n", + __FILE__, __LINE__, __FUNCTION__, strerror(errno)); + return ""; + } + timebuf[strlen(timebuf)-3] = 0; + + switch (errcode) { + case (200): + errortext = "200 OK"; + break; + default: + errortext = "501 NOT IMPLEMENTED"; + break; + } + + response = request->GetProtocolVersion() + " " + errortext + "\r\n"; + response += "Accept-Ranges: bytes\r\n"; + response += "Content-Type: " + doc_type + "\r\n"; + response += "Content-Length: " + std::to_string(doc_size) + "\r\n"; + response += "Date: " + (std::string) timebuf + "\r\n"; + response += "Server: SyslogCollect \r\n"; + if (addheader.length() > 0) + response += addheader; + + response += "\r\n"; + return response; +} + + +/// @brief send some data as response +/// @param request the orginal request this response is for +/// @param data json type data to send +/// @param addheader add header lines if set +/// @return 0 on error, 1 on success +int WebServerClient::SendResponseData (WebRequestBuffer *request, std::string data, std::string addheader) { + char *outbuffer = NULL; + int outbufferpos = 0; + int error = 200; + long int l; + std::string datatype = "application/x-www-form-urlencoded; charset=UTF-8"; + std::string header; + ssize_t databytes; + + // + // send header and file + header = CreateHeader (request, error, datatype, data.length(), addheader); + outbufferpos = header.length(); + outbuffer = (char*) malloc (outbufferpos + data.length() + 16); + memcpy (outbuffer, header.c_str(), outbufferpos); + memcpy (outbuffer + outbufferpos, data.c_str(), data.length()); + databytes = outbufferpos + data.length(); + outbuffer[databytes] = 0; + + if (ssl) l = ssl->Write(outbuffer, databytes); + else l = tcp->Write(outbuffer, databytes); + + if (l != databytes) { + debug ("could not send all data. Error:%s\n", strerror(errno)); + free (outbuffer); + return 0; + } + + free (outbuffer); + return 1; +} + + +std::string WebServerClient::GetFileType (std::string fname) { + std::string res = "text/html;charset=utf-8"; + std::size_t pos = fname.find_last_of('.', std::string::npos); + if (pos == std::string::npos) return res; + + std::string suffix = string_to_lower (fname.substr(pos)); + + if (suffix.compare (".html") == 0) return res; + if (suffix.compare (".htm") == 0) return res; + if (suffix.compare (".js") == 0) return res; + if (suffix.compare (".json") == 0) return "application/json; charset=utf-8"; + if (suffix.compare (".svg") == 0) return "image/svg+xml"; + if (suffix.compare (".png") == 0) return "image/png"; + if (suffix.compare (".jpg") == 0) return "image/jpeg"; + if (suffix.compare (".jpeg") == 0) return "image/jpeg"; + + return res; +} + + + +/// @brief send a file as response +/// @param request requested file +/// @param fname file relative to the DOCUMENT_ROOT +/// @param addheader add header if set -> must contain "\n\r" at each line +/// @return 0 on error, 1 on success +int WebServerClient::SendResponseFile (WebRequestBuffer *request, std::string fname, std::string addheader) { + char *outbuffer = NULL; + int outbufferpos = 0; + int error = 200; + long int l; + std::string fullfname = Get_WebFile_FullPath(fname); + std::string filetype = "text/html;charset=utf-8"; + std::string header; + struct stat filestat; + int filefd; + ssize_t filereadbytes; + + // + // open file and determine file type and size + filetype = GetFileType(fname); + filefd = open(fullfname.c_str(), O_RDONLY); + if (filefd < 0) { + debug ("could not open file '%s' for reading. Error:%s\n", + fullfname.c_str(), strerror(errno)); + // + // set errorcode and reopen error file + error = 404; + fullfname = Get_WebFile_FullPath("error404.html"); + filefd = open(fullfname.c_str(), O_RDONLY); + if (filefd < 0) errorexit ("could not open file '%s' for reading. Error:%s\n", + fullfname.c_str(), strerror(errno)); + } + + if (fstat (filefd, &filestat) != 0) { + debug ("could not get stats on file '%s'. Error:%s\n", + fullfname.c_str(), strerror(errno)); + error = 500; + fullfname = Get_WebFile_FullPath("error500.html"); + filefd = open(fullfname.c_str(), O_RDONLY); + if (filefd < 0) + errorexit ("could not open file '%s' for reading. Error:%s\n", + fullfname.c_str(), strerror(errno)); + if (fstat (filefd, &filestat) != 0) + errorexit ("could not get stats on file '%s'. Error:%s\n", + fullfname.c_str(), strerror(errno)); + } + + // + // send header and file + header = CreateHeader (request, error, filetype, filestat.st_size, addheader); + outbufferpos = header.length(); + outbuffer = (char*) malloc (outbufferpos + filestat.st_size + 16); + memcpy (outbuffer, header.c_str(), outbufferpos); + + filereadbytes = read (filefd, outbuffer+outbufferpos, filestat.st_size); + close (filefd); + if (filereadbytes <= 0) { + debug ("could not read all data from file '%s'. Error:%s\n", + fullfname.c_str(), strerror(errno)); + free (outbuffer); + return 0; + } + filereadbytes += outbufferpos; + + if (ssl) l = ssl->Write(outbuffer, filereadbytes); + else l = tcp->Write(outbuffer, filereadbytes); + // debug ("send file %s with %ld bytes\n", fname.c_str(), filereadbytes); + + if (l != filereadbytes) { + debug ("could not send all data. Error:%s\n", strerror(errno)); + free (outbuffer); + return 0; + } + + free (outbuffer); + return 1; +} + + +std::string WebServerClient::Get_WebFile_FullPath(std::string file) { + std::string ret = file; + + if (DocumentRoot.length() > 0) { + if (DocumentRoot[DocumentRoot.length()-1] == '/') { + if (file[0] == '/') ret = DocumentRoot + file.substr(1, std::string::npos); + else ret = DocumentRoot + file; + } + else { + if (file[0] == '/') ret = DocumentRoot + file; + else ret = DocumentRoot + "/" + file; + } + } + + return ret; +}; diff --git a/webutils.cc b/webutils.cc index ee4edcc..85b9d1d 100644 --- a/webutils.cc +++ b/webutils.cc @@ -51,13 +51,13 @@ int WebRequestBuffer::AddBuffer (char *buffer, int len) { buffer_len += len; buffer_ptr[buffer_len] = 0; - debug("buffer:'%s'\n", buffer_ptr); + // debug("buffer:'%s'\n", buffer_ptr); return 1; }; -/// @brief copy the curren buffer to the dstination, resize destination if needed +/// @brief copy the current buffer to the destination, 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 @@ -99,7 +99,7 @@ bool WebRequestBuffer::RequestComplete() { request = ""; protocol_version = "HTTP/1.0"; - request_type = WEB_REQEST_UNSUPPORTED; + request_type = WEB_REQUEST_UNSUPPORTED; buffer_pos_data = -1; if (buffer_ptr == NULL) return false; @@ -117,8 +117,8 @@ bool WebRequestBuffer::RequestComplete() { // 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; + if (hl.parameter.compare("GET") == 0) request_type = WEB_REQUEST_GET; + if (hl.parameter.compare("POST") == 0) request_type = WEB_REQUEST_POST; request = string_to_lower(hl.value); } diff --git a/www/index.html b/www/index.html new file mode 100644 index 0000000..2cadd00 --- /dev/null +++ b/www/index.html @@ -0,0 +1,13 @@ + + + TestWebserver + + +

Testpage

+

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

+ + + +