You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
305 lines
8.8 KiB
305 lines
8.8 KiB
|
|
#include "UDPTCPNetwork.h"
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <list>
|
|
|
|
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;
|
|
};
|
|
|
|
|
|
|
|
/// @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;
|
|
|
|
if (ssl) {
|
|
len = ssl->Read(buffer, WEB_READ_BUFFER_SIZE);
|
|
}
|
|
else {
|
|
len = tcp->ReadTimeout(buffer, WEB_READ_BUFFER_SIZE, 20);
|
|
}
|
|
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;
|
|
};
|