mirror of
https://github.com/tuxbox-neutrino/neutrino.git
synced 2025-08-31 17:31:20 +02:00
our current experimental Neutrino branch
git-svn-id: file:///home/bas/coolstream_public_svn/THIRDPARTY/applications/neutrino-experimental@27 e54a6e83-5905-42d5-8d5c-058d10e6a962
This commit is contained in:
4
src/nhttpd/yhttpd_core/AUTHORS
Normal file
4
src/nhttpd/yhttpd_core/AUTHORS
Normal file
@@ -0,0 +1,4 @@
|
||||
The code-base of yhttpd is Tuxbox-nhttpd 2.x (cvs.tuxbox.org)
|
||||
|
||||
Authors since re-writting of yhttpd:
|
||||
Johannes Golombek [yjogol@cvs.tuxbox.org]
|
18
src/nhttpd/yhttpd_core/Makefile.am
Normal file
18
src/nhttpd/yhttpd_core/Makefile.am
Normal file
@@ -0,0 +1,18 @@
|
||||
INCLUDES = \
|
||||
-I$(top_srcdir)/lib \
|
||||
-I$(top_srcdir)/src/zapit/include \
|
||||
-I$(top_srcdir)/src \
|
||||
-I$(top_srcdir)/src/nhttpd \
|
||||
-I$(top_srcdir)/lib/connection \
|
||||
-I$(top_srcdir)/lib/libeventserver \
|
||||
-I$(top_srcdir)/lib/libconfigfile \
|
||||
@FREETYPE_CFLAGS@
|
||||
|
||||
AM_CPPFLAGS = -fno-rtti -fno-exceptions
|
||||
|
||||
noinst_LIBRARIES = libyhttpd.a
|
||||
|
||||
libyhttpd_a_SOURCES = \
|
||||
ylogging.cpp helper.cpp \
|
||||
ywebserver.cpp yconnection.cpp yrequest.cpp yresponse.cpp yhook.cpp ysocket.cpp
|
||||
|
22
src/nhttpd/yhttpd_core/README
Normal file
22
src/nhttpd/yhttpd_core/README
Normal file
@@ -0,0 +1,22 @@
|
||||
------------------------------------------------------------------------------
|
||||
yhttpd-GOALS
|
||||
(yjogol@online.de, August 2006)
|
||||
------------------------------------------------------------------------------
|
||||
The yhttpd-Webserver is a small C++ based Webserver for embedded Systems.
|
||||
It has a simple Software-Architecture and uses for string-operations
|
||||
mostly buffer-save functions to prevent security exploits like buffer overruns.
|
||||
It is designed for threading and multiuser access for static files.
|
||||
It can be compiled on Windows Systems using Cygwin, on Linux-Systems
|
||||
and had been succesfully compiled for AVM Fritzbox or for
|
||||
Maxtor Shared Storage.
|
||||
|
||||
The code-base of yhttpd is Tuxbox-nhttpd 2.x (cvs.tuxbox.org)
|
||||
|
||||
Starting with Version 3.0 the nhttpd source code was splitted into
|
||||
a core Webserver (yhttpd_core) and extension modules (yhttpd_mods).
|
||||
|
||||
The yhttpd_core and yhttp_mods ware completly rewritten.
|
||||
|
||||
The Hook-System (extension modules) encapsulate all requests and responses
|
||||
for the tuxboxapi. So both tuxapi and yhttpd can be developed seperatlly and
|
||||
independ from each other.
|
280
src/nhttpd/yhttpd_core/helper.cpp
Normal file
280
src/nhttpd/yhttpd_core/helper.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Helper
|
||||
//=============================================================================
|
||||
|
||||
// c
|
||||
#include <cstdio> // printf prototype.
|
||||
#include <cstdlib> // calloc and free prototypes.
|
||||
#include <cstring> // str* and memset prototypes.
|
||||
#include <cstdarg>
|
||||
|
||||
// yhttpd
|
||||
#include "yconfig.h"
|
||||
#include "ytypes_globals.h"
|
||||
#include "helper.h"
|
||||
#include "ylogging.h"
|
||||
|
||||
//=============================================================================
|
||||
// Integers
|
||||
//=============================================================================
|
||||
//-------------------------------------------------------------------------
|
||||
// Check and set integer inside boundaries (min, max)
|
||||
//-------------------------------------------------------------------------
|
||||
int minmax(int value,int min, int max)
|
||||
{
|
||||
if(value < min) return min;
|
||||
if(value > max) return max;
|
||||
return value;
|
||||
}
|
||||
//=============================================================================
|
||||
// Date & Time
|
||||
//=============================================================================
|
||||
//-------------------------------------------------------------------------
|
||||
// Check and set Date/Time (tm*) inside boundaries
|
||||
//-------------------------------------------------------------------------
|
||||
void correctTime(struct tm *zt)
|
||||
{
|
||||
|
||||
zt->tm_year = minmax(zt->tm_year,0,129);
|
||||
zt->tm_mon = minmax(zt->tm_mon,0,11);
|
||||
zt->tm_mday = minmax(zt->tm_mday,1,31); //-> eine etwas laxe pruefung, aber mktime biegt das wieder grade
|
||||
zt->tm_hour = minmax(zt->tm_hour,0,23);
|
||||
zt->tm_min = minmax(zt->tm_min,0,59);
|
||||
zt->tm_sec = minmax(zt->tm_sec,0,59);
|
||||
}
|
||||
//=============================================================================
|
||||
// Strings
|
||||
//=============================================================================
|
||||
//-------------------------------------------------------------------------
|
||||
// Integer to Hexadecimal-String
|
||||
//-------------------------------------------------------------------------
|
||||
std::string itoh(unsigned int conv)
|
||||
{
|
||||
return string_printf("0x%06x",conv);
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// Integer to String
|
||||
//-------------------------------------------------------------------------
|
||||
std::string itoa(unsigned int conv)
|
||||
{
|
||||
return string_printf("%u",conv);
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// convert timer_t to "<hour>:<minutes>" String
|
||||
//-------------------------------------------------------------------------
|
||||
std::string timeString(time_t time)
|
||||
{
|
||||
char tmp[7]={'\0'};
|
||||
struct tm *tm = localtime(&time);
|
||||
if (strftime(tmp, 6, "%H:%M", tm))
|
||||
return std::string(tmp);
|
||||
else
|
||||
return std::string("??:??");
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// Printf and return formatet String. Buffer-save!
|
||||
// max length up to bufferlen -> then snip
|
||||
//-------------------------------------------------------------------------
|
||||
#define bufferlen 4*1024
|
||||
std::string string_printf(const char *fmt, ...)
|
||||
{
|
||||
char buffer[bufferlen];
|
||||
va_list arglist;
|
||||
va_start( arglist, fmt );
|
||||
if(arglist)
|
||||
vsnprintf( buffer, bufferlen, fmt, arglist );
|
||||
va_end(arglist);
|
||||
return std::string(buffer);
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// ySplitString: spit string "str" in two strings "left" and "right" at
|
||||
// one of the chars in "delimiter" returns true if delimiter found
|
||||
//-------------------------------------------------------------------------
|
||||
bool ySplitString(std::string str, std::string delimiter, std::string& left, std::string& right)
|
||||
{
|
||||
unsigned int pos;
|
||||
if ((pos = str.find_first_of(delimiter)) != std::string::npos)
|
||||
{
|
||||
left = str.substr(0, pos);
|
||||
right = str.substr(pos + 1, str.length() - (pos + 1 ));
|
||||
}
|
||||
else
|
||||
{
|
||||
left = str; //default if not found
|
||||
right = "";
|
||||
}
|
||||
return (pos != std::string::npos);
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// ySplitString: spit string "str" in two strings "left" and "right" at
|
||||
// one of the chars in "delimiter" returns true if delimiter found
|
||||
//-------------------------------------------------------------------------
|
||||
bool ySplitStringExact(std::string str, std::string delimiter, std::string& left, std::string& right)
|
||||
{
|
||||
unsigned int pos;
|
||||
if ((pos = str.find(delimiter)) != std::string::npos)
|
||||
{
|
||||
left = str.substr(0, pos);
|
||||
right = str.substr(pos + delimiter.length(), str.length() - (pos + delimiter.length() ));
|
||||
}
|
||||
else
|
||||
{
|
||||
left = str; //default if not found
|
||||
right = "";
|
||||
}
|
||||
return (pos != std::string::npos);
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// ySplitStringRight: spit string "str" in two strings "left" and "right" at
|
||||
// one of the chars in "delimiter" returns true if delimiter found
|
||||
//-------------------------------------------------------------------------
|
||||
bool ySplitStringLast(std::string str, std::string delimiter, std::string& left, std::string& right)
|
||||
{
|
||||
unsigned int pos;
|
||||
if ((pos = str.find_last_of(delimiter)) != std::string::npos)
|
||||
{
|
||||
left = str.substr(0, pos);
|
||||
right = str.substr(pos + 1, str.length() - (pos + 1 ));
|
||||
}
|
||||
else
|
||||
{
|
||||
left = str; //default if not found
|
||||
right = "";
|
||||
}
|
||||
return (pos != std::string::npos);
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// ySplitStringVector: spit string "str" and build vector of strings
|
||||
//-------------------------------------------------------------------------
|
||||
CStringArray ySplitStringVector(std::string str, std::string delimiter)
|
||||
{
|
||||
std::string left, right, rest;
|
||||
bool found;
|
||||
CStringArray split;
|
||||
rest = str;
|
||||
do
|
||||
{
|
||||
found = ySplitString(rest, delimiter, left, right);
|
||||
split.push_back(left);
|
||||
rest = right;
|
||||
}
|
||||
while(found);
|
||||
return split;
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// trim whitespaces
|
||||
//-------------------------------------------------------------------------
|
||||
std::string trim(std::string const& source, char const* delims)
|
||||
{
|
||||
std::string result(source);
|
||||
std::string::size_type index = result.find_last_not_of(delims);
|
||||
if(index != std::string::npos)
|
||||
result.erase(++index);
|
||||
|
||||
index = result.find_first_not_of(delims);
|
||||
if(index != std::string::npos)
|
||||
result.erase(0, index);
|
||||
else
|
||||
result.erase();
|
||||
return result;
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// equal-function for case insensitive compare
|
||||
//-------------------------------------------------------------------------
|
||||
bool nocase_compare (char c1, char c2)
|
||||
{
|
||||
return toupper(c1) == toupper(c2);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Decode URLEncoded std::string
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string decodeString(std::string encodedString)
|
||||
{
|
||||
const char *string = encodedString.c_str();
|
||||
unsigned int count=0;
|
||||
char hex[3]={'\0'};
|
||||
unsigned long iStr;
|
||||
std::string result = "";
|
||||
count = 0;
|
||||
|
||||
while(count<encodedString.length()) /* use the null character as a loop terminator */
|
||||
{
|
||||
if(string[count] == '%' && count+2 <encodedString.length())
|
||||
{
|
||||
hex[0]=string[count+1];
|
||||
hex[1]=string[count+2];
|
||||
hex[2]='\0';
|
||||
iStr = strtoul(hex,NULL,16); /* convert to Hex char */
|
||||
result += (char)iStr;
|
||||
count += 3;
|
||||
}
|
||||
else if(string[count] == '+')
|
||||
{
|
||||
result += ' ';
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += string[count];
|
||||
count++;
|
||||
}
|
||||
} /* end of while loop */
|
||||
return result;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Encode URLEncoded std::string
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string encodeString(std::string decodedString)
|
||||
{
|
||||
unsigned int len = sizeof(char) * decodedString.length()*5 + 1;
|
||||
std::string result( len, '\0' );
|
||||
char *newString = (char *)result.c_str();
|
||||
char *dstring = (char *)decodedString.c_str();
|
||||
char one_char;
|
||||
if(len == result.length()) // got memory needed
|
||||
{
|
||||
while((one_char = *dstring++)) /* use the null character as a loop terminator */
|
||||
{
|
||||
if(isalnum(one_char))
|
||||
*newString++ = one_char;
|
||||
else
|
||||
newString += sprintf(newString, "&#%d;", (unsigned char) one_char);
|
||||
}
|
||||
|
||||
*newString='\0'; /* when done copying the string,need to terminate w/ null char */
|
||||
result.resize((unsigned int)(newString - result.c_str()), '\0');
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// returns string in lower case
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string string_tolower(std::string str)
|
||||
{
|
||||
for(unsigned int i = 0; i < str.length(); i++)
|
||||
str[i] = tolower(str[i]);
|
||||
return str;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// write string to a file
|
||||
//-----------------------------------------------------------------------------
|
||||
bool write_to_file(std::string filename, std::string content)
|
||||
{
|
||||
FILE *fd = NULL;
|
||||
if((fd = fopen(filename.c_str(),"w")) != NULL) // open file
|
||||
{
|
||||
fwrite(content.c_str(), content.length(), 1, fd);
|
||||
fflush(fd); // flush and close file
|
||||
fclose(fd);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
42
src/nhttpd/yhttpd_core/helper.h
Normal file
42
src/nhttpd/yhttpd_core/helper.h
Normal file
@@ -0,0 +1,42 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Helper
|
||||
//=============================================================================
|
||||
|
||||
#ifndef __yhttpd_helper_h__
|
||||
#define __yhttpd_helper_h__
|
||||
// c
|
||||
#include <ctime>
|
||||
#include <sys/timeb.h>
|
||||
// c++
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "ytypes_globals.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
int minmax(int value,int min, int max);
|
||||
void correctTime(struct tm *zt);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// String Converter
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string itoa(unsigned int conv);
|
||||
std::string itoh(unsigned int conv);
|
||||
std::string decodeString(std::string encodedString);
|
||||
std::string encodeString(std::string decodedString);
|
||||
std::string string_tolower(std::string str);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// String Helpers
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string trim(std::string const& source, char const* delims = " \t\r\n");
|
||||
std::string string_printf(const char *fmt, ...);
|
||||
bool ySplitString(std::string str, std::string delimiter, std::string& left, std::string& right);
|
||||
bool ySplitStringExact(std::string str, std::string delimiter, std::string& left, std::string& right);
|
||||
bool ySplitStringLast(std::string str, std::string delimiter, std::string& left, std::string& right);
|
||||
CStringArray ySplitStringVector(std::string str, std::string delimiter);
|
||||
bool nocase_compare (char c1, char c2);
|
||||
std::string timeString(time_t time);
|
||||
bool write_to_file(std::string filename, std::string content);
|
||||
|
||||
#endif /* __yhttpd_helper_h__ */
|
147
src/nhttpd/yhttpd_core/yconnection.cpp
Normal file
147
src/nhttpd/yhttpd_core/yconnection.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Connection
|
||||
//=============================================================================
|
||||
|
||||
// system
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <cstring>
|
||||
// yhttpd
|
||||
#include "yconfig.h"
|
||||
#include "ytypes_globals.h"
|
||||
#include "ywebserver.h"
|
||||
#include "yconnection.h"
|
||||
#include "helper.h"
|
||||
//=============================================================================
|
||||
// Initialization of static variables
|
||||
//=============================================================================
|
||||
long CWebserverConnection::GConnectionNumber = 0;
|
||||
|
||||
//=============================================================================
|
||||
// Constructor & Destructor & Initialization
|
||||
//=============================================================================
|
||||
CWebserverConnection::CWebserverConnection(CWebserver *pWebserver)
|
||||
{
|
||||
Webserver = pWebserver;
|
||||
Request.Webserver = pWebserver;
|
||||
Request.Connection = this;
|
||||
Response.Webserver = pWebserver;
|
||||
Response.Connection = this;
|
||||
Method = M_UNKNOWN;
|
||||
HttpStatus = 0;
|
||||
RequestCanceled = false;
|
||||
#ifdef Y_CONFIG_FEATURE_KEEP_ALIVE
|
||||
keep_alive = true;
|
||||
#else
|
||||
keep_alive = false;
|
||||
#endif
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
CWebserverConnection::CWebserverConnection()
|
||||
{
|
||||
// aprintf("test CWebserverConnection::CWebserverConnection()\n");
|
||||
ConnectionNumber = ++GConnectionNumber;
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
CWebserverConnection::~CWebserverConnection(void)
|
||||
{
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// End The Connection. Request and Response allready handled.
|
||||
// do "after done" work, like create a www-Log entry.
|
||||
// Use "Hooks_EndConnection()" Handler to write own Hooks.
|
||||
//-------------------------------------------------------------------------
|
||||
void CWebserverConnection::EndConnection()
|
||||
{
|
||||
HookHandler.HookVarList["enlapsed_request"] = itoa(enlapsed_request/1000);
|
||||
HookHandler.HookVarList["enlapsed_response"] = itoa(enlapsed_response/1000);
|
||||
HookHandler.Hooks_EndConnection(); // Handle Hooks
|
||||
if(RequestCanceled) // Canceled
|
||||
keep_alive = false;
|
||||
RequestCanceled = true;
|
||||
// sock->Flush();
|
||||
#ifndef Y_CONFIG_FEATURE_KEEP_ALIVE
|
||||
sock->close();
|
||||
#endif
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// Main
|
||||
// Handle the Request, Handle (Send) Response), End the Connection
|
||||
//-------------------------------------------------------------------------
|
||||
void CWebserverConnection::HandleConnection()
|
||||
{
|
||||
gettimeofday(&tv_connection_start, &tz_connection_start);
|
||||
|
||||
// get the request
|
||||
if (Request.HandleRequest())
|
||||
{
|
||||
// determine time from Connection creation until now
|
||||
gettimeofday(&tv_connection_Response_start, &tz_connection_Response_start);
|
||||
enlapsed_request = ((tv_connection_Response_start.tv_sec - tv_connection_start.tv_sec) * 1000000
|
||||
+ (tv_connection_Response_start.tv_usec - tv_connection_start.tv_usec));
|
||||
|
||||
// Keep-Alive checking
|
||||
#ifdef Y_CONFIG_FEATURE_KEEP_ALIVE
|
||||
if(string_tolower(Request.HeaderList["Connection"]) == "close"
|
||||
|| (httprotocol != "HTTP/1.1" && string_tolower(Request.HeaderList["Connection"]) != "keep-alive")
|
||||
|| !Webserver->CheckKeepAliveAllowedByIP(sock->get_client_ip()))
|
||||
keep_alive = false;
|
||||
#else
|
||||
keep_alive = false;
|
||||
#endif
|
||||
// Send a response
|
||||
Response.SendResponse();
|
||||
|
||||
// determine time for SendResponse
|
||||
gettimeofday(&tv_connection_Response_end, &tz_connection_Response_end);
|
||||
enlapsed_response = ((tv_connection_Response_end.tv_sec - tv_connection_Response_start.tv_sec) * 1000000
|
||||
+ (tv_connection_Response_end.tv_usec - tv_connection_Response_start.tv_usec));
|
||||
|
||||
// print production times
|
||||
log_level_printf(1,"enlapsed time request:%ld response:%ld url:%s\n",
|
||||
enlapsed_request, enlapsed_response, (Request.UrlData["fullurl"]).c_str());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
RequestCanceled = true;
|
||||
keep_alive = false; // close this connection socket
|
||||
// dperror("Error while parsing request\n");
|
||||
log_level_printf(1,"request canceled: %s\n", strerror(errno));
|
||||
}
|
||||
EndConnection();
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
void CWebserverConnection::ShowEnlapsedRequest(char *text)
|
||||
{
|
||||
|
||||
long enlapsed = GetEnlapsedRequestTime() / 1000;
|
||||
log_level_printf(1,"enlapsed-f-start (%s) t:%ld url:%s\n",
|
||||
text, enlapsed, (Request.UrlData["fullurl"]).c_str());
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// Time from creation of socket until now in microseconds!
|
||||
//-------------------------------------------------------------------------
|
||||
long CWebserverConnection::GetEnlapsedRequestTime()
|
||||
{
|
||||
struct timeval tv_now;
|
||||
struct timezone tz_now;
|
||||
gettimeofday(&tv_now, &tz_now);
|
||||
return ((tv_now.tv_sec - tv_connection_start.tv_sec) * 1000000
|
||||
+ (tv_now.tv_usec - tv_connection_start.tv_usec));
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Time from beginning of response until now in microseconds!
|
||||
//-------------------------------------------------------------------------
|
||||
long CWebserverConnection::GetEnlapsedResponseTime()
|
||||
{
|
||||
struct timeval tv_now;
|
||||
struct timezone tz_now;
|
||||
gettimeofday(&tv_now, &tz_now);
|
||||
return ((tv_now.tv_sec - tv_connection_Response_start.tv_sec) * 1000000
|
||||
+ (tv_now.tv_usec - tv_connection_Response_start.tv_usec));
|
||||
}
|
||||
|
73
src/nhttpd/yhttpd_core/yconnection.h
Normal file
73
src/nhttpd/yhttpd_core/yconnection.h
Normal file
@@ -0,0 +1,73 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Connection
|
||||
//-----------------------------------------------------------------------------
|
||||
// The Connection Class handles a given Socket-Connection for an arriving
|
||||
// Request. Normally many Connections exists and they are Threaded!
|
||||
// "HandleConnection()" calls "HandleRequest()" and "SendResponse()"
|
||||
//=============================================================================
|
||||
#ifndef __yhttpd_connection_h__
|
||||
#define __yhttpd_connection_h__
|
||||
|
||||
// system
|
||||
#include <sys/time.h>
|
||||
// c++
|
||||
#include <string>
|
||||
// yhttpd
|
||||
#include "yconfig.h"
|
||||
#include "ytypes_globals.h"
|
||||
#include "ywebserver.h"
|
||||
#include "yrequest.h"
|
||||
#include "yresponse.h"
|
||||
|
||||
// Forward definition
|
||||
class CWebserver;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWebserverConnection
|
||||
{
|
||||
public:
|
||||
class CWebserver *Webserver; // Backreference
|
||||
CWebserverRequest Request; // http Request
|
||||
CWebserverResponse Response; // http Response
|
||||
CyhookHandler HookHandler; // One Instance per Connection
|
||||
|
||||
CySocket *sock; // Connection Socket
|
||||
bool RequestCanceled; // Canceled flag
|
||||
static long GConnectionNumber; // Number of Connection. Global counter
|
||||
long ConnectionNumber; // Number of Connection since Webserver start. Local number
|
||||
bool keep_alive; // should the response keep-alive socket?
|
||||
|
||||
THttp_Method Method; // http Method (GET, POST,...)
|
||||
int HttpStatus; // http response code
|
||||
std::string httprotocol; // HTTP 1.x
|
||||
|
||||
long enlapsed_request, // time for request in usec
|
||||
enlapsed_response; // time for response in usec
|
||||
|
||||
CWebserverConnection(CWebserver *pWebserver); // Constructor with given Backreferece
|
||||
CWebserverConnection();
|
||||
~CWebserverConnection(void);
|
||||
|
||||
void HandleConnection(); // Main: Handle the Connecten Live time
|
||||
|
||||
// performance/time measurement
|
||||
void ShowEnlapsedRequest(char *text); // Print text and time difference to Connection creation
|
||||
long GetEnlapsedRequestTime(); // Get time in micro-seconds from Connection creation
|
||||
long GetEnlapsedResponseTime(); // Get time in micro-seconds after Request handles. Start Respnse.
|
||||
|
||||
private:
|
||||
void EndConnection(); // End Connection Handling
|
||||
|
||||
// performance/time measurement
|
||||
struct timeval tv_connection_start,
|
||||
tv_connection_Response_start,
|
||||
tv_connection_Response_end;
|
||||
struct timezone tz_connection_start,
|
||||
tz_connection_Response_start,
|
||||
tz_connection_Response_end;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif /* __yhttpd_connection_h__ */
|
357
src/nhttpd/yhttpd_core/yhook.cpp
Normal file
357
src/nhttpd/yhttpd_core/yhook.cpp
Normal file
@@ -0,0 +1,357 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Hook and HookHandler
|
||||
//=============================================================================
|
||||
// C
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
// yhttpd
|
||||
#include "yhook.h"
|
||||
#include "ylogging.h"
|
||||
#include "helper.h"
|
||||
|
||||
//=============================================================================
|
||||
// Initialization of static variables
|
||||
//=============================================================================
|
||||
THookList CyhookHandler::HookList;
|
||||
|
||||
//=============================================================================
|
||||
// Hook Handling
|
||||
//=============================================================================
|
||||
//-----------------------------------------------------------------------------
|
||||
// Hook Dispatcher for Session Hooks
|
||||
// Execute every Hook in HookList until State change != HANDLED_NONE
|
||||
//-----------------------------------------------------------------------------
|
||||
THandleStatus CyhookHandler::Hooks_SendResponse()
|
||||
{
|
||||
log_level_printf(4,"Response Hook-List Start\n");
|
||||
THandleStatus _status = HANDLED_NONE;
|
||||
THookList::iterator i = HookList.begin();
|
||||
for ( ; i!= HookList.end(); i++ )
|
||||
{
|
||||
log_level_printf(4,"Response Hook-List (%s) Start\n", ((*i)->getHookName()).c_str());
|
||||
// response Hook
|
||||
_status = (*i)->Hook_SendResponse(this);
|
||||
log_level_printf(4,"Response Hook-List (%s) End. Status (%d)\n", ((*i)->getHookName()).c_str(), status);
|
||||
if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE))
|
||||
break;
|
||||
}
|
||||
log_level_printf(4,"Response Hook-List End\n");
|
||||
log_level_printf(8,"Response Hook-List Result:\n%s\n", yresult.c_str());
|
||||
status = _status;
|
||||
return _status;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Hook Dispatcher for Session Hooks
|
||||
// Execute every Hook in HookList until State change != HANDLED_NONE
|
||||
//-----------------------------------------------------------------------------
|
||||
THandleStatus CyhookHandler::Hooks_PrepareResponse()
|
||||
{
|
||||
log_level_printf(4,"PrepareResponse Hook-List Start\n");
|
||||
THandleStatus _status = HANDLED_NONE;
|
||||
THookList::iterator i = HookList.begin();
|
||||
for ( ; i!= HookList.end(); i++ )
|
||||
{
|
||||
log_level_printf(4,"PrepareResponse Hook-List (%s) Start\n", ((*i)->getHookName()).c_str());
|
||||
// response Hook
|
||||
_status = (*i)->Hook_PrepareResponse(this);
|
||||
log_level_printf(4,"PrepareResponse Hook-List (%s) End. Status (%d) HTTP Status (%d)\n", ((*i)->getHookName()).c_str(), status, httpStatus);
|
||||
if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE))
|
||||
break;
|
||||
}
|
||||
log_level_printf(4,"PrepareResponse Hook-List End\n");
|
||||
log_level_printf(8,"PrepareResponse Hook-List Result:\n%s\n", yresult.c_str());
|
||||
status = _status;
|
||||
return _status;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Hook Dispatcher for Server based Hooks
|
||||
// Execute every Hook in HookList until State change != HANDLED_NONE and
|
||||
// != HANDLED_CONTINUE
|
||||
//-----------------------------------------------------------------------------
|
||||
THandleStatus CyhookHandler::Hooks_ReadConfig(CConfigFile *Config, CStringList &ConfigList)
|
||||
{
|
||||
log_level_printf(4,"ReadConfig Hook-List Start\n");
|
||||
THandleStatus _status = HANDLED_NONE;
|
||||
THookList::iterator i = HookList.begin();
|
||||
for ( ; i!= HookList.end(); i++ )
|
||||
{
|
||||
// log_level_printf(4,"ReadConfig Hook-List (%s) Start\n", ((*i)->getHookName()).c_str());
|
||||
// response Hook
|
||||
_status = (*i)->Hook_ReadConfig(Config, ConfigList);
|
||||
log_level_printf(4,"ReadConfig Hook-List (%s) Status (%d)\n", ((*i)->getHookName()).c_str(), _status);
|
||||
if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE))
|
||||
break;
|
||||
}
|
||||
log_level_printf(4,"ReadConfig Hook-List End\n");
|
||||
return _status;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Hook Dispatcher for EndConnection
|
||||
//-----------------------------------------------------------------------------
|
||||
THandleStatus CyhookHandler::Hooks_EndConnection()
|
||||
{
|
||||
log_level_printf(4,"EndConnection Hook-List Start\n");
|
||||
THandleStatus _status = HANDLED_NONE;
|
||||
THookList::iterator i = HookList.begin();
|
||||
for ( ; i!= HookList.end(); i++ )
|
||||
{
|
||||
log_level_printf(4,"EndConnection Hook-List (%s) Start\n", ((*i)->getHookName()).c_str());
|
||||
// response Hook
|
||||
_status = (*i)->Hook_EndConnection(this);
|
||||
log_level_printf(4,"EndConnection Hook-List (%s) End. Status (%d)\n", ((*i)->getHookName()).c_str(), _status);
|
||||
if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE))
|
||||
break;
|
||||
}
|
||||
log_level_printf(4,"EndConnection Hook-List End\n");
|
||||
status = _status;
|
||||
return _status;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Hook Dispatcher for UploadSetFilename
|
||||
//-----------------------------------------------------------------------------
|
||||
THandleStatus CyhookHandler::Hooks_UploadSetFilename(std::string &Filename)
|
||||
{
|
||||
log_level_printf(4,"UploadSetFilename Hook-List Start. Filename:(%s)\n", Filename.c_str());
|
||||
THandleStatus _status = HANDLED_NONE;
|
||||
THookList::iterator i = HookList.begin();
|
||||
for ( ; i!= HookList.end(); i++ )
|
||||
{
|
||||
log_level_printf(4,"UploadSetFilename Hook-List (%s) Start\n", ((*i)->getHookName()).c_str());
|
||||
// response Hook
|
||||
_status = (*i)->Hook_UploadSetFilename(this, Filename);
|
||||
log_level_printf(4,"UploadSetFilename Hook-List (%s) End. Status (%d)\n", ((*i)->getHookName()).c_str(), _status);
|
||||
if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE))
|
||||
break;
|
||||
}
|
||||
log_level_printf(4,"UploadSetFilename Hook-List End\n");
|
||||
status = _status;
|
||||
return _status;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Hook Dispatcher for UploadSetFilename
|
||||
//-----------------------------------------------------------------------------
|
||||
THandleStatus CyhookHandler::Hooks_UploadReady(const std::string& Filename)
|
||||
{
|
||||
log_level_printf(4,"UploadReady Hook-List Start. Filename:(%s)\n", Filename.c_str());
|
||||
THandleStatus _status = HANDLED_NONE;
|
||||
THookList::iterator i = HookList.begin();
|
||||
for ( ; i!= HookList.end(); i++ )
|
||||
{
|
||||
log_level_printf(4,"UploadReady Hook-List (%s) Start\n", ((*i)->getHookName()).c_str());
|
||||
// response Hook
|
||||
_status = (*i)->Hook_UploadReady(this, Filename);
|
||||
log_level_printf(4,"UploadReady Hook-List (%s) End. Status (%d)\n", ((*i)->getHookName()).c_str(), _status);
|
||||
if((_status != HANDLED_NONE) && (_status != HANDLED_CONTINUE))
|
||||
break;
|
||||
}
|
||||
log_level_printf(4,"UploadReady Hook-List End\n");
|
||||
status = _status;
|
||||
return _status;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Output helpers
|
||||
//=============================================================================
|
||||
void CyhookHandler::session_init(CStringList _ParamList, CStringList _UrlData, CStringList _HeaderList,
|
||||
CStringList& _ConfigList, THttp_Method _Method, bool _keep_alive)
|
||||
{
|
||||
ParamList = _ParamList;
|
||||
UrlData = _UrlData;
|
||||
HeaderList = _HeaderList;
|
||||
WebserverConfigList = _ConfigList;
|
||||
Method = _Method;
|
||||
status = HANDLED_NONE;
|
||||
httpStatus = HTTP_OK;
|
||||
ContentLength = 0;
|
||||
LastModified = (time_t)-1;
|
||||
keep_alive = _keep_alive;
|
||||
HookVarList.clear();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Build Header
|
||||
//-----------------------------------------------------------------------------
|
||||
// RFC 2616 / 6 Response (Header)
|
||||
//
|
||||
// The first line of a Response message is the Status-Line, consisting
|
||||
// of the protocol version followed by a numeric status code and its
|
||||
// associated textual phrase, with each element separated by SP
|
||||
// characters. No CR or LF is allowed except in the final CRLF sequence.
|
||||
//
|
||||
// Response =Status-Line ; generated by SendHeader
|
||||
// *(( general-header
|
||||
// | response-header
|
||||
// | entity-header ) CRLF)
|
||||
// CRLF ; generated by SendHeader
|
||||
// [ message-body ] ; by HOOK Handling Loop or Sendfile
|
||||
//
|
||||
// Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF ; generated by SendHeader
|
||||
//
|
||||
// general-header = Cache-Control ; not implemented
|
||||
// | Connection ; implemented
|
||||
// | Date ; implemented
|
||||
// | Pragma ; not implemented
|
||||
// | Trailer ; not implemented
|
||||
// | Transfer-Encoding ; not implemented
|
||||
// | Upgrade ; not implemented
|
||||
// | Via ; not implemented
|
||||
// | Warning ; not implemented
|
||||
//
|
||||
// response-header = Accept-Ranges ; not implemented
|
||||
// | Age ; not implemented
|
||||
// | ETag ; not implemented
|
||||
// | Location ; implemented (redirection / Object moved)
|
||||
// | Proxy-Authenticate ; not implemented
|
||||
// | Retry-After ; not implemented
|
||||
// | Server ; implemented
|
||||
// | Vary ; not implemented
|
||||
// | WWW-Authenticate ; implemented (by mod_auth and SendHeader)
|
||||
//
|
||||
// entity-header = Allow ; not implemented
|
||||
// | Content-Encoding ; not implemented
|
||||
// | Content-Language ; not implemented
|
||||
// | Content-Length ; implemented
|
||||
// | Content-Location ; not implemented
|
||||
// | Content-MD5 ; not implemented
|
||||
// | Content-Range ; not implemented
|
||||
// | Content-Type ; implemented
|
||||
// | Expires ; not implemented
|
||||
// | Last-Modified ; implemented for static files
|
||||
// | extension-header
|
||||
//
|
||||
// extension-header = message-header
|
||||
//=============================================================================
|
||||
std::string CyhookHandler::BuildHeader(bool cache)
|
||||
{
|
||||
std::string result="";
|
||||
|
||||
const char *responseString = "";
|
||||
const char *infoString = 0;
|
||||
|
||||
// get Info Index
|
||||
for (unsigned int i = 0;i < (sizeof(httpResponseNames)/sizeof(httpResponseNames[0])); i++)
|
||||
if (httpResponseNames[i].type == httpStatus)
|
||||
{
|
||||
responseString = httpResponseNames[i].name;
|
||||
infoString = httpResponseNames[i].info;
|
||||
break;
|
||||
}
|
||||
// print Status-line
|
||||
result = string_printf(HTTP_PROTOCOL " %d %s\r\nContent-Type: %s\r\n",httpStatus, responseString, ResponseMimeType.c_str());
|
||||
log_level_printf(2,"Respose: HTTP/1.1 %d %s\r\nContent-Type: %s\r\n",httpStatus, responseString, ResponseMimeType.c_str());
|
||||
|
||||
switch (httpStatus)
|
||||
{
|
||||
case HTTP_UNAUTHORIZED:
|
||||
result += "WWW-Authenticate: Basic realm=\"";
|
||||
result += AUTH_NAME_MSG "\r\n";
|
||||
break;
|
||||
|
||||
case HTTP_MOVED_TEMPORARILY:
|
||||
case HTTP_MOVED_PERMANENTLY:
|
||||
// Status HTTP_*_TEMPORARILY (redirection)
|
||||
result += string_printf("Location: %s\r\n",NewURL.c_str());
|
||||
// NO break HERE !!!
|
||||
|
||||
default:
|
||||
time_t timer = time(0);
|
||||
char timeStr[80];
|
||||
// cache
|
||||
if(!cache && (HookVarList["CacheCategory"]).empty() )
|
||||
result += "Cache-Control: no-cache\r\n";
|
||||
else
|
||||
{
|
||||
time_t x_time = time(NULL);
|
||||
struct tm *ptm = gmtime(&x_time);
|
||||
ptm->tm_mday+=1;
|
||||
x_time = mktime(ptm);
|
||||
strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&x_time));
|
||||
result += string_printf("Expires: %s\r\n", timeStr);
|
||||
}
|
||||
result += "Server: " WEBSERVERNAME "\r\n";
|
||||
// actual date
|
||||
strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&timer));
|
||||
result += string_printf("Date: %s\r\n", timeStr);
|
||||
// connection type
|
||||
#ifdef Y_CONFIG_FEATURE_KEEP_ALIVE
|
||||
if(keep_alive)
|
||||
result += "Connection: keep-alive\r\n";
|
||||
else
|
||||
result += "Connection: close\r\n";
|
||||
#else
|
||||
result += "Connection: close\r\n";
|
||||
#endif
|
||||
// content-len, last-modified
|
||||
if(httpStatus == HTTP_NOT_MODIFIED ||httpStatus == HTTP_NOT_FOUND)
|
||||
result += "Content-Length: 0\r\n";
|
||||
else if(GetContentLength() >0)
|
||||
{
|
||||
time_t mod_time = time(NULL);
|
||||
if(LastModified != (time_t)-1)
|
||||
mod_time = LastModified;
|
||||
|
||||
strftime(timeStr, sizeof(timeStr), RFC1123FMT, gmtime(&mod_time));
|
||||
result += string_printf("Last-Modified: %s\r\nContent-Length: %ld\r\n", timeStr, GetContentLength());
|
||||
}
|
||||
result += "\r\n"; // End of Header
|
||||
break;
|
||||
}
|
||||
|
||||
// Body
|
||||
if (Method != M_HEAD)
|
||||
switch (httpStatus)
|
||||
{
|
||||
case HTTP_OK:
|
||||
case HTTP_NOT_MODIFIED:
|
||||
case HTTP_CONTINUE:
|
||||
case HTTP_ACCEPTED:
|
||||
case HTTP_NO_CONTENT:
|
||||
case HTTP_NOT_FOUND:
|
||||
case HTTP_INTERNAL_SERVER_ERROR:
|
||||
break;
|
||||
|
||||
case HTTP_MOVED_TEMPORARILY:
|
||||
case HTTP_MOVED_PERMANENTLY:
|
||||
result += "<html><head><title>Object moved</title></head><body>";
|
||||
result += string_printf("302 : Object moved.<br/>If you dont get redirected click <a href=\"%s\">here</a></body></html>\n",NewURL.c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
// Error pages
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Output helpers
|
||||
//=============================================================================
|
||||
//-----------------------------------------------------------------------------
|
||||
void CyhookHandler::SendHTMLHeader(const std::string& Titel)
|
||||
{
|
||||
WriteLn("<html>\n<head><title>" + Titel + "</title>\n");
|
||||
WriteLn("<meta http-equiv=\"cache-control\" content=\"no-cache\" />");
|
||||
WriteLn("<meta http-equiv=\"expires\" content=\"0\" />\n</head>\n<body>\n");
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void CyhookHandler::SendHTMLFooter(void)
|
||||
{
|
||||
WriteLn("</body>\n</html>\n\n");
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#define OUTBUFSIZE 4096
|
||||
void CyhookHandler::printf ( const char *fmt, ... )
|
||||
{
|
||||
char outbuf[OUTBUFSIZE];
|
||||
bzero(outbuf,OUTBUFSIZE);
|
||||
va_list arglist;
|
||||
va_start( arglist, fmt );
|
||||
vsnprintf( outbuf,OUTBUFSIZE, fmt, arglist );
|
||||
va_end(arglist);
|
||||
Write(outbuf);
|
||||
}
|
187
src/nhttpd/yhttpd_core/yhook.h
Normal file
187
src/nhttpd/yhttpd_core/yhook.h
Normal file
@@ -0,0 +1,187 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Hook and HookHandler
|
||||
//-----------------------------------------------------------------------------
|
||||
// Extentions for the Webserver can be implemented with Hooks.
|
||||
// A "Hook"-Class must be inherited from Cyhook and can override the virtual
|
||||
// functions from Cyhook.
|
||||
// There are three types of Hooks.
|
||||
// 1) Server based Hooks (exactly one Instance of Cyhttpd)
|
||||
// 2) Webserver based Hooks (for each Instance of CWebserver, actually one; not now)
|
||||
// 3) Connection based Hooks (for each Instance of CWebserverConnection)
|
||||
//-----------------------------------------------------------------------------
|
||||
// The CyhookHandler-Class itselfs calls the "attached" hooks.
|
||||
// Hook-Classes must be attached/de-attached in Cyhttpd-Class attach()/detach().
|
||||
// Hook-Class instances should be initialized in yhttpd.cpp.
|
||||
//-----------------------------------------------------------------------------
|
||||
// "Hook_SendResponse"
|
||||
//-----------------------------------------------------------------------------
|
||||
// "Hook_SendResponse": this is the most important Hook.
|
||||
// The CyhookHandler implements an abstraction for Request and Response data.
|
||||
// Every Hook which has implemented Hook_SendResponse could be called in order
|
||||
// of the "attach" to HookHandler. The control flow, which Hook will be called
|
||||
// depends on the return value "THandleStatus" from the hook called before.
|
||||
// For example HANDLED_NONE indicates that the Hook has not handled by the call.
|
||||
// For example HANDLED_READY indicates that the call is handled and HookHandler
|
||||
// should finish the Hook-Loop. HANDLED_NOT_IMPLEMENTED indicated that the Hook
|
||||
// should handle the call, but has not any function to do that.
|
||||
// Several Hooks can work together (HANDLED_CONTINUE or HANDLED_REWRITE); so the
|
||||
// next called Hook can work with the output (in yresult) from the before called
|
||||
// Hook or controls the workflow of following Hooks.
|
||||
// For example the CmAuth-Hook validates HTTP-Authentication and returns
|
||||
// HANDLED_CONTINUE if the authentication fits.
|
||||
// Each Hook_SendResponse is encapsulated from the webserver and should not work
|
||||
// on Socket-Connections. The Response is written to yresult. There are many
|
||||
// helper-function for Output and Status-Handling.
|
||||
// The Input is encapsulated too: ParamList, HeaderList, UrlData, ...
|
||||
// Look at Response.SendResponse()
|
||||
//-----------------------------------------------------------------------------
|
||||
// Other Hooks
|
||||
//-----------------------------------------------------------------------------
|
||||
// - Hook_ReadConfig: Every Hook can read OWN Variables from the yhttpd-Configfile
|
||||
// - Hook_EndConnection: After Response is sent and before Connection Instance
|
||||
// is deleted
|
||||
// - Hook_UploadSetFilename: this hook can set the filename for a file to upload
|
||||
// via POST (before upload)
|
||||
// - Hook_UploadReady: this Hook is called after uploading a file
|
||||
//=============================================================================
|
||||
#ifndef __yhttpd_yhook_h__
|
||||
#define __yhttpd_yhook_h__
|
||||
// C++
|
||||
#include <string>
|
||||
#include <list>
|
||||
// yhttpd
|
||||
#include "yconfig.h"
|
||||
#include "ytypes_globals.h"
|
||||
#include "ylogging.h"
|
||||
// tuxbox
|
||||
#include <configfile.h>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// forward declarations
|
||||
//-----------------------------------------------------------------------------
|
||||
class CyhookHandler;
|
||||
class Cyhook;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Type definitions for Hooks
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef enum
|
||||
{
|
||||
HANDLED_NONE = 0, // Init
|
||||
HANDLED_READY, // Handled
|
||||
HANDLED_ABORT, // Abort Connection Fatal
|
||||
HANDLED_ERROR, // Have Error like missing Parameter
|
||||
HANDLED_NOT_IMPLEMENTED,// URL should be handled but not implemented
|
||||
HANDLED_REDIRECTION, // Set new URL and send HTTPD Object Moved
|
||||
HANDLED_SENDFILE, // Set new URL and Send File
|
||||
HANDLED_REWRITE, // Set new URL and call Hooks again
|
||||
HANDLED_CONTINUE, // handled but go on
|
||||
|
||||
} THandleStatus;
|
||||
typedef std::list<Cyhook *> THookList;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// An abstract Hook-Class. Custom Hook must be inherited.
|
||||
//-----------------------------------------------------------------------------
|
||||
class Cyhook
|
||||
{
|
||||
protected:
|
||||
public:
|
||||
Cyhook(){};
|
||||
virtual ~Cyhook(){};
|
||||
// Informations & Control
|
||||
virtual std::string getHookVersion(void) {return std::string("0.0.0");}
|
||||
virtual std::string getHookName(void) {return std::string("Abstract Hook Class");}
|
||||
// CWebserverConnection based hooks
|
||||
virtual THandleStatus Hook_PrepareResponse(CyhookHandler *hh){return HANDLED_NONE;};
|
||||
virtual THandleStatus Hook_SendResponse(CyhookHandler *hh){return HANDLED_NONE;};
|
||||
virtual THandleStatus Hook_EndConnection(CyhookHandler *hh){return HANDLED_NONE;}
|
||||
virtual THandleStatus Hook_UploadSetFilename(CyhookHandler *hh, std::string &Filename){return HANDLED_NONE;}
|
||||
virtual THandleStatus Hook_UploadReady(CyhookHandler *hh, std::string Filename){return HANDLED_NONE;}
|
||||
// Cyhttpd based hooks
|
||||
virtual THandleStatus Hook_ReadConfig(CConfigFile *Config, CStringList &ConfigList){return HANDLED_NONE;};
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Hook Handling and Input & Output abstraction
|
||||
//-----------------------------------------------------------------------------
|
||||
class CyhookHandler
|
||||
{
|
||||
protected:
|
||||
static THookList HookList;
|
||||
public:
|
||||
// Output
|
||||
std::string yresult; // content for response output
|
||||
THandleStatus status; // status of Hook handling
|
||||
HttpResponseType httpStatus; // http-status code for response
|
||||
std::string ResponseMimeType; // mime-type for response
|
||||
std::string NewURL; // new URL for Redirection
|
||||
long ContentLength; // Length of Response Body
|
||||
time_t LastModified; // Last Modified Time of Item to send / -1 dynamic content
|
||||
std::string Sendfile; // Path & Name (local os style) of file to send
|
||||
bool keep_alive;
|
||||
|
||||
// Input
|
||||
CStringList ParamList; // local copy of ParamList (Request)
|
||||
CStringList UrlData; // local copy of UrlData (Request)
|
||||
CStringList HeaderList; // local copy of HeaderList (Request)
|
||||
CStringList WebserverConfigList; // Reference (writable) to ConfigList
|
||||
CStringList HookVarList; // Variables in Hook-Handling passing to other Hooks
|
||||
THttp_Method Method; // HTTP Method (requested)
|
||||
// constructor & deconstructor
|
||||
CyhookHandler(){};
|
||||
virtual ~CyhookHandler(){};
|
||||
|
||||
// hook slot handler
|
||||
static void attach(Cyhook *yh) // attach a Hook-Class to HookHandler
|
||||
{HookList.push_back(yh);};
|
||||
static void detach(Cyhook *yh) // detach a Hook-Class to HookHandler
|
||||
{HookList.remove(yh);};
|
||||
|
||||
// session handling
|
||||
void session_init(CStringList _ParamList, CStringList _UrlData, CStringList _HeaderList,
|
||||
CStringList& _ConfigList, THttp_Method _Method, bool _keep_alive);
|
||||
|
||||
// Cyhttpd based hooks
|
||||
static THandleStatus Hooks_ReadConfig(CConfigFile *Config, CStringList &ConfigList);
|
||||
// CWebserverConnection based hooks
|
||||
THandleStatus Hooks_PrepareResponse(); // Hook for Response.SendResonse Dispatching
|
||||
THandleStatus Hooks_SendResponse(); // Hook for Response.SendResonse Dispatching
|
||||
THandleStatus Hooks_EndConnection();
|
||||
THandleStatus Hooks_UploadSetFilename(std::string &Filename);
|
||||
THandleStatus Hooks_UploadReady(const std::string& Filename);
|
||||
|
||||
// status handling
|
||||
void SetHeader(HttpResponseType _httpStatus, std::string _ResponseMimeType)
|
||||
{httpStatus = _httpStatus; ResponseMimeType = _ResponseMimeType;}
|
||||
void SetHeader(HttpResponseType _httpStatus, std::string _ResponseMimeType, THandleStatus _status)
|
||||
{httpStatus = _httpStatus; ResponseMimeType = _ResponseMimeType; status = _status;}
|
||||
void SetError(HttpResponseType responseType)
|
||||
{SetHeader(responseType, "text/html");}
|
||||
void SetError(HttpResponseType responseType, THandleStatus _status)
|
||||
{SetError(responseType); status = _status;}
|
||||
// others
|
||||
long GetContentLength()
|
||||
{return (status==HANDLED_SENDFILE)?ContentLength : (long)yresult.length();}
|
||||
// output methods
|
||||
std::string BuildHeader(bool cache = false);
|
||||
void addResult(const std::string& result) {yresult += result;}
|
||||
void addResult(const std::string& result, THandleStatus _status)
|
||||
{yresult += result; status = _status;}
|
||||
void printf(const char *fmt, ...);
|
||||
void Write(const std::string& text) { addResult(text); }
|
||||
void WriteLn(const std::string& text) { addResult(text+"\r\n"); }
|
||||
void Write(char const *text) {Write(std::string(text));}
|
||||
void WriteLn(char const *text) {WriteLn(std::string(text));}
|
||||
void SendHTMLHeader(const std::string& Titel);
|
||||
void SendHTMLFooter(void);
|
||||
void SendOk(void) {Write("ok");}
|
||||
void SendError(void) {Write("error");}
|
||||
void SendFile(const std::string& url) {NewURL = url; status = HANDLED_SENDFILE;}
|
||||
void SendRedirect(const std::string& url) {httpStatus=HTTP_MOVED_TEMPORARILY; NewURL = url; status = HANDLED_REDIRECTION;}
|
||||
void SendRewrite(const std::string& url){NewURL = url; status = HANDLED_REWRITE;}
|
||||
friend class CyParser;
|
||||
};
|
||||
|
||||
#endif /*__yhttpd_yhook_h__*/
|
94
src/nhttpd/yhttpd_core/ylogging.cpp
Normal file
94
src/nhttpd/yhttpd_core/ylogging.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Logging & Debugging
|
||||
//=============================================================================
|
||||
|
||||
// c
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
// yhttpd
|
||||
#include "yconfig.h"
|
||||
#include "ytypes_globals.h"
|
||||
#include "ylogging.h"
|
||||
#include "yconnection.h"
|
||||
//=============================================================================
|
||||
// Instance Handling - like Singelton Pattern
|
||||
//=============================================================================
|
||||
//-----------------------------------------------------------------------------
|
||||
// Init as Singelton
|
||||
//-----------------------------------------------------------------------------
|
||||
CLogging *CLogging::instance = NULL;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// There is only one Instance
|
||||
//-----------------------------------------------------------------------------
|
||||
CLogging *CLogging::getInstance(void)
|
||||
{
|
||||
if (!instance)
|
||||
instance = new CLogging();
|
||||
return instance;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CLogging::deleteInstance(void)
|
||||
{
|
||||
if (instance)
|
||||
delete instance;
|
||||
instance = NULL;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Constructor
|
||||
//-----------------------------------------------------------------------------
|
||||
CLogging::CLogging(void)
|
||||
{
|
||||
Debug = false;
|
||||
LogToFile = false; //not implemented
|
||||
LogLevel = 0;
|
||||
Logfile = NULL;
|
||||
pthread_mutex_init(&Log_mutex, NULL);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CLogging::~CLogging(void)
|
||||
{
|
||||
}
|
||||
//=============================================================================
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void CLogging::setDebug(bool _debug)
|
||||
{
|
||||
Debug = _debug;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CLogging::getDebug(void)
|
||||
{
|
||||
return Debug;
|
||||
}
|
||||
//=============================================================================
|
||||
// Logging Calls
|
||||
// use mutex controlled calls to output resources
|
||||
// Normal Logging to Stdout, if "Log" is true then Log to file
|
||||
//=============================================================================
|
||||
#define bufferlen 1024*8
|
||||
//-----------------------------------------------------------------------------
|
||||
void CLogging::printf ( const char *fmt, ... )
|
||||
{
|
||||
char buffer[bufferlen];
|
||||
|
||||
|
||||
va_list arglist;
|
||||
va_start( arglist, fmt );
|
||||
if(arglist)
|
||||
vsnprintf( buffer, bufferlen, fmt, arglist );
|
||||
va_end(arglist);
|
||||
|
||||
pthread_mutex_lock( &Log_mutex );
|
||||
::printf(buffer);
|
||||
if(LogToFile)
|
||||
; //FIXME Logging to File
|
||||
pthread_mutex_unlock( &Log_mutex );
|
||||
}
|
||||
|
86
src/nhttpd/yhttpd_core/ylogging.h
Normal file
86
src/nhttpd/yhttpd_core/ylogging.h
Normal file
@@ -0,0 +1,86 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Logging & Debugging
|
||||
//=============================================================================
|
||||
#ifndef __yhttpd_logging_h__
|
||||
#define __yhttpd_logging_h__
|
||||
|
||||
// system
|
||||
#include <pthread.h>
|
||||
// yhttpd
|
||||
#include "yconfig.h"
|
||||
|
||||
// forward declaration
|
||||
class CWebserverConnection;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Log Level
|
||||
//-----------------------------------------------------------------------------
|
||||
// 1 : Connection Information (called URL)
|
||||
// 2 : Parameters??
|
||||
// 3 : yParser Main Infos
|
||||
// 5 : ycmd: Every yParser Loop Command expansion
|
||||
// 6 : yresult: Every yParser Loop Command expansion
|
||||
// 8 : Central Functions Detailed
|
||||
// 9 : Socket Operations
|
||||
//-----------------------------------------------------------------------------
|
||||
class CLogging
|
||||
{
|
||||
protected:
|
||||
pthread_mutex_t Log_mutex;
|
||||
FILE *Logfile;
|
||||
bool Debug;
|
||||
bool LogToFile;
|
||||
|
||||
static CLogging *instance;
|
||||
|
||||
CLogging(void);
|
||||
~CLogging(void);
|
||||
|
||||
public:
|
||||
int LogLevel;
|
||||
|
||||
// Instance Handling
|
||||
static CLogging *getInstance(void);
|
||||
static void deleteInstance(void);
|
||||
|
||||
void setDebug(bool _debug);
|
||||
bool getDebug(void);
|
||||
|
||||
// Logging
|
||||
void printf(const char *fmt, ...);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// definitions for easy use
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// print always
|
||||
#define aprintf(fmt, args...) \
|
||||
do { CLogging::getInstance()->printf("[yhttpd] " fmt, ## args); } while (0)
|
||||
|
||||
// print show file and linenumber
|
||||
#define log_printfX(fmt, args...) \
|
||||
do { CLogging::getInstance()->printf("[yhttpd(%s:%d)] " fmt, __FILE__, __LINE__, ## args); } while (0)
|
||||
|
||||
//Set Watch Point (show file and linenumber and function)
|
||||
#define WP() \
|
||||
do { CLogging::getInstance()->printf("[yhttpd(%s:%d)%s]\n", __FILE__, __LINE__, __FUNCTION__); } while (0)
|
||||
|
||||
// print if level matches
|
||||
#define log_level_printf(level, fmt, args...) \
|
||||
do { if(CLogging::getInstance()->LogLevel>=level) CLogging::getInstance()->printf("[yhttpd#%d] " fmt, level, ## args); } while (0)
|
||||
|
||||
// print if level matches / show file and linenumber
|
||||
#define log_level_printfX(level, fmt, args...) \
|
||||
do { if(CLogging::getInstance()->LogLevel>=level) CLogging::getInstance()->printf("[yhttpd#%d(%s:%d)] " fmt, level, __FILE__, __LINE__, ## args); } while (0)
|
||||
|
||||
// print only if debug is on
|
||||
#define dprintf(fmt, args...) \
|
||||
do { if(CLogging::getInstance()->getDebug())CLogging::getInstance()->printf("[yhttpd] " fmt, ## args); } while (0)
|
||||
|
||||
// print string to stdandard error
|
||||
#define dperror(str) \
|
||||
do { perror("[yhttpd] " str); } while (0)
|
||||
|
||||
#endif /* __yttpd_logging_h__ */
|
601
src/nhttpd/yhttpd_core/yrequest.cpp
Normal file
601
src/nhttpd/yhttpd_core/yrequest.cpp
Normal file
@@ -0,0 +1,601 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Request
|
||||
//-----------------------------------------------------------------------------
|
||||
//-----------------------------------------------------------------------------
|
||||
// Request-Data
|
||||
// UrlData (CStringList)
|
||||
// startline First line of http-request (GET <fullurl> HTTP/1.x)
|
||||
// fullurl ::= <url>?<paramstring>
|
||||
// paramstring Is provided by String-Array ParameterList
|
||||
// url ::= <path>/<filename> "/" is last "/" in string
|
||||
// path begin with /...
|
||||
// filename ::= <filenamepure>.<fileext>
|
||||
// filenamepure
|
||||
// fileext Extension like html, jpg, ...
|
||||
//=============================================================================
|
||||
|
||||
// c++
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <string.h>
|
||||
#include <cstdlib>
|
||||
#include <errno.h>
|
||||
// system
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
// yhttpd
|
||||
#include "yrequest.h"
|
||||
#include "yconnection.h"
|
||||
#include "helper.h"
|
||||
|
||||
//=============================================================================
|
||||
// Constructor & Destructor
|
||||
//=============================================================================
|
||||
CWebserverRequest::CWebserverRequest(CWebserver *pWebserver)
|
||||
{
|
||||
Webserver = pWebserver;
|
||||
CWebserverRequest();
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Parsing Request
|
||||
//=============================================================================
|
||||
//-----------------------------------------------------------------------------
|
||||
// Main Request Parsing
|
||||
// RFC2616
|
||||
// generic-message = start-line
|
||||
// *(message-header CRLF)
|
||||
// CRLF
|
||||
// [ message-body ]
|
||||
// start-line = Request-Line | Status-Line
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserverRequest::HandleRequest(void)
|
||||
{
|
||||
std::string start_line = "";
|
||||
// read first line
|
||||
do
|
||||
{
|
||||
start_line = Connection->sock->ReceiveLine();
|
||||
if(!Connection->sock->isValid)
|
||||
return false;
|
||||
if(start_line == "") // Socket empty
|
||||
{
|
||||
log_level_printf(1,"HandleRequest: End of line not found\n");
|
||||
Connection->Response.SendError(HTTP_INTERNAL_SERVER_ERROR);
|
||||
Connection->RequestCanceled = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while(start_line == "\r\n"); // ignore empty lines at begin on start-line
|
||||
|
||||
start_line = trim(start_line);
|
||||
log_level_printf(1,"Request: %s\n", start_line.c_str() );
|
||||
UrlData["startline"] = start_line;
|
||||
if(!ParseStartLine(start_line))
|
||||
return false;
|
||||
|
||||
|
||||
if(Connection->Method == M_GET || Connection->Method == M_HEAD)
|
||||
{
|
||||
std::string tmp_line;
|
||||
//read header (speed up: read rest of request in blockmode)
|
||||
tmp_line = Connection->sock->ReceiveBlock();
|
||||
if(!Connection->sock->isValid)
|
||||
{
|
||||
Connection->Response.SendError(HTTP_INTERNAL_SERVER_ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(tmp_line == "")
|
||||
{
|
||||
Connection->Response.SendError(HTTP_INTERNAL_SERVER_ERROR);
|
||||
return false;
|
||||
}
|
||||
ParseHeader(tmp_line);
|
||||
}
|
||||
// Other Methods
|
||||
if(Connection->Method == M_DELETE || Connection->Method == M_PUT || Connection->Method == M_TRACE)
|
||||
{
|
||||
//todo: implement
|
||||
aprintf("HTTP Method not implemented :%d\n",Connection->Method);
|
||||
Connection->Response.SendError(HTTP_NOT_IMPLEMENTED);
|
||||
return false;
|
||||
}
|
||||
// handle POST (read header & body)
|
||||
if(Connection->Method == M_POST)
|
||||
{
|
||||
Connection->Response.Write("HTTP/1.1 100 Continue\r\n\r\n"); // POST Requests requires CONTINUE in HTTP/1.1
|
||||
return HandlePost();
|
||||
}
|
||||
// if you are here, something went wrong
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Parse the start-line
|
||||
// from RFC2616 / 5.1 Request-Line (start-line):
|
||||
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF (SP=Space)
|
||||
//
|
||||
// Determine Reqest-Method, URL, HTTP-Version and Split Parameters
|
||||
// Split URL into path, filename, fileext .. UrlData[]
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserverRequest::ParseStartLine(std::string start_line)
|
||||
{
|
||||
std::string method,url,http,tmp;
|
||||
|
||||
log_level_printf(8,"<ParseStartLine>: line: %s\n", start_line.c_str() );
|
||||
if(ySplitString(start_line," ",method,tmp))
|
||||
{
|
||||
if(ySplitStringLast(tmp," ",url,Connection->httprotocol))
|
||||
{
|
||||
analyzeURL(url);
|
||||
UrlData["httprotocol"] = Connection->httprotocol;
|
||||
// determine http Method
|
||||
if(method.compare("POST") == 0) Connection->Method = M_POST;
|
||||
else if(method.compare("GET") == 0) Connection->Method = M_GET;
|
||||
else if(method.compare("PUT") == 0) Connection->Method = M_PUT;
|
||||
else if(method.compare("HEAD") == 0) Connection->Method = M_HEAD;
|
||||
else if(method.compare("PUT") == 0) Connection->Method = M_PUT;
|
||||
else if(method.compare("DELETE") == 0) Connection->Method = M_DELETE;
|
||||
else if(method.compare("TRACE") == 0) Connection->Method = M_TRACE;
|
||||
else
|
||||
{
|
||||
log_level_printf(1,"Unknown Method or invalid request\n");
|
||||
Connection->Response.SendError(HTTP_NOT_IMPLEMENTED);
|
||||
log_level_printf(3,"Request: '%s'\n",rawbuffer.c_str());
|
||||
return false;
|
||||
}
|
||||
log_level_printf(3,"Request: FullURL: %s\n",UrlData["fullurl"].c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// parse parameter string
|
||||
// parameter = attribute "=" value
|
||||
// attribute = token
|
||||
// value = token | quoted-string
|
||||
//
|
||||
// If parameter attribute is multiple times given, the values are stored like this:
|
||||
// <attribute>=<value1>,<value2>,..,<value n>
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserverRequest::ParseParams(std::string param_string)
|
||||
{
|
||||
bool ende = false;
|
||||
std::string param, name="", value, number;
|
||||
|
||||
while(!ende)
|
||||
{
|
||||
if(!ySplitStringExact(param_string,"&",param,param_string))
|
||||
ende = true;
|
||||
if(ySplitStringExact(param,"=",name,value))
|
||||
{
|
||||
value = trim(value);
|
||||
if(ParameterList[name].empty())
|
||||
ParameterList[name] = value;
|
||||
else
|
||||
{
|
||||
ParameterList[name] += ",";
|
||||
ParameterList[name] += value;
|
||||
}
|
||||
}
|
||||
number = string_printf("%d", ParameterList.size()+1);
|
||||
log_level_printf(7,"ParseParams: name: %s value: %s\n",name.c_str(), value.c_str());
|
||||
ParameterList[number] = name;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// parse the header of the request
|
||||
// from RFC 2616 / 4.2 Message Header:
|
||||
// message-header = field-name ":" [ field-value ]
|
||||
// field-name = token
|
||||
// field-value = *( field-content | LWS )
|
||||
// field-content = <the OCTETs making up the field-value
|
||||
// and consisting of either *TEXT or combinations
|
||||
// of token, separators, and quoted-string>
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserverRequest::ParseHeader(std::string header)
|
||||
{
|
||||
bool ende = false;
|
||||
std::string sheader, name, value;
|
||||
HeaderList.clear();
|
||||
|
||||
while(!ende)
|
||||
{
|
||||
if(!ySplitStringExact(header,"\r\n",sheader,header))
|
||||
ende = true;
|
||||
if(ySplitStringExact(sheader,":",name,value))
|
||||
HeaderList[name] = trim(value);
|
||||
log_level_printf(8,"ParseHeader: name: %s value: %s\n",name.c_str(), value.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Analyze URL
|
||||
// Extract Filename, Path, FileExt
|
||||
// form RFC2616 / 3.2.2:
|
||||
// http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
|
||||
// query data is splitted and stored in ParameterList
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWebserverRequest::analyzeURL(std::string url)
|
||||
{
|
||||
ParameterList.clear();
|
||||
// URI decode
|
||||
url = decodeString(url);
|
||||
url = trim(url, "\r\n"); // non-HTTP-Standard: allow \r or \n in URL. Delete it.
|
||||
UrlData["fullurl"] = url;
|
||||
// split Params
|
||||
if(ySplitString(url,"?",UrlData["url"],UrlData["paramstring"])) // split pure URL and all Params
|
||||
ParseParams(UrlData["paramstring"]); // split params to ParameterList
|
||||
else // No Params
|
||||
UrlData["url"] = url;
|
||||
|
||||
if(!ySplitStringLast(UrlData["url"],"/",UrlData["path"],UrlData["filename"]))
|
||||
{
|
||||
UrlData["path"] = "/"; // Set "/" if not contained
|
||||
}
|
||||
else
|
||||
UrlData["path"] += "/";
|
||||
if(( UrlData["url"].length() == 1) || (UrlData["url"][UrlData["url"].length()-1] == '/' ))
|
||||
{ // if "/" at end use index.html
|
||||
UrlData["path"] = UrlData["url"];
|
||||
UrlData["filename"] = "index.html";
|
||||
}
|
||||
ySplitStringLast(UrlData["filename"],".",UrlData["filenamepure"],UrlData["fileext"]);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Handle Post (Form and ONE file upload)
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserverRequest::HandlePost()
|
||||
{
|
||||
//read header: line by line
|
||||
std::string raw_header, tmp_line;
|
||||
do
|
||||
{
|
||||
tmp_line = Connection->sock->ReceiveLine();
|
||||
if(tmp_line == "") // Socket empty
|
||||
{
|
||||
log_level_printf(1,"HandleRequest: (Header) End of line not found: %s\n", strerror(errno));
|
||||
Connection->Response.SendError(HTTP_INTERNAL_SERVER_ERROR);
|
||||
return false;
|
||||
}
|
||||
raw_header.append(tmp_line);
|
||||
}
|
||||
while(tmp_line != "\r\n"); // header ends with first empty line
|
||||
ParseHeader(raw_header);
|
||||
|
||||
// read meesage body
|
||||
unsigned int content_len = 0;
|
||||
if(HeaderList["Content-Length"] != "")
|
||||
content_len = atoi( HeaderList["Content-Length"].c_str() );
|
||||
|
||||
// Get Rest of Request from Socket
|
||||
log_level_printf(2,"Connection->Method Post !\n");
|
||||
log_level_printf(9,"Conntent Type:%s\n",(HeaderList["Content-Type"]).c_str());
|
||||
log_level_printf(8,"Post Content-Length:%d as string:(%s)\n", content_len, HeaderList["Content-Length"].c_str());
|
||||
|
||||
static const std::string t = "multipart/form-data; boundary=";
|
||||
if(HeaderList["Content-Type"].compare(0,t.length(),t) == 0) // this a a multpart POST, normallly: file upload
|
||||
{
|
||||
#ifdef Y_CONFIG_FEATURE_UPLOAD
|
||||
std::string boundary = "--" + HeaderList["Content-Type"].substr(t.length(),HeaderList["Content-Type"].length() - t.length());
|
||||
std::string post_header;
|
||||
do
|
||||
{
|
||||
content_len = HandlePostBoundary(boundary, content_len);
|
||||
}
|
||||
while(content_len > 0);
|
||||
#else
|
||||
Connection->Response.SendError(HTTP_NOT_IMPLEMENTED);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
else if(HeaderList["Content-Type"].compare("application/x-www-form-urlencoded") == 0) //this is a normal POST with form-data (no upload)
|
||||
{
|
||||
// handle normal form POST
|
||||
log_level_printf(6,"Handle POST application/x-www-form-urlencoded\n");
|
||||
std::string post_header;
|
||||
// get message-body
|
||||
post_header = Connection->sock->ReceiveBlock();
|
||||
if(post_header.length() < content_len)
|
||||
{
|
||||
aprintf("POST form less data then expected\n");
|
||||
Connection->Response.SendError(HTTP_INTERNAL_SERVER_ERROR);
|
||||
return false;
|
||||
}
|
||||
// parse the params in post_header (message-body) an add them to ParameterList
|
||||
ParseParams(post_header);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// POST multipart ! FILE UPLOAD!
|
||||
//
|
||||
// No 'Content-type: multipart/mixed' now supported
|
||||
// designed for recursion for different boundaries.
|
||||
//
|
||||
// from RFC 1867:
|
||||
// 2. HTML forms with file submission
|
||||
//
|
||||
// The current HTML specification defines eight possible values for the
|
||||
// attribute TYPE of an INPUT element: CHECKBOX, HIDDEN, IMAGE,
|
||||
// PASSWORD, RADIO, RESET, SUBMIT, TEXT.
|
||||
//
|
||||
// In addition, it defines the default ENCTYPE attribute of the FORM
|
||||
// element using the POST METHOD to have the default value
|
||||
// "application/x-www-form-urlencoded"
|
||||
//
|
||||
// 6. Examples
|
||||
//
|
||||
// Suppose the server supplies the following HTML:
|
||||
//
|
||||
// <FORM ACTION="http://server.dom/cgi/handle"
|
||||
// ENCTYPE="multipart/form-data"
|
||||
// METHOD=POST>
|
||||
// What is your name? <INPUT TYPE=TEXT NAME=submitter>
|
||||
// What files are you sending? <INPUT TYPE=FILE NAME=pics>
|
||||
// </FORM>
|
||||
//
|
||||
// and the user types "Joe Blow" in the name field, and selects a text
|
||||
// file "file1.txt" for the answer to 'What files are you sending?'
|
||||
//
|
||||
// The client might send back the following data:
|
||||
//
|
||||
// Content-type: multipart/form-data, boundary=AaB03x
|
||||
//
|
||||
// --AaB03x
|
||||
// content-disposition: form-data; name="field1"
|
||||
//
|
||||
// Joe Blow
|
||||
// --AaB03x
|
||||
// content-disposition: form-data; name="pics"; filename="file1.txt"
|
||||
// Content-Type: text/plain
|
||||
//
|
||||
// ... contents of file1.txt ...
|
||||
// --AaB03x--
|
||||
//
|
||||
// 7. Registration of multipart/form-data
|
||||
//
|
||||
// The media-type multipart/form-data follows the rules of all multipart
|
||||
// MIME data streams as outlined in RFC 1521. It is intended for use in
|
||||
// returning the data that comes about from filling out a form. In a
|
||||
// form (in HTML, although other applications may also use forms), there
|
||||
// are a series of fields to be supplied by the user who fills out the
|
||||
// form. Each field has a name. Within a given form, the names are
|
||||
// unique.
|
||||
//
|
||||
// multipart/form-data contains a series of parts. Each part is expected
|
||||
// to contain a content-disposition header where the value is "form-
|
||||
// data" and a name attribute specifies the field name within the form,
|
||||
// e.g., 'content-disposition: form-data; name="xxxxx"', where xxxxx is
|
||||
// the field name corresponding to that field. Field names originally in
|
||||
// non-ASCII character sets may be encoded using the method outlined in
|
||||
// RFC 1522.
|
||||
//
|
||||
// As with all multipart MIME types, each part has an optional Content-
|
||||
// Type which defaults to text/plain. If the contents of a file are
|
||||
// returned via filling out a form, then the file input is identified as
|
||||
// application/octet-stream or the appropriate media type, if known. If
|
||||
// multiple files are to be returned as the result of a single form
|
||||
// entry, they can be returned as multipart/mixed embedded within the
|
||||
// multipart/form-data.
|
||||
//
|
||||
// Each part may be encoded and the "content-transfer-encoding" header
|
||||
// supplied if the value of that part does not conform to the default
|
||||
// encoding.
|
||||
//
|
||||
// File inputs may also identify the file name. The file name may be
|
||||
// described using the 'filename' parameter of the "content-disposition"
|
||||
// header. This is not required, but is strongly recommended in any case
|
||||
// where the original filename is known. This is useful or necessary in
|
||||
// many applications.
|
||||
//-----------------------------------------------------------------------------
|
||||
unsigned int CWebserverRequest::HandlePostBoundary(std::string boundary, unsigned int content_len)
|
||||
{
|
||||
std::string tmp_line;
|
||||
|
||||
// read boundary
|
||||
tmp_line = Connection->sock->ReceiveLine();
|
||||
content_len -= tmp_line.length();
|
||||
|
||||
log_level_printf(2,"<POST Boundary> Start\n");
|
||||
if(tmp_line.find(boundary) != std::string::npos)
|
||||
{
|
||||
// is it the boudary end?
|
||||
if(tmp_line.find(boundary+"--") != std::string::npos)
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> Boundary END found\n");
|
||||
return 0;
|
||||
}
|
||||
log_level_printf(7,"<POST Boundary> Boundary START found\n");
|
||||
|
||||
// read content-disposition: ...
|
||||
tmp_line = Connection->sock->ReceiveLine();
|
||||
content_len -= tmp_line.length();
|
||||
if(tmp_line.find("Content-Disposition:") == std::string::npos)
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> no content-disposition found. line:(%s)\n", tmp_line.c_str());
|
||||
return 0;
|
||||
}
|
||||
if(tmp_line.find("filename") != std::string::npos)
|
||||
{
|
||||
#ifdef Y_CONFIG_FEATURE_UPLOAD
|
||||
// this part is a file
|
||||
log_level_printf(2,"<POST Boundary> disposition !!this is a file!! found. line:(%s)\n", tmp_line.c_str());
|
||||
// get para from 'content-disposition: form-data; name="pics"; filename="file1.txt"'
|
||||
// set to ParameterList["<name>"]="<filename>"
|
||||
std::string left, right, var_name, var_value;
|
||||
if(!ySplitStringExact(tmp_line, "name=\"", left, right))
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> no var_name START found. line:(%s)\n", tmp_line.c_str());
|
||||
return 0;
|
||||
}
|
||||
if(!ySplitStringExact(right, "\"", var_name, right))
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> no var_name END found. line:(%s)\n", tmp_line.c_str());
|
||||
return 0;
|
||||
}
|
||||
if(!ySplitStringExact(right, "filename=\"", left, right))
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> no filename START found. line:(%s)\n", tmp_line.c_str());
|
||||
return 0;
|
||||
}
|
||||
if(!ySplitStringExact(right, "\"", var_value, right))
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> no filename END found. line:(%s)\n", tmp_line.c_str());
|
||||
return 0;
|
||||
}
|
||||
var_value = trim(var_value);
|
||||
ParameterList[var_name] = var_value;
|
||||
log_level_printf(7,"<POST Boundary> filename found. name:(%s) value:(%s)\n", var_name.c_str(), var_value.c_str());
|
||||
|
||||
//read 'Content-Type: <mime>'
|
||||
tmp_line = Connection->sock->ReceiveLine();
|
||||
content_len -= tmp_line.length();
|
||||
// Get Content-Type: put it to ParameterList["<name>_mime"]="<mime>"
|
||||
if(!ySplitStringExact(tmp_line, "Content-Type:", left, right))
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> no Content-Type found. line:(%s)\n", tmp_line.c_str());
|
||||
return 0;
|
||||
}
|
||||
var_value = trim(right);
|
||||
ParameterList[var_name+"_mime"] = var_value;
|
||||
log_level_printf(7,"<POST Boundary> Content-Type found. name:(%s_mime) value:(%s)\n", var_name.c_str(), var_value.c_str());
|
||||
|
||||
|
||||
//read empty line as separator
|
||||
tmp_line = Connection->sock->ReceiveLine();
|
||||
content_len -= tmp_line.length();
|
||||
if(tmp_line != "\r\n")
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> no empty line found. line:(%s)\n", tmp_line.c_str());
|
||||
return 0;
|
||||
|
||||
}
|
||||
log_level_printf(7,"<POST Boundary> read file Start\n");
|
||||
|
||||
std::string upload_filename;
|
||||
upload_filename = UPLOAD_TMP_FILE;
|
||||
// Hook for Filename naming
|
||||
Connection->HookHandler.Hooks_UploadSetFilename(upload_filename);
|
||||
// Set upload filename to ParameterList["<name>_upload_filename"]="<upload_filename>"
|
||||
ParameterList[var_name+"_upload_filename"] = upload_filename;
|
||||
|
||||
// open file for write
|
||||
int fd = open(upload_filename.c_str(), O_WRONLY|O_CREAT|O_TRUNC);
|
||||
if (fd<=0)
|
||||
{
|
||||
aprintf("cannot open file %s: ", upload_filename.c_str());
|
||||
dperror("");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ASSUMPTION: the complete multipart has no more then SEARCH_BOUNDARY_LEN bytes after the file.
|
||||
// It only works, if no multipart/mixed is used (e.g. in file attachments). Not nessesary in embedded systems.
|
||||
// To speed up uploading, read content_len - SEARCH_BOUNDARY_LEN bytes in blockmode.
|
||||
// To save memory, write them direct into the file.
|
||||
#define SEARCH_BOUNDARY_LEN 2*RECEIVE_BLOCK_LEN // >= RECEIVE_BLOCK_LEN in ySocket
|
||||
unsigned int _readbytes = 0;
|
||||
if((int)content_len - SEARCH_BOUNDARY_LEN >0)
|
||||
{
|
||||
_readbytes = Connection->sock->ReceiveFileGivenLength(fd, content_len - SEARCH_BOUNDARY_LEN);
|
||||
content_len -= _readbytes;
|
||||
log_level_printf(8,"<POST Boundary> read block (already:%d all:%d)\n", _readbytes, content_len);
|
||||
}
|
||||
|
||||
// read rest of file and check for boundary end
|
||||
_readbytes = 0;
|
||||
bool is_CRLF = false;
|
||||
|
||||
bool found_end_boundary = false;
|
||||
do
|
||||
{
|
||||
// read line by line
|
||||
tmp_line = Connection->sock->ReceiveLine();
|
||||
_readbytes += tmp_line.length();
|
||||
|
||||
// is this line a boundary?
|
||||
if(tmp_line.find(boundary) != std::string::npos)
|
||||
{
|
||||
if(tmp_line.find(boundary+"--") != std::string::npos)
|
||||
found_end_boundary = true; // it is the end! of POST request!
|
||||
break; // boundary found. end of file.
|
||||
}
|
||||
else // no Boundary: write CRFL if found in last line
|
||||
{
|
||||
if(is_CRLF)
|
||||
if ((unsigned int)write(fd, "\r\n", 2) != 2)
|
||||
{
|
||||
perror("write file failed\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// normal line: write it to file
|
||||
// CRLF at end? Maybe CRLF before boundary. Can not decide yet
|
||||
is_CRLF = (tmp_line.length()>=2 && tmp_line[tmp_line.length()-2]=='\r' && tmp_line[tmp_line.length()-1]=='\n');
|
||||
int write_len = is_CRLF ? tmp_line.length()-2 : tmp_line.length();
|
||||
if (write(fd, tmp_line.c_str(), write_len) != write_len)
|
||||
{
|
||||
perror("write file failed\n");
|
||||
return 0;
|
||||
}
|
||||
log_level_printf(2,"<POST Boundary> read file (already:%d all:%d)\n", _readbytes, content_len);
|
||||
}
|
||||
while((_readbytes < content_len) && (tmp_line.length() != 0));
|
||||
content_len -= _readbytes;
|
||||
close(fd);
|
||||
log_level_printf(2,"<POST Boundary> read file End\n");
|
||||
if(found_end_boundary) // upload ok?
|
||||
{
|
||||
|
||||
Connection->HookHandler.Hooks_UploadReady(upload_filename);
|
||||
return 0;
|
||||
}
|
||||
#endif // Y_CONFIG_FEATURE_UPLOAD
|
||||
}
|
||||
else
|
||||
// this part is a POST variable/parameter
|
||||
{
|
||||
// get var_name from 'content-disposition: form-data; name="var_name"'
|
||||
std::string left, right, var_name, var_value;
|
||||
if(!ySplitStringExact(tmp_line, "name=\"", left, right))
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> no var_name START found. line:(%s)\n", tmp_line.c_str());
|
||||
return 0;
|
||||
}
|
||||
if(!ySplitStringExact(right, "\"", var_name, right))
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> no var_name END found. line:(%s)\n", tmp_line.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
//read empty line as separator
|
||||
tmp_line = Connection->sock->ReceiveLine();
|
||||
content_len -= tmp_line.length();
|
||||
if(tmp_line != "\r\n")
|
||||
{
|
||||
log_level_printf(7,"<POST Boundary> no empty line found. line:(%s)\n", tmp_line.c_str());
|
||||
return 0;
|
||||
|
||||
}
|
||||
//read var_value line
|
||||
// ASSUMPTION!!!! Only one Line for value, new line is a boundary again
|
||||
// ATTENTION!! var_name must not be unique. So Parameters are store by number too.
|
||||
var_value = Connection->sock->ReceiveLine();
|
||||
content_len -= tmp_line.length();
|
||||
var_value = trim(var_value);
|
||||
ParameterList[var_name] = var_value;
|
||||
log_level_printf(7,"<POST Boundary> Parameter found. name:(%s) value:(%s)\n", var_name.c_str(), var_value.c_str());
|
||||
}
|
||||
}
|
||||
return content_len;
|
||||
}
|
52
src/nhttpd/yhttpd_core/yrequest.h
Normal file
52
src/nhttpd/yhttpd_core/yrequest.h
Normal file
@@ -0,0 +1,52 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Request
|
||||
//=============================================================================
|
||||
|
||||
#ifndef __yhttpd_request_h__
|
||||
#define __yhttpd_request_h__
|
||||
|
||||
// C++
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
// yhttpd
|
||||
#include "yconfig.h"
|
||||
#include "ytypes_globals.h"
|
||||
|
||||
// forward declaration
|
||||
class CWebserver;
|
||||
class CWebserverConnection;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWebserverRequest
|
||||
{
|
||||
private:
|
||||
bool HandlePost();
|
||||
unsigned int HandlePostBoundary(std::string boundary, unsigned int content_len);
|
||||
bool ParseStartLine(std::string start_line);
|
||||
bool ParseParams(std::string param_string);
|
||||
bool ParseHeader(std::string header);
|
||||
|
||||
protected:
|
||||
std::string rawbuffer;
|
||||
|
||||
// request control
|
||||
public:
|
||||
class CWebserver *Webserver;
|
||||
class CWebserverConnection *Connection;
|
||||
|
||||
// Request Data
|
||||
CStringList ParameterList;
|
||||
CStringList HeaderList;
|
||||
CStringList UrlData; // url, fullurl, path, filename, fileext, paramstring
|
||||
void analyzeURL(std::string);
|
||||
|
||||
// constructor & destructor
|
||||
CWebserverRequest() {};
|
||||
CWebserverRequest(CWebserver *pWebserver);
|
||||
// Handler
|
||||
bool HandleRequest(void);
|
||||
};
|
||||
|
||||
#endif /* __yhttpd_request_h__ */
|
251
src/nhttpd/yhttpd_core/yresponse.cpp
Normal file
251
src/nhttpd/yhttpd_core/yresponse.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Response
|
||||
//=============================================================================
|
||||
|
||||
// c
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <errno.h>
|
||||
// c++
|
||||
#include <string.h>
|
||||
// system
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
// yhttpd
|
||||
#include "yconfig.h"
|
||||
#include "yhttpd.h"
|
||||
#include "ytypes_globals.h"
|
||||
#include "ylogging.h"
|
||||
#include "ywebserver.h"
|
||||
#include "yconnection.h"
|
||||
#include "helper.h"
|
||||
#include "yhook.h"
|
||||
|
||||
#ifdef Y_CONFIG_HAVE_SENDFILE
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Constructor & Destructor
|
||||
//=============================================================================
|
||||
CWebserverResponse::CWebserverResponse(CWebserver *pWebserver)
|
||||
{
|
||||
Webserver = pWebserver;
|
||||
CWebserverResponse();
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
CWebserverResponse::CWebserverResponse()
|
||||
{
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Main Dispacher for Response
|
||||
// To understand HOOKS reade yhook.cpp Comments!!!
|
||||
//-----------------------------------------------------------------------------
|
||||
// RFC 2616 / 6 Response
|
||||
//
|
||||
// After receiving and interpreting a request message, a server responds
|
||||
// with an HTTP response message.
|
||||
// Response =Status-Line ; generated by SendHeader
|
||||
// *(( general-header ; generated by SendHeader
|
||||
// | response-header ; generated by SendHeader
|
||||
// | entity-header ) CRLF); generated by SendHeader
|
||||
// CRLF ; generated by SendHeader
|
||||
// [ message-body ] ; by HOOK Handling Loop or Sendfile
|
||||
//=============================================================================
|
||||
bool CWebserverResponse::SendResponse()
|
||||
{
|
||||
// Init Hookhandler
|
||||
Connection->HookHandler.session_init(Connection->Request.ParameterList, Connection->Request.UrlData,
|
||||
(Connection->Request.HeaderList), (Cyhttpd::ConfigList), Connection->Method, Connection->keep_alive);
|
||||
//--------------------------------------------------------------
|
||||
// HOOK Handling Loop [ PREPARE response hook ]
|
||||
// Checking and Preperation: Auth, static, cache, ...
|
||||
//--------------------------------------------------------------
|
||||
|
||||
// move to mod_sendfile ???
|
||||
#ifdef Y_CONFIG_USE_HOSTEDWEB
|
||||
// for hosted webs: rewrite URL
|
||||
std::string _hosted="/hosted/";
|
||||
if((Connection->Request.UrlData["path"]).compare(0,_hosted.length(),"/hosted/") == 0) // hosted Web ?
|
||||
Connection->Request.UrlData["path"]=Cyhttpd::ConfigList["HostedDocumentRoot"]
|
||||
+(Connection->Request.UrlData["path"]).substr(_hosted.length()-1);
|
||||
#endif //Y_CONFIG_USE_HOSTEDWEB
|
||||
|
||||
do
|
||||
{
|
||||
if(Connection->RequestCanceled)
|
||||
return false;
|
||||
|
||||
Connection->HookHandler.Hooks_PrepareResponse();
|
||||
if(Connection->HookHandler.status == HANDLED_ERROR || Connection->HookHandler.status == HANDLED_ABORT)
|
||||
{
|
||||
log_level_printf(2,"Response Prepare Hook found but Error\n");
|
||||
Write(Connection->HookHandler.BuildHeader());
|
||||
Write(Connection->HookHandler.yresult);
|
||||
return false;
|
||||
}
|
||||
// URL has new value. Analyze new URL for SendFile
|
||||
else if(Connection->HookHandler.status == HANDLED_SENDFILE ||
|
||||
Connection->HookHandler.status == HANDLED_REWRITE)
|
||||
{
|
||||
Connection->Request.analyzeURL(Connection->HookHandler.NewURL);
|
||||
Connection->HookHandler.UrlData = Connection->Request.UrlData;
|
||||
}
|
||||
if(Connection->HookHandler.status == HANDLED_REDIRECTION)
|
||||
{
|
||||
Write(Connection->HookHandler.BuildHeader());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while(Connection->HookHandler.status == HANDLED_REWRITE);
|
||||
|
||||
// Prepare = NOT_MODIFIED ?
|
||||
if(Connection->HookHandler.httpStatus == HTTP_NOT_MODIFIED)
|
||||
{
|
||||
Write(Connection->HookHandler.BuildHeader());
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// HOOK Handling Loop [ response hook ]
|
||||
// Production
|
||||
//--------------------------------------------------------------
|
||||
if(Connection->HookHandler.status != HANDLED_SENDFILE)
|
||||
do
|
||||
{
|
||||
if(Connection->RequestCanceled)
|
||||
return false;
|
||||
|
||||
Connection->HookHandler.Hooks_SendResponse();
|
||||
if((Connection->HookHandler.status == HANDLED_READY)||(Connection->HookHandler.status == HANDLED_CONTINUE))
|
||||
{
|
||||
log_level_printf(2,"Response Hook Output. Status:%d\n", Connection->HookHandler.status);
|
||||
Write(Connection->HookHandler.BuildHeader());
|
||||
if(Connection->Method != M_HEAD)
|
||||
Write(Connection->HookHandler.yresult);
|
||||
if(Connection->HookHandler.status != HANDLED_CONTINUE)
|
||||
return true;
|
||||
}
|
||||
else if(Connection->HookHandler.status == HANDLED_ERROR)
|
||||
{
|
||||
log_level_printf(2,"Response Hook found but Error\n");
|
||||
Write(Connection->HookHandler.BuildHeader());
|
||||
if(Connection->Method != M_HEAD)
|
||||
Write(Connection->HookHandler.yresult);
|
||||
return false;
|
||||
}
|
||||
else if(Connection->HookHandler.status == HANDLED_ABORT)
|
||||
return false;
|
||||
// URL has new value. Analyze new URL for SendFile
|
||||
else if(Connection->HookHandler.status == HANDLED_SENDFILE ||
|
||||
Connection->HookHandler.status == HANDLED_REWRITE)
|
||||
{
|
||||
Connection->Request.analyzeURL(Connection->HookHandler.NewURL);
|
||||
Connection->HookHandler.UrlData = Connection->Request.UrlData;
|
||||
}
|
||||
if(Connection->HookHandler.status == HANDLED_REDIRECTION)
|
||||
{
|
||||
Write(Connection->HookHandler.BuildHeader());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while(Connection->HookHandler.status == HANDLED_REWRITE);
|
||||
|
||||
// Send static file
|
||||
if(Connection->HookHandler.status == HANDLED_SENDFILE && !Connection->RequestCanceled)
|
||||
{
|
||||
bool cache = true;
|
||||
// if(Connection->HookHandler.UrlData["path"] == "/tmp/")//TODO: un-cachable dirs
|
||||
// cache = false;
|
||||
Write(Connection->HookHandler.BuildHeader(cache));
|
||||
if(Connection->Method != M_HEAD)
|
||||
Sendfile(Connection->Request.UrlData["url"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// arrived here? = error!
|
||||
SendError(HTTP_NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Output
|
||||
//=============================================================================
|
||||
void CWebserverResponse::SendHeader(HttpResponseType responseType, bool cache, std::string ContentType)
|
||||
{
|
||||
Connection->HookHandler.SetHeader(responseType, ContentType);
|
||||
Write(Connection->HookHandler.BuildHeader(cache));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// BASIC Send over Socket for Strings (char*)
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserverResponse::WriteData(char const * data, long length)
|
||||
{
|
||||
if(Connection->RequestCanceled)
|
||||
return false;
|
||||
if(Connection->sock->Send(data, length) == -1)
|
||||
{
|
||||
log_level_printf(1,"response canceled: %s\n", strerror(errno));
|
||||
Connection->RequestCanceled = true;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
#define bufferlen 4*1024
|
||||
void CWebserverResponse::printf ( const char *fmt, ... )
|
||||
{
|
||||
char buffer[bufferlen];
|
||||
va_list arglist;
|
||||
va_start( arglist, fmt );
|
||||
vsnprintf( buffer, bufferlen, fmt, arglist );
|
||||
va_end(arglist);
|
||||
Write(buffer);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserverResponse::Write(char const *text)
|
||||
{
|
||||
return WriteData(text, strlen(text));
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserverResponse::WriteLn(char const *text)
|
||||
{
|
||||
if(!WriteData(text, strlen(text)))
|
||||
return false;
|
||||
return WriteData("\r\n",2);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserverResponse::Sendfile(std::string filename)
|
||||
{
|
||||
if(Connection->RequestCanceled)
|
||||
return false;
|
||||
int filed = open( filename.c_str(), O_RDONLY );
|
||||
if(filed != -1 ) //can access file?
|
||||
{
|
||||
if(!Connection->sock->SendFile(filed))
|
||||
Connection->RequestCanceled = true;
|
||||
close(filed);
|
||||
}
|
||||
return (filed != -1 );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Send File: Determine MIME-Type fro File-Extention
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string CWebserverResponse::GetContentType(std::string ext)
|
||||
{
|
||||
std::string ctype = "text/plain";
|
||||
ext = string_tolower(ext);
|
||||
for (unsigned int i = 0;i < (sizeof(MimeFileExtensions)/sizeof(MimeFileExtensions[0])); i++)
|
||||
if (MimeFileExtensions[i].fileext == ext)
|
||||
{
|
||||
ctype = MimeFileExtensions[i].mime;
|
||||
break;
|
||||
}
|
||||
return ctype;
|
||||
}
|
56
src/nhttpd/yhttpd_core/yresponse.h
Normal file
56
src/nhttpd/yhttpd_core/yresponse.h
Normal file
@@ -0,0 +1,56 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Response
|
||||
//=============================================================================
|
||||
|
||||
#ifndef __yhttpd_response_h__
|
||||
#define __yhttpd_response_h__
|
||||
|
||||
// c++
|
||||
#include <string>
|
||||
// yhttpd
|
||||
#include "yconfig.h"
|
||||
#include "ytypes_globals.h"
|
||||
#include "yhook.h"
|
||||
|
||||
// forward declaration
|
||||
class CWebserver;
|
||||
class CWebserverConnection;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWebserverResponse
|
||||
{
|
||||
private:
|
||||
|
||||
protected:
|
||||
bool WriteData(char const *data, long length);
|
||||
bool Sendfile(std::string filename);
|
||||
std::string redirectURI; // URI for redirection else: empty
|
||||
|
||||
public:
|
||||
class CWebserver *Webserver;
|
||||
class CWebserverConnection *Connection;
|
||||
|
||||
// con/destructors
|
||||
CWebserverResponse();
|
||||
CWebserverResponse(CWebserver *pWebserver);
|
||||
|
||||
// response control
|
||||
bool SendResponse(void);
|
||||
|
||||
// output methods
|
||||
void printf(const char *fmt, ...);
|
||||
bool Write(char const *text);
|
||||
bool WriteLn(char const *text);
|
||||
bool Write(const std::string text) { return Write(text.c_str()); }
|
||||
bool WriteLn(const std::string text) { return WriteLn(text.c_str()); }
|
||||
|
||||
// Headers
|
||||
void SendError(HttpResponseType responseType) {SendHeader(responseType, false, "text/html");}
|
||||
void SendHeader(HttpResponseType responseType, bool cache=false, std::string ContentType="text/html");
|
||||
|
||||
// Helpers
|
||||
std::string GetContentType(std::string ext);
|
||||
};
|
||||
|
||||
#endif /* __yhttpd_response_h__ */
|
504
src/nhttpd/yhttpd_core/ysocket.cpp
Normal file
504
src/nhttpd/yhttpd_core/ysocket.cpp
Normal file
@@ -0,0 +1,504 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Socket Class : Basic Socket Operations
|
||||
//=============================================================================
|
||||
|
||||
#include <cstring>
|
||||
|
||||
// system
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
// yhttpd
|
||||
#include "yhttpd.h"
|
||||
#include "ysocket.h"
|
||||
#include "ylogging.h"
|
||||
// system
|
||||
#ifdef Y_CONFIG_HAVE_SENDFILE
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rand.h>
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Initialization of static variables
|
||||
//=============================================================================
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
SSL_CTX *CySocket::SSL_ctx;
|
||||
std::string CySocket::SSL_pemfile;
|
||||
std::string CySocket::SSL_CA_file;
|
||||
#endif
|
||||
|
||||
//=============================================================================
|
||||
// Constructor & Destructor & Initialization
|
||||
//=============================================================================
|
||||
CySocket::CySocket()
|
||||
: sock(0)
|
||||
{
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
ssl = NULL;
|
||||
#endif
|
||||
tv_start_waiting.tv_sec = 0;
|
||||
tv_start_waiting.tv_usec = 0;
|
||||
BytesSend =0;
|
||||
sock = socket(AF_INET,SOCK_STREAM,0);
|
||||
init();
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
CySocket::~CySocket()
|
||||
{
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
if(isSSLSocket && ssl != NULL)
|
||||
SSL_free(ssl);
|
||||
#endif
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// initialize
|
||||
//-----------------------------------------------------------------------------
|
||||
void CySocket::init(void)
|
||||
{
|
||||
handling = false;
|
||||
isOpened = false;
|
||||
isValid = true;
|
||||
addr_len = sizeof(addr);
|
||||
memset(&addr, 0, addr_len);
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
isSSLSocket = false;
|
||||
#endif
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Initialize this socket as a SSL-socket
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
|
||||
bool CySocket::initAsSSL(void)
|
||||
{
|
||||
isSSLSocket = true;
|
||||
if (NULL == (ssl = SSL_new(CySocket::SSL_ctx))) // create SSL-socket
|
||||
{
|
||||
aprintf("ySocket:SSL Error: Create SSL_new : %s\n", ERR_error_string(ERR_get_error(), NULL) );
|
||||
return false;
|
||||
}
|
||||
SSL_set_accept_state(ssl); // accept connection
|
||||
if(1 != (SSL_set_fd(ssl, sock))) // associate socket descriptor
|
||||
if (NULL == (ssl = SSL_new(CySocket::SSL_ctx)))
|
||||
{
|
||||
aprintf("ySocket:SSL Error: Create SSL_new : %s\n", ERR_error_string(ERR_get_error(), NULL) );
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// static: initialize the SSL-Library and the SSL ctx object.
|
||||
// Read and assoziate the keyfile.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CySocket::initSSL(void)
|
||||
{
|
||||
SSL_load_error_strings(); // Load SSL Error Strings
|
||||
SSL_library_init(); // Load SSL Library
|
||||
if (0 == RAND_status()) // set Random
|
||||
{
|
||||
aprintf("ySocket:SSL got no rand\n");
|
||||
return false;
|
||||
}
|
||||
if((SSL_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) // create master ctx
|
||||
{
|
||||
aprintf("ySocket:SSL Error: Create SSL_CTX_new : %s\n", ERR_error_string(ERR_get_error(), NULL) );
|
||||
return false;
|
||||
}
|
||||
if(SSL_pemfile == "")
|
||||
{
|
||||
aprintf("ySocket:SSL Error: no pemfile given\n");
|
||||
return false;
|
||||
}
|
||||
if(SSL_CA_file != "") // have a CA?
|
||||
if(1 != SSL_CTX_load_verify_locations(SSL_ctx, SSL_CA_file.c_str(), NULL))
|
||||
{
|
||||
aprintf("ySocket:SSL Error: %s CA-File:%s\n",ERR_error_string(ERR_get_error(), NULL), SSL_CA_file.c_str());
|
||||
return false;
|
||||
}
|
||||
if(SSL_CTX_use_certificate_file(SSL_ctx, SSL_pemfile.c_str(), SSL_FILETYPE_PEM) < 0)
|
||||
{
|
||||
aprintf("ySocket:SSL Error: %s PEM-File:%s\n",ERR_error_string(ERR_get_error(), NULL), SSL_pemfile.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(SSL_CTX_use_PrivateKey_file(SSL_ctx, SSL_pemfile.c_str(), SSL_FILETYPE_PEM) < 0)
|
||||
{
|
||||
aprintf("ySocket:SSL Error: Private Keys: %s PEM-File:%s\n",ERR_error_string(ERR_get_error(), NULL), SSL_pemfile.c_str());
|
||||
return false;
|
||||
}
|
||||
if(SSL_CTX_check_private_key(SSL_ctx) != 1)
|
||||
{
|
||||
aprintf("ySocket:SSL Error: Private Keys not compatile to public keys: %s PEM-File:%s\n",ERR_error_string(ERR_get_error(), NULL), SSL_pemfile.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
//=============================================================================
|
||||
// Socket handling
|
||||
//=============================================================================
|
||||
void CySocket::close(void)
|
||||
{
|
||||
if(sock != 0 && sock != INVALID_SOCKET)
|
||||
::close(sock);
|
||||
#ifndef Y_CONFIG_FEATURE_KEEP_ALIVE
|
||||
sock = 0;
|
||||
#endif
|
||||
isOpened = false;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void CySocket::shutdown(void)
|
||||
{
|
||||
if(sock != 0 && sock != INVALID_SOCKET)
|
||||
::shutdown(sock,SHUT_RDWR);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CySocket::listen(int port, int max_connections)
|
||||
{
|
||||
if(sock == INVALID_SOCKET)
|
||||
return false;
|
||||
|
||||
// set sockaddr for listening
|
||||
sockaddr_in sai;
|
||||
memset(&sai, 0, sizeof(sai));
|
||||
sai.sin_family = AF_INET; // Protocol
|
||||
sai.sin_addr.s_addr = htonl(INADDR_ANY); // No Filter
|
||||
sai.sin_port = htons(port); // Listening Port
|
||||
|
||||
set_reuse_port(); // Re-Use Port
|
||||
set_reuse_addr(); // Re-Use IP
|
||||
if(bind(sock, (sockaddr *)&sai, sizeof(sockaddr_in)) != SOCKET_ERROR)
|
||||
if(::listen(sock, max_connections) == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
CySocket* CySocket::accept()
|
||||
{
|
||||
init();
|
||||
SOCKET newSock = ::accept(sock, (sockaddr *) &addr, &addr_len);
|
||||
if(newSock == INVALID_SOCKET)
|
||||
{
|
||||
dperror("accept: invalid socket\n");
|
||||
return NULL;
|
||||
}
|
||||
CySocket *new_ySocket = new CySocket(newSock);
|
||||
if(new_ySocket != NULL)
|
||||
{
|
||||
new_ySocket->setAddr(addr);
|
||||
#ifdef TCP_CORK
|
||||
new_ySocket->set_option(IPPROTO_TCP, TCP_CORK);
|
||||
#else
|
||||
set_tcp_nodelay();
|
||||
#endif
|
||||
}
|
||||
new_ySocket->isOpened = true;
|
||||
// handling = true;
|
||||
return new_ySocket;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string CySocket::get_client_ip(void)
|
||||
{
|
||||
return inet_ntoa(addr.sin_addr);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
int CySocket::get_accept_port(void)
|
||||
{
|
||||
return (int)ntohs(addr.sin_port);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
void CySocket::setAddr(sockaddr_in _addr)
|
||||
{
|
||||
addr = _addr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Set Socket Option (return = false = error)
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CySocket::set_option(int typ, int option)
|
||||
{
|
||||
int on = 1;
|
||||
return (setsockopt(sock, typ, option, (char *)&on, sizeof(on)) >= 0);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Set Re-Use Option for Port.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CySocket::set_reuse_port()
|
||||
{
|
||||
#ifdef SO_REUSEPORT
|
||||
if(!set_option(SOL_SOCKET, SO_REUSEPORT))
|
||||
dperror("setsockopt(SO_REUSEPORT)\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Set Re-Use Option for Address.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CySocket::set_reuse_addr()
|
||||
{
|
||||
#ifdef SO_REUSEADDR
|
||||
if(!set_option(SOL_SOCKET, SO_REUSEADDR))
|
||||
dperror("setsockopt(SO_REUSEADDR)\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Set Keep-Alive Option for Socket.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CySocket::set_keep_alive()
|
||||
{
|
||||
#ifdef SO_KEEPALIVE
|
||||
if(!set_option(SOL_SOCKET, SO_KEEPALIVE))
|
||||
dperror("setsockopt(SO_KEEPALIVE)\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Set Keep-Alive Option for Socket.
|
||||
//-----------------------------------------------------------------------------
|
||||
void CySocket::set_tcp_nodelay()
|
||||
{
|
||||
#ifdef TCP_NODELAY
|
||||
if(!set_option(IPPROTO_TCP, TCP_NODELAY))
|
||||
dperror("setsockopt(SO_KEEPALIVE)\n");
|
||||
#endif
|
||||
}
|
||||
//=============================================================================
|
||||
// Send and receive
|
||||
//=============================================================================
|
||||
//-----------------------------------------------------------------------------
|
||||
// Read a buffer (normal or SSL)
|
||||
//-----------------------------------------------------------------------------
|
||||
int CySocket::Read(char *buffer, unsigned int length)
|
||||
{
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
if(isSSLSocket)
|
||||
return SSL_read(ssl, buffer, length);
|
||||
else
|
||||
#endif
|
||||
return ::read(sock, buffer, length);
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Send a buffer (normal or SSL)
|
||||
//-----------------------------------------------------------------------------
|
||||
int CySocket::Send(char const *buffer, unsigned int length)
|
||||
{
|
||||
unsigned int len = 0;
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
if(isSSLSocket)
|
||||
len = SSL_write(ssl, buffer, length);
|
||||
else
|
||||
#endif
|
||||
len = ::send(sock, buffer, length, MSG_NOSIGNAL);
|
||||
if(len >= 0)
|
||||
BytesSend += len;
|
||||
return len;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Check if Socket was closed by client
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CySocket::CheckSocketOpen()
|
||||
{
|
||||
char buffer[32];
|
||||
|
||||
#ifdef CONFIG_SYSTEM_CYGWIN
|
||||
return !(recv(sock, buffer, sizeof(buffer), MSG_PEEK | MSG_NOSIGNAL) == 0);
|
||||
#else
|
||||
return !(recv(sock, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Aggregated Send- and Receive- Operations
|
||||
//=============================================================================
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// BASIC Send File over Socket for FILE*
|
||||
// fd is an opened FILE-Descriptor
|
||||
//-----------------------------------------------------------------------------
|
||||
int CySocket::SendFile(int filed)
|
||||
{
|
||||
if(!isValid)
|
||||
return false;
|
||||
#ifdef Y_CONFIG_HAVE_SENDFILE
|
||||
// does not work with SSL !!!
|
||||
off_t start = 0;
|
||||
off_t end = lseek(filed,0,SEEK_END);
|
||||
int written = 0;
|
||||
if((written = ::sendfile(sock,filed,&start,end)) == -1)
|
||||
{
|
||||
perror("sendfile failed\n");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
BytesSend += written;
|
||||
#else
|
||||
char sbuf[1024];
|
||||
unsigned int r=0;
|
||||
while ((r=read(filed, sbuf, 1024)) > 0)
|
||||
{
|
||||
if (Send(sbuf, r) < 0)
|
||||
{
|
||||
perror("sendfile failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif // Y_CONFIG_HAVE_SENDFILE
|
||||
log_level_printf(9,"<Sock:SendFile>: Bytes:%ld\n", BytesSend);
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Receive File over Socket for FILE* filed
|
||||
// read/write in small blocks (to save memory).
|
||||
// usind sleep for sync input
|
||||
// fd is an opened FILE-Descriptor
|
||||
//-----------------------------------------------------------------------------
|
||||
//TODO: Write upload Progress Informations into a file
|
||||
unsigned int CySocket::ReceiveFileGivenLength(int filed, unsigned int _length)
|
||||
{
|
||||
unsigned int _readbytes = 0;
|
||||
char buffer[RECEIVE_BLOCK_LEN];
|
||||
int retries=0;
|
||||
|
||||
do
|
||||
{
|
||||
// check bytes in Socket buffer
|
||||
u_long readarg = 0;
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
if(isSSLSocket)
|
||||
readarg = RECEIVE_BLOCK_LEN;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if(ioctl(sock, FIONREAD, &readarg) != 0)// How many bytes avaiable on socket?
|
||||
break;
|
||||
if(readarg > RECEIVE_BLOCK_LEN) // enough bytes to read
|
||||
readarg = RECEIVE_BLOCK_LEN; // read only given length
|
||||
}
|
||||
if(readarg == 0) // nothing to read: sleep
|
||||
{
|
||||
retries++;
|
||||
if(retries >NON_BLOCKING_MAX_RETRIES)
|
||||
break;
|
||||
sleep(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
int bytes_gotten = Read(buffer, readarg);
|
||||
|
||||
if(bytes_gotten == -1 && errno == EINTR)// non-blocking
|
||||
continue;
|
||||
if(bytes_gotten <= 0) // ERROR Code gotten or Conection closed by peer
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
_readbytes += bytes_gotten;
|
||||
if (write(filed, buffer, bytes_gotten) != bytes_gotten)
|
||||
{
|
||||
perror("write file failed\n");
|
||||
return 0;
|
||||
}
|
||||
retries = 0;
|
||||
if(bytes_gotten < NON_BLOCKING_TRY_BYTES) // to few bytes gotten: sleep
|
||||
sleep(1);
|
||||
}
|
||||
log_level_printf(8,"Receive Block length:%d all:%d\n",_readbytes, _length);
|
||||
}
|
||||
while(_readbytes + RECEIVE_BLOCK_LEN < _length);
|
||||
return _readbytes;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// read all data avaiable on Socket
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string CySocket::ReceiveBlock()
|
||||
{
|
||||
std::string result = "";
|
||||
char buffer[RECEIVE_BLOCK_LEN];
|
||||
|
||||
if(!isValid || !isOpened)
|
||||
return "";
|
||||
// signal(SIGALRM, ytimeout);
|
||||
alarm(1);
|
||||
|
||||
while(true)
|
||||
{
|
||||
// check bytes in Socket buffer
|
||||
u_long readarg = 0;
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
if(isSSLSocket)
|
||||
readarg = RECEIVE_BLOCK_LEN;
|
||||
else
|
||||
#endif
|
||||
// determine bytes that can be read
|
||||
{
|
||||
if(ioctl(sock, FIONREAD, &readarg) != 0)
|
||||
break;
|
||||
if(readarg == 0) // nothing to read
|
||||
break;
|
||||
if(readarg > RECEIVE_BLOCK_LEN) // need more loops
|
||||
readarg = RECEIVE_BLOCK_LEN;
|
||||
}
|
||||
// Read data
|
||||
int bytes_gotten = Read(buffer, readarg);
|
||||
|
||||
if(bytes_gotten == -1 && errno == EINTR)// non-blocking
|
||||
continue;
|
||||
if(bytes_gotten <= 0) // ERROR Code gotten or Conection closed by peer
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
result.append(buffer, bytes_gotten);
|
||||
if((u_long)bytes_gotten < readarg) // no more bytes
|
||||
break;
|
||||
}
|
||||
alarm(0);
|
||||
signal(SIGALRM,SIG_IGN);
|
||||
return result;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Read on line (Ends with LF) or maximum MAX_LINE_BUFFER chars
|
||||
// Result Contains [CR]LF!
|
||||
//-----------------------------------------------------------------------------
|
||||
std::string CySocket::ReceiveLine()
|
||||
{
|
||||
char buffer[MAX_LINE_BUFFER];
|
||||
int bytes_gotten = 0;
|
||||
std::string result="";
|
||||
|
||||
while(true)
|
||||
{
|
||||
// read one char
|
||||
if(Read(buffer+bytes_gotten, 1) == 1)
|
||||
{
|
||||
if(buffer[bytes_gotten] == '\n')
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if(bytes_gotten < MAX_LINE_BUFFER-1)
|
||||
bytes_gotten ++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
buffer[++bytes_gotten] = '\0';
|
||||
result.assign(buffer, bytes_gotten);
|
||||
|
||||
return result;
|
||||
}
|
98
src/nhttpd/yhttpd_core/ysocket.h
Normal file
98
src/nhttpd/yhttpd_core/ysocket.h
Normal file
@@ -0,0 +1,98 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Socket Class : Basic Socket Operations
|
||||
//-----------------------------------------------------------------------------
|
||||
// Socket-Handler
|
||||
//=============================================================================
|
||||
#ifndef __yhttpd_ysocket_h__
|
||||
#define __yhttpd_ysocket_h__
|
||||
|
||||
// system
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include "yconfig.h"
|
||||
#include "ytypes_globals.h"
|
||||
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
//-----------------------------------------------------------------------------
|
||||
// Some common socket definitions
|
||||
#ifndef SOCKET
|
||||
#define SOCKET int
|
||||
#endif
|
||||
#ifndef INVALID_SOCKET
|
||||
#define INVALID_SOCKET (SOCKET)(~0)
|
||||
#define SOCKET_ERROR (-1)
|
||||
#endif
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#define NON_BLOCKING_MAX_RETRIES 10 // Retries, if receive buffer is empty
|
||||
#define NON_BLOCKING_TRY_BYTES 512 // try to catch min bytes .. else sleep
|
||||
#define RECEIVE_BLOCK_LEN 1024 // receiving block length
|
||||
#define MAX_LINE_BUFFER 1024*2 // Max length of one line
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CySocket
|
||||
{
|
||||
public:
|
||||
// constructor & destructor
|
||||
CySocket();
|
||||
CySocket(SOCKET s):sock(s){init();};
|
||||
virtual ~CySocket(void);
|
||||
void init(void); // some extra initialization
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
bool initAsSSL(void); // initialize this socket as a SSL-socket
|
||||
static bool initSSL(void); // global initialize of SSL-Library, CTX and cerificate/key-file
|
||||
static std::string SSL_pemfile; // "server.pem" file with SSL-Certificate (now: only one certificate at all)
|
||||
static std::string SSL_CA_file; // CA Certificate (optional)
|
||||
#endif
|
||||
// Socket handling
|
||||
bool handling; // true: Socket yet is handled by a Connection Thread
|
||||
bool isOpened; // is this socket open?
|
||||
bool isValid; // false on Socket Errors. Must close.
|
||||
struct timeval tv_start_waiting; // Put keep-alive Socket to Wait-Queue
|
||||
|
||||
void close(void); // Close Socket
|
||||
void shutdown(void); // Shutdown Socket
|
||||
bool listen(int port, int max_connections); // Listen on Port for max Slave Connections
|
||||
CySocket* accept(); // Wait for Connection. Returns "slave" Socket
|
||||
void setAddr(sockaddr_in _clientaddr);
|
||||
std::string get_client_ip(void); // Get IP from Client
|
||||
SOCKET get_socket(){return sock;} // Return "C" Socket-ID
|
||||
int get_accept_port(void); // Get Port for accepted connection
|
||||
|
||||
// send & receive (basic)
|
||||
int Read(char *buffer, unsigned int length); // Read a buffer (normal or SSL)
|
||||
int Send(char const *buffer, unsigned int length); // Send a buffer (normal or SSL)
|
||||
bool CheckSocketOpen(); // check if socket was closed by client
|
||||
|
||||
// send & receive
|
||||
int SendFile(int filed); // Send a File
|
||||
std::string ReceiveBlock(); // receive a Block. Look at length
|
||||
unsigned int ReceiveFileGivenLength(int filed, unsigned int _length); // Receive File of given length
|
||||
std::string ReceiveLine(); // receive until "\n"
|
||||
|
||||
protected:
|
||||
long BytesSend; // Bytes send over Socket
|
||||
bool set_option(int typ, int option); // Set Socket Options
|
||||
void set_reuse_port(); // Set Reuse Port Option for Socket
|
||||
void set_reuse_addr(); // Set Reuse Address Option for Socket
|
||||
void set_keep_alive(); // Set Keep-Alive Option for Socket
|
||||
void set_tcp_nodelay();
|
||||
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
bool isSSLSocket; // This is a SSL based Socket
|
||||
static SSL_CTX *SSL_ctx; // Global SSL ctx object
|
||||
SSL *ssl; // ssl habdler for this socket
|
||||
#endif
|
||||
private:
|
||||
std::string receive_buffer; // buffer for receive from socket
|
||||
sockaddr_in addr; // "slave" Client Socket Data
|
||||
socklen_t addr_len; // Length of addr struct
|
||||
SOCKET sock; // "C" Socket-ID
|
||||
};
|
||||
#endif // __yhttpd_ysocket_h__
|
157
src/nhttpd/yhttpd_core/ytypes_globals.h
Normal file
157
src/nhttpd/yhttpd_core/ytypes_globals.h
Normal file
@@ -0,0 +1,157 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Type Definitions and Global Variables
|
||||
//=============================================================================
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef __yhttpd_types_globals_h__
|
||||
#define __yhttpd_types_globals_h__
|
||||
|
||||
// c++
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
//=============================================================================
|
||||
// Global Types
|
||||
//=============================================================================
|
||||
typedef std::map<std::string, std::string> CStringList;
|
||||
typedef std::vector<std::string> CStringArray;
|
||||
typedef std::vector<std::string> CStringVector;
|
||||
|
||||
//=============================================================================
|
||||
// HTTP Protocol Definitions
|
||||
//=============================================================================
|
||||
// HTTP-Status codes
|
||||
typedef enum
|
||||
{
|
||||
HTTP_NIL = -1,
|
||||
HTTP_CONTINUE = 100,
|
||||
HTTP_SWITCHING_PROTOCOLS = 101, // not used
|
||||
HTTP_OK = 200, // use: everything is right. request (and response) handled
|
||||
HTTP_CREATED = 201, // not used
|
||||
HTTP_ACCEPTED = 202, // use: idicate accept of request. request take more time.
|
||||
HTTP_NON_AUTHORITATIVE_INFO = 203,
|
||||
HTTP_NO_CONTENT = 204,
|
||||
HTTP_RESET_CONTENT = 205, // not used
|
||||
HTTP_PARTIAL_CONTENT = 206, // not used
|
||||
HTTP_MULTIBLE_CHOICES = 300, // not used
|
||||
HTTP_MOVED_PERMANENTLY = 301,
|
||||
HTTP_MOVED_TEMPORARILY = 302,
|
||||
HTTP_SEE_OTHER = 303, // not used
|
||||
HTTP_NOT_MODIFIED = 304,
|
||||
HTTP_USE_PROXY = 305, // not used
|
||||
HTTP_TEMPORARY_REDIRECT = 307, // not used
|
||||
HTTP_BAD_REQUEST = 400,
|
||||
HTTP_UNAUTHORIZED = 401,
|
||||
HTTP_PAYMENT_REQUIRED = 402, // not used
|
||||
HTTP_FORBIDDEN = 403,
|
||||
HTTP_NOT_FOUND = 404,
|
||||
HTTP_METHOD_NOT_ALLOWED = 405, // not used
|
||||
HTTP_NOT_ACCEPTABLE = 406, // not used
|
||||
HTTP_PROXY_AUTHENTICATION_REQUIRED= 407, // not used
|
||||
HTTP_REQUEST_TIMEOUT = 408, // not used
|
||||
HTTP_CONFLICT = 409, // not used
|
||||
HTTP_GONE = 410, // not used
|
||||
HTTP_LENGTH_REQUIRED = 411, // not used
|
||||
HTTP_PRECONDITION_FAILED = 412, // not used
|
||||
HTTP_REQUEST_ENTITY_TOO_LARGE = 413, // not used
|
||||
HTTP_REQUEST_URI_TOO_LARGE = 414, // not used
|
||||
HTTP_UNSUPPORTED_MEDIA_TYPE = 415, // not used
|
||||
HTTP_REQUEST_RANGE_NOT_SATISFIABLE= 416, // not used
|
||||
HTTP_EXPECTAION_FAILED = 417, // not used
|
||||
HTTP_INTERNAL_SERVER_ERROR = 500,
|
||||
HTTP_NOT_IMPLEMENTED = 501,
|
||||
HTTP_BAD_GATEWAY = 502, // not used
|
||||
HTTP_SERVICE_UNAVAILABLE = 503, // not used
|
||||
HTTP_GATEWAY_TIMEOUT = 504, // not used
|
||||
HTTP_HTTP_VERSION_NOT_SUPPORTED = 505, // not used
|
||||
} HttpResponseType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HttpResponseType type;
|
||||
const char *name;
|
||||
const char *info;
|
||||
} HttpEnumString;
|
||||
|
||||
static const HttpEnumString httpResponseNames[] = {
|
||||
{ HTTP_CONTINUE, "Continue" },
|
||||
{ HTTP_SWITCHING_PROTOCOLS, "Switching Protocols" },
|
||||
{ HTTP_OK, "OK" },
|
||||
{ HTTP_CREATED, "Created" },
|
||||
{ HTTP_ACCEPTED, "Accepted" },
|
||||
{ HTTP_NON_AUTHORITATIVE_INFO, "No Authorative Info" },
|
||||
{ HTTP_NO_CONTENT, "No Content" },
|
||||
{ HTTP_RESET_CONTENT, "Reset Content" },
|
||||
{ HTTP_PARTIAL_CONTENT, "Partial Content" },
|
||||
{ HTTP_MULTIBLE_CHOICES, "Multiple Choices" },
|
||||
{ HTTP_MOVED_PERMANENTLY, "Moved Permanently" },
|
||||
{ HTTP_MOVED_TEMPORARILY, "Moved Temporarily" },
|
||||
{ HTTP_SEE_OTHER, "See Other" },
|
||||
{ HTTP_NOT_MODIFIED, "Not Modified" },
|
||||
{ HTTP_USE_PROXY, "Use Proxy" },
|
||||
{ HTTP_TEMPORARY_REDIRECT, "Temporary Redirect" },
|
||||
{ HTTP_BAD_REQUEST, "Bad Request", "Unsupported method." },
|
||||
{ HTTP_UNAUTHORIZED, "Unauthorized", "Access denied." },
|
||||
{ HTTP_PAYMENT_REQUIRED, "Payment Required" },
|
||||
{ HTTP_FORBIDDEN, "Forbidden", "" },
|
||||
{ HTTP_NOT_FOUND, "Not Found", "The requested URL was not found on this server." },
|
||||
{ HTTP_METHOD_NOT_ALLOWED, "Method Not Allowed" },
|
||||
{ HTTP_NOT_ACCEPTABLE, "Not Acceptable" },
|
||||
{ HTTP_PROXY_AUTHENTICATION_REQUIRED,"Proxy Authentication Required" },
|
||||
{ HTTP_REQUEST_TIMEOUT, "Request Time-out" },
|
||||
{ HTTP_CONFLICT, "Conflict" },
|
||||
{ HTTP_GONE, "Gone" },
|
||||
{ HTTP_LENGTH_REQUIRED, "Length Required" },
|
||||
{ HTTP_PRECONDITION_FAILED, "Precondition Failed" },
|
||||
{ HTTP_REQUEST_ENTITY_TOO_LARGE,"Request Entity Too Large" },
|
||||
{ HTTP_REQUEST_URI_TOO_LARGE, "Request-URI Too Large" },
|
||||
{ HTTP_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type" },
|
||||
{ HTTP_REQUEST_RANGE_NOT_SATISFIABLE,"Requested range not satisfiable" },
|
||||
{ HTTP_EXPECTAION_FAILED, "Expectation Failed" },
|
||||
{ HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error", "Internal Server Error" },
|
||||
{ HTTP_NOT_IMPLEMENTED, "Not Implemented", "The requested method is not recognized by this server." },
|
||||
{ HTTP_BAD_GATEWAY, "Bad Gateway" },
|
||||
{ HTTP_SERVICE_UNAVAILABLE, "Service Unavailable" },
|
||||
{ HTTP_GATEWAY_TIMEOUT, "Gateway Time-out" },
|
||||
{ HTTP_HTTP_VERSION_NOT_SUPPORTED,"HTTP Version not supported" },
|
||||
};
|
||||
|
||||
// HTTP-methods
|
||||
typedef enum
|
||||
{
|
||||
M_UNKNOWN = 0,
|
||||
M_POST = 1,
|
||||
M_GET,
|
||||
M_HEAD,
|
||||
M_PUT,
|
||||
M_DELETE,
|
||||
M_TRACE
|
||||
} THttp_Method;
|
||||
|
||||
// Date Format RFC 1123
|
||||
static const char RFC1123FMT[] = "%a, %d %b %Y %H:%M:%S GMT";
|
||||
|
||||
// mime-table
|
||||
typedef struct
|
||||
{
|
||||
const char *fileext;
|
||||
const char *mime;
|
||||
} TMimePair;
|
||||
static const TMimePair MimeFileExtensions[] = {
|
||||
{"xml", "text/xml"},
|
||||
{"htm", "text/html"},
|
||||
{"html", "text/html"},
|
||||
{"yhtm", "text/html"},
|
||||
{"yhtml", "text/html"},
|
||||
{"jpg", "image/jpeg"},
|
||||
{"jpeg", "image/jpeg"},
|
||||
{"gif", "image/gif"},
|
||||
{"png", "image/png"},
|
||||
{"txt", "image/plain"},
|
||||
{"css", "text/css"},
|
||||
|
||||
};
|
||||
|
||||
#endif // __yhttpd_types_globals_h__
|
507
src/nhttpd/yhttpd_core/ywebserver.cpp
Normal file
507
src/nhttpd/yhttpd_core/ywebserver.cpp
Normal file
@@ -0,0 +1,507 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Webserver Class : Until now: exact one instance
|
||||
//=============================================================================
|
||||
// c++
|
||||
#include <cerrno>
|
||||
#include <csignal>
|
||||
|
||||
// system
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
// tuxbox
|
||||
#include <configfile.h>
|
||||
|
||||
// yhttpd
|
||||
#include "yhttpd.h"
|
||||
#include "ytypes_globals.h"
|
||||
#include "ywebserver.h"
|
||||
#include "ylogging.h"
|
||||
#include "helper.h"
|
||||
#include "ysocket.h"
|
||||
#include "yconnection.h"
|
||||
#include "yrequest.h"
|
||||
|
||||
//=============================================================================
|
||||
// Initialization of static variables
|
||||
//=============================================================================
|
||||
bool CWebserver::is_threading = true;
|
||||
pthread_mutex_t CWebserver::mutex = PTHREAD_MUTEX_INITIALIZER;;
|
||||
|
||||
//=============================================================================
|
||||
// Constructor & Destructor & Initialization
|
||||
//=============================================================================
|
||||
CWebserver::CWebserver()
|
||||
{
|
||||
terminate = false;
|
||||
for(int i=0;i<HTTPD_MAX_CONNECTIONS;i++)
|
||||
{
|
||||
Connection_Thread_List[i] = (pthread_t)NULL;
|
||||
SocketList[i] = NULL;
|
||||
}
|
||||
FD_ZERO(&master); // initialize FD_SETs
|
||||
FD_ZERO(&read_fds);
|
||||
fdmax = 0;
|
||||
open_connections = 0;
|
||||
//pthread_attr_init(&attr);
|
||||
//pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
port=80;
|
||||
|
||||
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
CWebserver::~CWebserver()
|
||||
{
|
||||
listenSocket.close();
|
||||
}
|
||||
//=============================================================================
|
||||
// Start Webserver. Main-Loop.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Wait for Connection and schedule ist to handle_connection()
|
||||
// HTTP/1.1 should can handle "keep-alive" connections to reduce socket
|
||||
// creation and handling. This is handled using FD_SET and select Socket
|
||||
// mechanism.
|
||||
// select wait for socket-activity. Cases:
|
||||
// 1) get a new connection
|
||||
// 2) re-use a socket
|
||||
// 3) timeout: close unused sockets
|
||||
//-----------------------------------------------------------------------------
|
||||
// from RFC 2616:
|
||||
// 8 Connections
|
||||
// 8.1 Persistent Connections
|
||||
// 8.1.1 Purpose
|
||||
//
|
||||
// Prior to persistent connections, a separate TCP connection was
|
||||
// established to fetch each URL, increasing the load on HTTP servers
|
||||
// and causing congestion on the Internet. The use of inline images and
|
||||
// other associated data often require a client to make multiple
|
||||
// requests of the same server in a short amount of time. Analysis of
|
||||
// these performance problems and results from a prototype
|
||||
// implementation are available [26] [30]. Implementation experience and
|
||||
// measurements of actual HTTP/1.1 (RFC 2068) implementations show good
|
||||
// results [39]. Alternatives have also been explored, for example,
|
||||
// T/TCP [27].
|
||||
//
|
||||
// Persistent HTTP connections have a number of advantages:
|
||||
//
|
||||
// - By opening and closing fewer TCP connections, CPU time is saved
|
||||
// in routers and hosts (clients, servers, proxies, gateways,
|
||||
// tunnels, or caches), and memory used for TCP protocol control
|
||||
// blocks can be saved in hosts.
|
||||
//
|
||||
// - HTTP requests and responses can be pipelined on a connection.
|
||||
// Pipelining allows a client to make multiple requests without
|
||||
// waiting for each response, allowing a single TCP connection to
|
||||
// be used much more efficiently, with much lower elapsed time.
|
||||
//
|
||||
// - Network congestion is reduced by reducing the number of packets
|
||||
// caused by TCP opens, and by allowing TCP sufficient time to
|
||||
// determine the congestion state of the network.
|
||||
//
|
||||
// - Latency on subsequent requests is reduced since there is no time
|
||||
// spent in TCP's connection opening handshake.
|
||||
//
|
||||
// - HTTP can evolve more gracefully, since errors can be reported
|
||||
// without the penalty of closing the TCP connection. Clients using
|
||||
// future versions of HTTP might optimistically try a new feature,
|
||||
// but if communicating with an older server, retry with old
|
||||
// semantics after an error is reported.
|
||||
//
|
||||
// HTTP implementations SHOULD implement persistent connections.
|
||||
//=============================================================================
|
||||
#define MAX_TIMEOUTS_TO_CLOSE 10
|
||||
#define MAX_TIMEOUTS_TO_TEST 100
|
||||
bool CWebserver::run(void)
|
||||
{
|
||||
if(!listenSocket.listen(port, HTTPD_MAX_CONNECTIONS))
|
||||
{
|
||||
dperror("Socket cannot bind and listen. Abort.\n");
|
||||
return false;
|
||||
}
|
||||
#ifdef Y_CONFIG_FEATURE_KEEP_ALIVE
|
||||
|
||||
// initialize values for select
|
||||
int listener = listenSocket.get_socket();// Open Listener
|
||||
struct timeval tv; // timeout struct
|
||||
FD_SET(listener, &master); // add the listener to the master set
|
||||
fdmax = listener; // init max fd
|
||||
fcntl(listener, F_SETFD , O_NONBLOCK); // listener master socket non-blocking
|
||||
int timeout_counter = 0; // Counter for Connection Timeout checking
|
||||
int test_counter = 0; // Counter for Testing long running Connections
|
||||
|
||||
// main Webserver Loop
|
||||
while(!terminate)
|
||||
{
|
||||
// select : init vars
|
||||
read_fds = master; // copy it
|
||||
tv.tv_usec = 10000; // microsec: Timeout for select ! for re-use / keep-alive socket
|
||||
tv.tv_sec = 0; // seconds
|
||||
int fd = -1;
|
||||
|
||||
// select : wait for socket activity
|
||||
if(open_connections <= 0) // No open Connection. Wait in select.
|
||||
fd = select(fdmax+1,&read_fds, NULL, NULL, NULL);// wait for socket activity
|
||||
else
|
||||
fd = select(fdmax+1,&read_fds, NULL, NULL, &tv);// wait for socket activity or timeout
|
||||
|
||||
// too much to do : sleep
|
||||
if(open_connections >= HTTPD_MAX_CONNECTIONS-1)
|
||||
sleep(1);
|
||||
|
||||
// Socket Error?
|
||||
if(fd == -1 && errno != EINTR)
|
||||
{
|
||||
perror("select");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Socket Timeout?
|
||||
if(fd == 0)
|
||||
{
|
||||
// Testoutput for long living threads
|
||||
if(++test_counter >= MAX_TIMEOUTS_TO_TEST)
|
||||
{
|
||||
for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++)
|
||||
if(SocketList[j] != NULL) // here is a socket
|
||||
log_level_printf(2,"FD-TEST sock:%d handle:%d open:%d\n",SocketList[j]->get_socket(),
|
||||
SocketList[j]->handling,SocketList[j]->isOpened);
|
||||
test_counter=0;
|
||||
}
|
||||
// some connection closing previous missed?
|
||||
if(++timeout_counter >= MAX_TIMEOUTS_TO_CLOSE)
|
||||
{
|
||||
CloseConnectionSocketsByTimeout();
|
||||
timeout_counter=0;
|
||||
}
|
||||
continue; // main loop again
|
||||
}
|
||||
//----------------------------------------------------------------------------------------
|
||||
// Check all observed descriptors & check new or re-use Connections
|
||||
//----------------------------------------------------------------------------------------
|
||||
for(int i = listener; i <= fdmax; i++)
|
||||
{
|
||||
int slot = -1;
|
||||
if(FD_ISSET(i, &read_fds)) // Socket observed?
|
||||
{ // we got one!!
|
||||
if (i == listener) // handle new connections
|
||||
slot = AcceptNewConnectionSocket();
|
||||
else // Connection on an existing open Socket = reuse (keep-alive)
|
||||
{
|
||||
slot = SL_GetExistingSocket(i);
|
||||
if(slot>=0)
|
||||
log_level_printf(2,"FD: reuse con fd:%d\n",SocketList[slot]->get_socket());
|
||||
}
|
||||
// prepare Connection handling
|
||||
if(slot>=0)
|
||||
if(SocketList[slot] != NULL && !SocketList[slot]->handling && SocketList[slot]->isValid)
|
||||
{
|
||||
log_level_printf(2,"FD: START CON HANDLING con fd:%d\n",SocketList[slot]->get_socket());
|
||||
FD_CLR(SocketList[slot]->get_socket(), &master); // remove from master set
|
||||
SocketList[slot]->handling = true; // prepares for thread-handling
|
||||
if(!handle_connection(SocketList[slot]))// handle this activity
|
||||
{ // Can not handle more threads
|
||||
char httpstr[]=HTTP_PROTOCOL " 503 Service Unavailable\r\n\r\n";
|
||||
SocketList[slot]->Send(httpstr, strlen(httpstr));
|
||||
SL_CloseSocketBySlot(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}// for
|
||||
CloseConnectionSocketsByTimeout(); // Check connections to close
|
||||
|
||||
}//while
|
||||
#else
|
||||
while(!terminate)
|
||||
{
|
||||
CySocket *newConnectionSock;
|
||||
if(!(newConnectionSock = listenSocket.accept() )) //Now: Blocking wait
|
||||
{
|
||||
dperror("Socket accept error. Continue.\n");
|
||||
continue;
|
||||
}
|
||||
log_level_printf(3,"Socket connect from %s\n", (listenSocket.get_client_ip()).c_str() );
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
if(Cyhttpd::ConfigList["SSL"]=="true")
|
||||
newConnectionSock->initAsSSL(); // make it a SSL-socket
|
||||
#endif
|
||||
handle_connection(newConnectionSock);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
//=============================================================================
|
||||
// SocketList Handler
|
||||
//=============================================================================
|
||||
//-----------------------------------------------------------------------------
|
||||
// Accept new Connection
|
||||
//-----------------------------------------------------------------------------
|
||||
int CWebserver::AcceptNewConnectionSocket()
|
||||
{
|
||||
int slot = -1;
|
||||
CySocket *connectionSock = NULL;
|
||||
int newfd;
|
||||
|
||||
if(!(connectionSock = listenSocket.accept() )) // Blocking wait
|
||||
{
|
||||
dperror("Socket accept error. Continue.\n");
|
||||
delete connectionSock;
|
||||
return -1;
|
||||
}
|
||||
#ifdef Y_CONFIG_USE_OPEN_SSL
|
||||
if(Cyhttpd::ConfigList["SSL"]=="true")
|
||||
connectionSock->initAsSSL(); // make it a SSL-socket
|
||||
#endif
|
||||
log_level_printf(2,"FD: new con fd:%d on port:%d\n",connectionSock->get_socket(), connectionSock->get_accept_port());
|
||||
|
||||
// Add Socket to List
|
||||
slot = SL_GetFreeSlot();
|
||||
if(slot < 0)
|
||||
{
|
||||
connectionSock->close();
|
||||
aprintf("No free Slot in SocketList found. Open:%d\n",open_connections);
|
||||
}
|
||||
else
|
||||
{
|
||||
SocketList[slot] = connectionSock; // put it to list
|
||||
fcntl(connectionSock->get_socket() , F_SETFD , O_NONBLOCK); // set non-blocking
|
||||
open_connections++; // count open connectins
|
||||
newfd = connectionSock->get_socket();
|
||||
if (newfd > fdmax) // keep track of the maximum fd
|
||||
fdmax = newfd;
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Get Index for Socket from SocketList
|
||||
//-----------------------------------------------------------------------------
|
||||
int CWebserver::SL_GetExistingSocket(SOCKET sock)
|
||||
{
|
||||
int slot = -1;
|
||||
for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++)
|
||||
if(SocketList[j] != NULL // here is a socket
|
||||
&& SocketList[j]->get_socket() == sock) // we know that socket
|
||||
{
|
||||
slot = j;
|
||||
break;
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Get Index for free Slot in SocketList
|
||||
//-----------------------------------------------------------------------------
|
||||
int CWebserver::SL_GetFreeSlot()
|
||||
{
|
||||
int slot = -1;
|
||||
for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++)
|
||||
if(SocketList[j] == NULL) // here is a free slot
|
||||
{
|
||||
slot = j;
|
||||
break;
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Look for Sockets to close
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWebserver::CloseConnectionSocketsByTimeout()
|
||||
{
|
||||
CySocket *connectionSock = NULL;
|
||||
for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++)
|
||||
if(SocketList[j] != NULL // here is a socket
|
||||
&& !SocketList[j]->handling) // it is not handled
|
||||
{
|
||||
connectionSock = SocketList[j];
|
||||
SOCKET thisSocket = connectionSock->get_socket();
|
||||
bool shouldClose = true;
|
||||
|
||||
if(!connectionSock->isValid) // If not valid -> close
|
||||
; // close
|
||||
else if(connectionSock->tv_start_waiting.tv_sec != 0 || SocketList[j]->tv_start_waiting.tv_usec != 0)
|
||||
{ // calculate keep-alive timeout
|
||||
struct timeval tv_now;
|
||||
struct timezone tz_now;
|
||||
gettimeofday(&tv_now, &tz_now);
|
||||
long long tdiff = ((tv_now.tv_sec - connectionSock->tv_start_waiting.tv_sec) * 1000000
|
||||
+ (tv_now.tv_usec - connectionSock->tv_start_waiting.tv_usec));
|
||||
if(tdiff < HTTPD_KEEPALIVE_TIMEOUT || tdiff <0)
|
||||
shouldClose = false;
|
||||
}
|
||||
if(shouldClose)
|
||||
{
|
||||
log_level_printf(2,"FD: close con Timeout fd:%d\n",thisSocket);
|
||||
SL_CloseSocketBySlot(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Add Socket fd to FD_SET again (for select-handling)
|
||||
// Add start-time for waiting for connection re-use / keep-alive
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWebserver::addSocketToMasterSet(SOCKET fd)
|
||||
{
|
||||
int slot = SL_GetExistingSocket(fd); // get slot/index for fd
|
||||
if(slot<0)
|
||||
return;
|
||||
log_level_printf(2,"FD: add to master fd:%d\n",fd);
|
||||
struct timeval tv_now;
|
||||
struct timezone tz_now;
|
||||
gettimeofday(&tv_now, &tz_now);
|
||||
SocketList[slot]->tv_start_waiting = tv_now; // add keep-alive wait time
|
||||
FD_SET(fd, &master); // add fd to select-master-set
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Close (FD_SET handled) Socket
|
||||
// Clear it from SocketList
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWebserver::SL_CloseSocketBySlot(int slot)
|
||||
{
|
||||
open_connections--; // count open connections
|
||||
if(SocketList[slot] == NULL)
|
||||
return;
|
||||
SocketList[slot]->handling = false; // no handling anymore
|
||||
FD_CLR(SocketList[slot]->get_socket(), &master);// remove from master set
|
||||
SocketList[slot]->close(); // close the socket
|
||||
delete SocketList[slot]; // destroy ySocket
|
||||
SocketList[slot] = NULL; // free in list
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
// Thread Handling
|
||||
//=============================================================================
|
||||
//-----------------------------------------------------------------------------
|
||||
// Check if IP is allowed for keep-alive
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserver::CheckKeepAliveAllowedByIP(std::string client_ip)
|
||||
{
|
||||
pthread_mutex_lock( &mutex );
|
||||
bool do_keep_alive = true;
|
||||
CStringVector::const_iterator it = conf_no_keep_alive_ips.begin();
|
||||
while(it != conf_no_keep_alive_ips.end())
|
||||
{
|
||||
if(trim(*it) == client_ip)
|
||||
do_keep_alive = false;
|
||||
it++;
|
||||
}
|
||||
pthread_mutex_unlock( &mutex );
|
||||
return do_keep_alive;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Set Entry(number)to NULL in Threadlist
|
||||
//-----------------------------------------------------------------------------
|
||||
void CWebserver::clear_Thread_List_Number(int number)
|
||||
{
|
||||
pthread_mutex_lock( &mutex );
|
||||
if(number <HTTPD_MAX_CONNECTIONS)
|
||||
Connection_Thread_List[number] = (pthread_t)NULL;
|
||||
CloseConnectionSocketsByTimeout();
|
||||
pthread_mutex_unlock( &mutex );
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// A new Connection is established to newSock. Create a (threaded) Connection
|
||||
// and handle the Request.
|
||||
//-----------------------------------------------------------------------------
|
||||
bool CWebserver::handle_connection(CySocket *newSock)
|
||||
{
|
||||
void *WebThread(void *args); //forward declaration
|
||||
|
||||
// create arguments
|
||||
TWebserverConnectionArgs *newConn = new TWebserverConnectionArgs;
|
||||
newConn->ySock = newSock;
|
||||
newConn->ySock->handling = true;
|
||||
newConn->WebserverBackref = this;
|
||||
//newConn->is_treaded = is_threading;
|
||||
newConn->is_treaded = false;
|
||||
|
||||
int index = -1;
|
||||
if(0) /*if(is_threading) FIXME not work */
|
||||
{
|
||||
pthread_mutex_lock( &mutex );
|
||||
// look for free Thread slot
|
||||
for(int i=0;i<HTTPD_MAX_CONNECTIONS;i++)
|
||||
if(Connection_Thread_List[i] == (pthread_t)NULL)
|
||||
{
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
if(index == -1)
|
||||
{
|
||||
dperror("Maximum Connection-Threads reached\n");
|
||||
pthread_mutex_unlock( &mutex );
|
||||
return false;
|
||||
}
|
||||
newConn->thread_number = index; //remember Index of Thread slot (for clean up)
|
||||
|
||||
// Create an orphan Thread. It is not joinable anymore
|
||||
pthread_mutex_unlock( &mutex );
|
||||
|
||||
// start connection Thread
|
||||
if(pthread_create(&Connection_Thread_List[index], &attr, WebThread, (void *)newConn) != 0)
|
||||
dperror("Could not create Connection-Thread\n");
|
||||
}
|
||||
else // non threaded
|
||||
WebThread((void *)newConn);
|
||||
return ((index != -1) || !is_threading);
|
||||
}
|
||||
//-------------------------------------------------------------------------
|
||||
// Webserver-Thread for each connection
|
||||
//-------------------------------------------------------------------------
|
||||
void *WebThread(void *args)
|
||||
{
|
||||
CWebserverConnection *con;
|
||||
CWebserver *ws;
|
||||
TWebserverConnectionArgs *newConn = (TWebserverConnectionArgs *) args;
|
||||
ws = newConn->WebserverBackref;
|
||||
|
||||
bool is_threaded = newConn->is_treaded;
|
||||
if(is_threaded)
|
||||
log_level_printf(1,"++ Thread 0x06%X gestartet\n", (int) pthread_self());
|
||||
|
||||
if (!newConn) {
|
||||
dperror("WebThread called without arguments!\n");
|
||||
if(newConn->is_treaded)
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
// (1) create & init Connection
|
||||
con = new CWebserverConnection(ws);
|
||||
con->Request.UrlData["clientaddr"] = newConn->ySock->get_client_ip(); // TODO:here?
|
||||
con->sock = newConn->ySock; // give socket reference
|
||||
newConn->ySock->handling = true; // dont handle this socket now be webserver main loop
|
||||
|
||||
// (2) handle the connection
|
||||
con->HandleConnection();
|
||||
|
||||
// (3) end connection handling
|
||||
#ifdef Y_CONFIG_FEATURE_KEEP_ALIVE
|
||||
if(!con->keep_alive)
|
||||
log_level_printf(2,"FD SHOULD CLOSE sock:%d!!!\n",con->sock->get_socket());
|
||||
else
|
||||
ws->addSocketToMasterSet(con->sock->get_socket()); // add to master set
|
||||
#else
|
||||
delete newConn->ySock;
|
||||
#endif
|
||||
if(!con->keep_alive)
|
||||
con->sock->isValid = false;
|
||||
con->sock->handling = false; // socket can be handled by webserver main loop (select) again
|
||||
|
||||
// (4) end thread
|
||||
delete con;
|
||||
int thread_number = newConn->thread_number;
|
||||
delete newConn;
|
||||
if(is_threaded)
|
||||
{
|
||||
log_level_printf(1,"-- Thread 0x06%X beendet\n",(int)pthread_self());
|
||||
ws->clear_Thread_List_Number(thread_number);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
return NULL;
|
||||
}
|
91
src/nhttpd/yhttpd_core/ywebserver.h
Normal file
91
src/nhttpd/yhttpd_core/ywebserver.h
Normal file
@@ -0,0 +1,91 @@
|
||||
//=============================================================================
|
||||
// YHTTPD
|
||||
// Webserver Class : Until now: exact one instance
|
||||
//-----------------------------------------------------------------------------
|
||||
// The Webserver Class creates one "master" Listener Socket on given Port.
|
||||
// If a Connection is accepted, a new Socket ist used. The Webserver creates
|
||||
// a new Connection-Class witch is (normaly) Threaded. The Connection-Class
|
||||
// handles Request and Response.
|
||||
// For HTTP/1.1 permanent Connections (keep-alive) socket-multiplexing using
|
||||
// FD_SET and select() is implemented. Sockets are stored in SocketList and
|
||||
// can be reused for the connected client (to reduce socket handling overhead).
|
||||
// Reuse of connections is often done by pipelinig (read->send->read->send ...)
|
||||
//=============================================================================
|
||||
#ifndef __yhttpd_ywebserver_h__
|
||||
#define __yhttpd_ywebserver_h__
|
||||
|
||||
// system
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
// yhttpd
|
||||
#include "yconfig.h"
|
||||
#include "ytypes_globals.h"
|
||||
#include "ysocket.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWebserver; //forward declaration
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Arguments for Connection-Thread
|
||||
//-----------------------------------------------------------------------------
|
||||
typedef struct
|
||||
{
|
||||
CySocket *ySock; // Connection "Slave" Socket
|
||||
CWebserver *WebserverBackref; // Backreference to Webserver
|
||||
bool is_treaded; // Use threading?
|
||||
int thread_number; // Number of Thread (informational use)
|
||||
} TWebserverConnectionArgs;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// until now: One Instance for one hosted Web
|
||||
//-----------------------------------------------------------------------------
|
||||
class CWebserver
|
||||
{
|
||||
private:
|
||||
static pthread_mutex_t mutex;
|
||||
pthread_t Connection_Thread_List[HTTPD_MAX_CONNECTIONS]; //Thread-List per webserver
|
||||
fd_set master; // master file descriptor list for select()
|
||||
fd_set read_fds; // read file descriptor list for select()
|
||||
int fdmax; // maximum file descriptor number
|
||||
CySocket *SocketList[HTTPD_MAX_CONNECTIONS]; // List of concurrent hadled connections
|
||||
int open_connections; // Number of opened connections
|
||||
pthread_attr_t attr; // pthread Attributes for deattach
|
||||
protected:
|
||||
bool terminate; // flag: indicate to terminate the Webserver
|
||||
CySocket listenSocket; // Master Socket for listening
|
||||
unsigned int port; // Port to listen on
|
||||
bool handle_connection(CySocket *newSock); // Create a new Connection Instance and handle Connection
|
||||
|
||||
// Connection Socket handling
|
||||
int AcceptNewConnectionSocket(); // Start new Socket connection
|
||||
void CloseConnectionSocketsByTimeout(); // Check Sockets to close
|
||||
void SL_CloseSocketBySlot(int slot); // Close socket by slot index
|
||||
int SL_GetExistingSocket(SOCKET sock); // look for socket reuse
|
||||
int SL_GetFreeSlot(); // get free slot
|
||||
|
||||
|
||||
public:
|
||||
static bool is_threading; // Use Threading for new Connections
|
||||
CStringVector conf_no_keep_alive_ips; // List of IP for NO keep-alive
|
||||
|
||||
// constructor & destructor
|
||||
CWebserver();
|
||||
~CWebserver(void);
|
||||
|
||||
void init(unsigned int _port, bool _is_threading) // Initialize Webserver Settings
|
||||
{port=_port; is_threading=_is_threading;}
|
||||
void set_conf_no_keep_alive_ips(CStringVector _conf_no_keep_alive_ips)
|
||||
{conf_no_keep_alive_ips=_conf_no_keep_alive_ips;}
|
||||
bool run(void); // Start the Webserver
|
||||
void stop(void) // Stop the Webserver
|
||||
{terminate=true;};
|
||||
|
||||
// public for WebTread
|
||||
void clear_Thread_List_Number(int number); // Set Entry(number)to NULL in Threadlist
|
||||
void addSocketToMasterSet(SOCKET fd); // add Socket to select master set
|
||||
bool CheckKeepAliveAllowedByIP(std::string client_ip); // Check if IP is allowed for keep-alive
|
||||
};
|
||||
|
||||
#endif // __yhttpd_ywebserver_h__
|
Reference in New Issue
Block a user