From 54cee7878503c6d738761e4d7780627a2910cc96 Mon Sep 17 00:00:00 2001 From: Fernando Rozenblit Date: Wed, 21 Aug 2019 00:37:47 +0200 Subject: [PATCH 1/5] Improved network code used by http and aux servers --- .../network/singlepollers/pollingwithpoll.h | 2 +- pol-core/clib/network/wnsckt.cpp | 146 +++++++----------- pol-core/clib/network/wnsckt.h | 2 +- 3 files changed, 58 insertions(+), 92 deletions(-) diff --git a/pol-core/clib/network/singlepollers/pollingwithpoll.h b/pol-core/clib/network/singlepollers/pollingwithpoll.h index 93023a33f7..0c613f9614 100644 --- a/pol-core/clib/network/singlepollers/pollingwithpoll.h +++ b/pol-core/clib/network/singlepollers/pollingwithpoll.h @@ -8,7 +8,7 @@ #ifdef _WIN32 // compatibility wrapper for windows -int poll( struct pollfd* fds, ULONG nfds, int timeout ) +inline int poll( struct pollfd* fds, ULONG nfds, int timeout ) { return WSAPoll( fds, nfds, timeout ); }; diff --git a/pol-core/clib/network/wnsckt.cpp b/pol-core/clib/network/wnsckt.cpp index 10c5bfb27f..65fac9260a 100644 --- a/pol-core/clib/network/wnsckt.cpp +++ b/pol-core/clib/network/wnsckt.cpp @@ -14,6 +14,8 @@ #include "passert.h" #include "strutil.h" +#include "singlepoller.h" + #if defined( WINDOWS ) #define SOCKET_ERRNO( x ) WSA##x #define socket_errno WSAGetLastError() @@ -103,7 +105,7 @@ std::string Socket::getpeername() const { struct sockaddr client_addr; // inet_addr socklen_t addrlen = sizeof client_addr; - if ( ::getpeername( _sck, &client_addr, &addrlen ) == 0 ) + if (::getpeername( _sck, &client_addr, &addrlen ) == 0 ) { struct sockaddr_in* in_addr = (struct sockaddr_in*)&client_addr; if ( client_addr.sa_family == AF_INET ) @@ -249,7 +251,7 @@ bool Socket::listen( unsigned short port ) HandleError(); return false; } - if ( ::listen( _sck, SOMAXCONN ) == -1 ) + if (::listen( _sck, SOMAXCONN ) == -1 ) { HandleError(); return false; @@ -257,29 +259,27 @@ bool Socket::listen( unsigned short port ) return true; } -bool Socket::select( unsigned int seconds, unsigned int useconds ) -{ - fd_set fd; - struct timeval timeout = {0, 0}; - int nfds = 0; - FD_ZERO( &fd ); - FD_SET( _sck, &fd ); -#ifndef _WIN32 - passert_r( _sck < FD_SETSIZE, - "Select() implementation in Linux cant handle this many sockets at the same time." ) - nfds = _sck + 1; -#endif +bool Socket::select( unsigned int seconds, unsigned int useconds, int* result ) +{ + SinglePoller poller( _sck ); + poller.set_timeout( seconds, useconds ); - int res; + if ( !poller.prepare( false ) ) + { + if ( result ) + *result = -1; + throw std::runtime_error( "Unable to poll socket=" + tostring( _sck ) ); + } + int res = -1; do { - timeout.tv_sec = seconds; - timeout.tv_usec = useconds; - res = ::select( nfds, &fd, nullptr, nullptr, &timeout ); + res = poller.wait_for_events(); } while ( res < 0 && !exit_signalled && socket_errno == SOCKET_ERRNO( EINTR ) ); - return ( res > 0 && FD_ISSET( _sck, &fd ) ); + if ( result ) + *result = res; + return poller.incoming(); } bool Socket::accept( SOCKET* s, unsigned int /*mstimeout*/ ) @@ -360,42 +360,31 @@ void Socket::HandleError() bool Socket::recvbyte( unsigned char* ch, unsigned int waitms ) { - fd_set fd; - if ( !connected() ) return false; #if SCK_WATCH INFO_PRINT << "{L;1}\n"; #endif - FD_ZERO( &fd ); - FD_SET( _sck, &fd ); - int nfds = 0; -#ifndef _WIN32 - nfds = _sck + 1; -#endif - struct timeval tv; - int res; - - do - { - tv.tv_sec = 0; - tv.tv_usec = waitms * 1000; - res = ::select( nfds, &fd, nullptr, nullptr, &tv ); - } while ( res < 0 && exit_signalled && socket_errno == SOCKET_ERRNO( EINTR ) ); - if ( res == 0 ) + int res; + if ( !select( 0, waitms * 1000, &res ) ) { + if ( res == -1 ) + { + HandleError(); + close(); + } + else if ( res == 0 ) + { #if SCK_WATCH - INFO_PRINT << "{TO}\n"; + INFO_PRINT << "{TO}\n"; #endif + } + return false; } - else if ( res == -1 ) - { - HandleError(); - close(); /* FIXME: very likely unrecoverable */ - } + res = recv( _sck, (char*)ch, 1, 0 ); if ( res == 1 ) @@ -423,9 +412,7 @@ bool Socket::recvbyte( unsigned char* ch, unsigned int waitms ) bool Socket::recvdata( void* vdest, unsigned len, unsigned int waitms ) { - fd_set fd; char* pdest = (char*)vdest; - int nfds = 0; while ( len ) { @@ -435,33 +422,23 @@ bool Socket::recvdata( void* vdest, unsigned len, unsigned int waitms ) #if SCK_WATCH INFO_PRINT << "{L:" << len << "}\n"; #endif - FD_ZERO( &fd ); - FD_SET( _sck, &fd ); -#ifndef _WIN32 - nfds = _sck + 1; -#endif - - struct timeval tv; int res; - do - { - tv.tv_sec = 0; - tv.tv_usec = waitms * 1000; - res = ::select( nfds, &fd, nullptr, nullptr, &tv ); - } while ( res < 0 && exit_signalled && socket_errno == SOCKET_ERRNO( EINTR ) ); - - if ( res == 0 ) + if ( !select( 0, waitms * 1000, &res ) ) { + if ( res == -1 ) + { + HandleError(); + close(); + } + else if ( res == 0 ) + { #if SCK_WATCH - INFO_PRINT << "{TO}\n"; + INFO_PRINT << "{TO}\n"; #endif + } + return false; } - else if ( res == -1 ) - { - HandleError(); - close(); /* FIXME: very likely unrecoverable */ - } res = ::recv( _sck, pdest, len, 0 ); @@ -499,39 +476,28 @@ bool Socket::recvdata( void* vdest, unsigned len, unsigned int waitms ) unsigned Socket::peek( void* vdest, unsigned len, unsigned int wait_sec ) { - fd_set fd; + ; char* pdest = (char*)vdest; - int nfds = 0; #if SCK_WATCH INFO_PRINT << "{L:" << len << "}\n"; #endif - FD_ZERO( &fd ); - FD_SET( _sck, &fd ); -#ifndef _WIN32 - nfds = _sck + 1; -#endif - struct timeval tv; - int res; - - do - { - tv.tv_sec = wait_sec; - tv.tv_usec = 0; - res = ::select( nfds, &fd, nullptr, nullptr, &tv ); - } while ( res < 0 && exit_signalled && socket_errno == SOCKET_ERRNO( EINTR ) ); - if ( res == 0 ) + int res; + if ( !select( wait_sec, 0, &res ) ) { + if ( res == -1 ) + { + HandleError(); + close(); + } + else if ( res == 0 ) + { #if SCK_WATCH - INFO_PRINT << "{TO}\n"; + INFO_PRINT << "{TO}\n"; #endif - return 0; - } - else if ( res == -1 ) - { - HandleError(); - close(); /* FIXME: very likely unrecoverable */ + } + return 0; } diff --git a/pol-core/clib/network/wnsckt.h b/pol-core/clib/network/wnsckt.h index 2319d8222e..2450068cea 100644 --- a/pol-core/clib/network/wnsckt.h +++ b/pol-core/clib/network/wnsckt.h @@ -39,7 +39,7 @@ class Socket bool open( const char* ipaddr, unsigned short port ); bool listen( unsigned short port ); - bool select( unsigned int seconds, unsigned int useconds ); + bool select( unsigned int seconds, unsigned int useconds, int* result = nullptr ); bool accept( SOCKET* s, unsigned int mstimeout ); bool accept( Socket* newsocket ); bool recvbyte( unsigned char* byte, unsigned int waitms ); From 001bbc5008ef38a3adf9f75fe56d5c3777f651f9 Mon Sep 17 00:00:00 2001 From: Fernando Rozenblit Date: Sun, 25 Aug 2019 21:03:40 +0200 Subject: [PATCH 2/5] Added SocketLineReader class, which is responsible for reading lines from a socket in a more efficient way. Modified Aux, Debug and WWW to use the new class. --- pol-core/clib/network/sckutil.cpp | 66 ------------ pol-core/clib/network/sckutil.h | 6 +- pol-core/clib/network/socketsvc.cpp | 4 +- pol-core/clib/network/socketsvc.h | 2 +- pol-core/clib/network/wnsckt.cpp | 158 +++++++++++++++++++++++++++- pol-core/clib/network/wnsckt.h | 27 ++++- pol-core/pol/network/auxclient.cpp | 3 +- pol-core/pol/poldbg.cpp | 5 +- pol-core/pol/polwww.cpp | 73 ++++++------- pol-core/pol/uolisten.cpp | 6 +- 10 files changed, 227 insertions(+), 123 deletions(-) diff --git a/pol-core/clib/network/sckutil.cpp b/pol-core/clib/network/sckutil.cpp index 66744898a9..ab7c227f03 100644 --- a/pol-core/clib/network/sckutil.cpp +++ b/pol-core/clib/network/sckutil.cpp @@ -14,77 +14,11 @@ namespace Pol { namespace Clib { -bool readline( Socket& sck, std::string& s, bool* timeout_exit, unsigned int timeout_secs, - unsigned maxlen ) -{ - if ( timeout_exit ) - *timeout_exit = false; - s = ""; - unsigned char ch; - unsigned timeouts_left = timeout_secs / 2; - while ( !exit_signalled && sck.connected() ) - { - if ( sck.recvbyte( &ch, 2000 ) ) - { - timeouts_left = timeout_secs / 2; - if ( isprint( ch ) ) - { - s.append( 1, ch ); - if ( maxlen && s.length() > maxlen ) - { - sck.close(); - return false; - } - } - else - { - if ( ch == '\n' ) - return true; - } - } - else - { - if ( timeout_secs && !--timeouts_left ) - { - if ( timeout_exit ) - *timeout_exit = true; - return false; - } - } - } - return false; -} - void writeline( Socket& sck, const std::string& s ) { sck.send( (void*)s.c_str(), static_cast( s.length() ) ); sck.send( "\r\n", 2 ); } -bool readstring( Socket& sck, std::string& s, unsigned int interchar_secs, unsigned length ) -{ - s = ""; - unsigned char ch; - unsigned timeouts_left = interchar_secs / 2; - while ( !exit_signalled && sck.connected() ) - { - if ( sck.recvbyte( &ch, 2000 ) ) - { - timeouts_left = interchar_secs / 2; - - s.append( 1, ch ); - if ( s.length() == length ) - { - return true; - } - } - else - { - if ( !--timeouts_left ) - return false; - } - } - return false; -} } } diff --git a/pol-core/clib/network/sckutil.h b/pol-core/clib/network/sckutil.h index 48fa4b7c3d..ba5805e3aa 100644 --- a/pol-core/clib/network/sckutil.h +++ b/pol-core/clib/network/sckutil.h @@ -14,12 +14,10 @@ namespace Clib { class Socket; -bool readline( Socket& sck, std::string& s, bool* timeout_exit = 0, - unsigned int interchar_timeout_secs = 0, unsigned maxlen = 0 ); - +// TODO: move this function into the Socket class +// TODO: get rid of http_writeline, it's doing exactly the same as this function void writeline( Socket& sck, const std::string& s ); -bool readstring( Socket& sck, std::string& s, unsigned int interchar_secs, unsigned length ); } } #endif diff --git a/pol-core/clib/network/socketsvc.cpp b/pol-core/clib/network/socketsvc.cpp index 31e9942e97..b6a2883fe7 100644 --- a/pol-core/clib/network/socketsvc.cpp +++ b/pol-core/clib/network/socketsvc.cpp @@ -36,9 +36,9 @@ SocketListener::SocketListener( unsigned short port, Socket::option opt ) : _lis } bool SocketListener::GetConnection( Socket* newsck, unsigned int timeout_sec, - unsigned int timeout_usec ) + unsigned int timeout_msec ) { - if ( _listen_sck.select( timeout_sec, timeout_usec ) ) + if ( _listen_sck.has_incoming_data( timeout_sec * 1000 + timeout_msec ) ) return _listen_sck.accept( newsck ); return false; } diff --git a/pol-core/clib/network/socketsvc.h b/pol-core/clib/network/socketsvc.h index 2278f43ff5..50e43eee43 100644 --- a/pol-core/clib/network/socketsvc.h +++ b/pol-core/clib/network/socketsvc.h @@ -18,7 +18,7 @@ class SocketListener public: explicit SocketListener( unsigned short port ); SocketListener( unsigned short port, Socket::option opt ); - bool GetConnection( Socket* newsck, unsigned int timeout_sec, unsigned int timeout_usec = 0 ); + bool GetConnection( Socket* newsck, unsigned int timeout_sec, unsigned int timeout_msec = 0 ); friend class SocketClientThread; diff --git a/pol-core/clib/network/wnsckt.cpp b/pol-core/clib/network/wnsckt.cpp index 65fac9260a..fb8bfad9eb 100644 --- a/pol-core/clib/network/wnsckt.cpp +++ b/pol-core/clib/network/wnsckt.cpp @@ -6,6 +6,7 @@ #include "wnsckt.h" +#include #include #include @@ -259,10 +260,10 @@ bool Socket::listen( unsigned short port ) return true; } -bool Socket::select( unsigned int seconds, unsigned int useconds, int* result ) +bool Socket::has_incoming_data( unsigned int waitms, int* result ) { SinglePoller poller( _sck ); - poller.set_timeout( seconds, useconds ); + poller.set_timeout( 0, waitms * 1000 ); if ( !poller.prepare( false ) ) { @@ -279,6 +280,7 @@ bool Socket::select( unsigned int seconds, unsigned int useconds, int* result ) if ( result ) *result = res; + return poller.incoming(); } @@ -368,7 +370,7 @@ bool Socket::recvbyte( unsigned char* ch, unsigned int waitms ) #endif int res; - if ( !select( 0, waitms * 1000, &res ) ) + if ( !has_incoming_data( waitms, &res ) ) { if ( res == -1 ) { @@ -410,6 +412,43 @@ bool Socket::recvbyte( unsigned char* ch, unsigned int waitms ) } } +bool Socket::recvdata_nowait( char* pdest, unsigned len, int* bytes_read ) +{ + if ( bytes_read ) + *bytes_read = -1; + + if ( !connected() ) + return false; + +#if SCK_WATCH + INFO_PRINT << "{L:" << len << "}\n"; +#endif + + int res; + res = ::recv( _sck, pdest, len, 0 ); + + if ( bytes_read ) + *bytes_read = res; + + if ( res < 0 ) + { + /* Can't time out here this is an ERROR! */ + HandleError(); + return false; + } + + if ( res == 0 ) + { +#if SCK_WATCH + INFO_PRINT << "{CLOSE}\n"; +#endif + close(); + return false; + } + + return true; +} + bool Socket::recvdata( void* vdest, unsigned len, unsigned int waitms ) { char* pdest = (char*)vdest; @@ -423,7 +462,7 @@ bool Socket::recvdata( void* vdest, unsigned len, unsigned int waitms ) INFO_PRINT << "{L:" << len << "}\n"; #endif int res; - if ( !select( 0, waitms * 1000, &res ) ) + if ( !has_incoming_data( waitms, &res ) ) { if ( res == -1 ) { @@ -484,7 +523,7 @@ unsigned Socket::peek( void* vdest, unsigned len, unsigned int wait_sec ) #endif int res; - if ( !select( wait_sec, 0, &res ) ) + if ( !has_incoming_data( 1000 * wait_sec, &res ) ) { if ( res == -1 ) { @@ -613,5 +652,114 @@ bool Socket::is_local() const std::string s = getpeername(); return ( s == "127.0.0.1" ); } + +bool is_invalid_readline_char( unsigned char c ) +{ + return !isprint( c ) && c != '\n' && c != '\r'; +} + +bool SocketLineReader::try_readline( std::string& out, bool* timed_out ) +{ + if ( timed_out ) + *timed_out = false; + + // check if there is already a line in the buffer + auto pos_newline = _currentLine.find_first_of( "\r\n" ); + + // If not, try to read more data + if ( pos_newline == std::string::npos ) + { + std::array buffer; + buffer.fill( '\0' ); + + int res = -1; + if ( !_socket.has_incoming_data( _waitms, &res ) ) + { + if ( timed_out ) + *timed_out = true; + return false; + } + int bytes_read = -1; + if ( !_socket.recvdata_nowait( buffer.data(), buffer.size(), &bytes_read ) ) + return false; + + // store current line size so we don't need to search from the beginning again + size_t oldSize = _currentLine.size(); + + // remove invalid characters from buffer + auto buffer_end = + std::remove_if( buffer.begin(), buffer.begin() + bytes_read, is_invalid_readline_char ); + + // append only the valid characters to the buffer + _currentLine.append( buffer.data(), std::distance( buffer.begin(), buffer_end ) ); + if ( _maxLinelength > 0 && _currentLine.size() > _maxLinelength ) + { + out = _currentLine; + _currentLine.clear(); + return false; + } + + // update position + pos_newline = _currentLine.find_first_of( "\r\n", oldSize ); + } + + // Haven't found it yet + if ( pos_newline == std::string::npos ) + return false; + + auto end_newline = pos_newline + 1; + if ( _currentLine[pos_newline] == '\r' ) + end_newline++; + + + out = _currentLine.substr( 0, pos_newline ); + _currentLine.erase( 0, end_newline ); + + return true; +} + +// Blocks until a whole line is received, waitms are over or maxlen is reached +bool SocketLineReader::readline( std::string& out, bool* timed_out ) +{ + out = ""; + + const int max_timeouts = (_timeout_secs*1000) / _waitms; + + bool single_timed_out = false; + + int timeout_left = max_timeouts; + while ( !Clib::exit_signalled && _socket.connected() ) + { + if ( try_readline( out, &single_timed_out ) ) + { + return true; + } + + // if try_readline() is false, string "out" should be empty unless the maxlen was reached. + if ( !out.empty() ) + { + _socket.close(); + return false; + } + + if ( _timeout_secs > 0 && single_timed_out ) + { + timeout_left--; + if ( timeout_left <= 0 ) + { + _socket.close(); + *timed_out = true; + } + } + else + { + // refresh timeout counter + timeout_left = max_timeouts; + } + } + return false; +} + + } // namespace Clib } // namespace Pol diff --git a/pol-core/clib/network/wnsckt.h b/pol-core/clib/network/wnsckt.h index 2450068cea..14ab9bb55d 100644 --- a/pol-core/clib/network/wnsckt.h +++ b/pol-core/clib/network/wnsckt.h @@ -39,10 +39,11 @@ class Socket bool open( const char* ipaddr, unsigned short port ); bool listen( unsigned short port ); - bool select( unsigned int seconds, unsigned int useconds, int* result = nullptr ); + bool has_incoming_data( unsigned int waitms, int* result = nullptr ); bool accept( SOCKET* s, unsigned int mstimeout ); bool accept( Socket* newsocket ); bool recvbyte( unsigned char* byte, unsigned int waitms ); + bool recvdata_nowait( char* vdest, unsigned len, int* bytes_read ); bool recvdata( void* vdest, unsigned len, unsigned int waitms ); unsigned peek( void* vdest, unsigned len, unsigned int waitms ); void send( const void* data, unsigned length ); @@ -78,6 +79,30 @@ class Socket int _options; struct sockaddr _peer; }; + +class SocketLineReader +{ +public: + SocketLineReader( Socket& socket, unsigned int timeout_secs = 0, unsigned int max_linelength = 0 ) + : _socket( socket ), _waitms(500), _timeout_secs(timeout_secs),_maxLinelength( max_linelength ) + { + } + bool try_readline( std::string& out, bool* timed_out = nullptr); + bool readline( std::string& out, bool* timed_out = nullptr ); + + void set_max_linelength( unsigned int max_linelength ) { _maxLinelength = max_linelength; } + void set_wait( unsigned int waitms ) { _waitms = waitms; } + void set_timeout( unsigned int timeout_secs ) { _timeout_secs = timeout_secs; } + +private: + Socket& _socket; + std::string _currentLine; + + unsigned int _waitms; + unsigned int _timeout_secs; + unsigned int _maxLinelength; +}; + } // namespace Clib } // namespace Pol #endif // CLIB_WNSCKT_H diff --git a/pol-core/pol/network/auxclient.cpp b/pol-core/pol/network/auxclient.cpp index 204034bba1..80f4026e10 100644 --- a/pol-core/pol/network/auxclient.cpp +++ b/pol-core/pol/network/auxclient.cpp @@ -182,9 +182,10 @@ void AuxClientThread::run() std::string tmp; bool result, timeout_exit; + Clib::SocketLineReader linereader(_sck, 5); for ( ;; ) { - result = readline( _sck, tmp, &timeout_exit, 5 ); + result = linereader.readline( tmp, &timeout_exit ); if ( !result && !timeout_exit ) break; diff --git a/pol-core/pol/poldbg.cpp b/pol-core/pol/poldbg.cpp index 413de327a5..175680cfa7 100644 --- a/pol-core/pol/poldbg.cpp +++ b/pol-core/pol/poldbg.cpp @@ -1358,10 +1358,13 @@ void DebugClientThread::run() DebugContext dctx; std::string cmdline; std::vector results; + + Clib::SocketLineReader linereader(_sck); + while ( !dctx.done() ) { Clib::writeline( _sck, dctx.prompt() ); - if ( !readline( _sck, cmdline ) ) + if ( !linereader.readline( cmdline ) ) break; bool ret = dctx.process( cmdline, results ); diff --git a/pol-core/pol/polwww.cpp b/pol-core/pol/polwww.cpp index fed21450f1..35eabe10c4 100644 --- a/pol-core/pol/polwww.cpp +++ b/pol-core/pol/polwww.cpp @@ -33,8 +33,11 @@ #include "../clib/stlutil.h" #include "../clib/strutil.h" #include "../clib/threadhelp.h" +#include "../clib/timer.h" + #include "../plib/pkg.h" #include "../plib/systemstate.h" + #include "globals/uvars.h" #include "module/httpmod.h" #include "module/uomod.h" @@ -126,41 +129,10 @@ void config_web_server() load_mime_config(); } -// TODO: The http server is susceptible to DOS attacks -// TODO: limit access to localhost by default, probably - -bool http_readline( Clib::Socket& sck, std::string& s ) -{ - bool res = false; - s = ""; - unsigned char ch; - while ( sck.connected() && sck.recvbyte( &ch, 10000 ) ) - { - if ( isprint( ch ) ) - { - s.append( 1, ch ); - if ( s.length() > 3000 ) - { - sck.close(); - break; // return false; - } - } - else - { - if ( ch == '\n' ) - { - res = true; - break; - // return true; - } - } - } - return res; -} void http_writeline( Clib::Socket& sck, const std::string& s ) { sck.send( (void*)s.c_str(), static_cast( s.length() ) ); - sck.send( "\n", 1 ); + sck.send( "\r\n", 2 ); } void http_forbidden( Clib::Socket& sck ) @@ -197,6 +169,17 @@ void http_not_authorized( Clib::Socket& sck, const std::string& /*filename*/ ) http_writeline( sck, "" ); } +void http_internal_error( Clib::Socket& sck, const std::string& filename ) +{ + http_writeline( sck, "HTTP/1.1 500 Internal Sever Error" ); + http_writeline( sck, "Content-Type: text/html" ); + http_writeline( sck, "" ); + http_writeline( sck, "500 Internal Server Error" ); + http_writeline( sck, "

Internal Server Error

" ); + http_writeline( sck, "The requested URL " + filename + " caused an internal server error." ); + http_writeline( sck, "" ); +} + void http_not_found( Clib::Socket& sck, const std::string& filename ) { http_writeline( sck, "HTTP/1.1 404 Not Found" ); @@ -618,21 +601,22 @@ void send_binary( Clib::Socket& sck, const std::string& page, const std::string& void http_func( SOCKET client_socket ) { Clib::Socket sck( client_socket ); + Clib::SocketLineReader lineReader( sck, 5, 3000 ); + std::string get; std::string auth; std::string tmpstr; std::string host; - if ( Plib::systemstate.config.web_server_local_only ) + if ( Plib::systemstate.config.web_server_local_only && !sck.is_local() ) { - if ( !sck.is_local() ) - { - http_forbidden( sck ); - return; - } + http_forbidden( sck ); + return; } - while ( sck.connected() && http_readline( sck, tmpstr ) ) + bool timed_out = false; + Tools::HighPerfTimer requestTimer; + while ( sck.connected() && lineReader.readline( tmpstr, &timed_out ) ) { if ( Plib::systemstate.config.web_server_debug ) INFO_PRINT << "http(" << sck.handle() << "): '" << tmpstr << "'\n"; @@ -645,9 +629,19 @@ void http_func( SOCKET client_socket ) if ( strncmp( tmpstr.c_str(), "Host: ", 5 ) == 0 ) host = tmpstr.substr( 6 ); } + + if ( timed_out ) + INFO_PRINT << "HTTP connection timed out\n"; + if ( !sck.connected() ) return; + if ( Plib::systemstate.config.web_server_debug ) + { + INFO_PRINT << "[" << double( requestTimer.ellapsed().count() / 1000.0 ) + << " msec] finished reading header\n"; + } + ISTRINGSTREAM is( get ); std::string cmd; // GET, POST (we only handle GET) @@ -761,6 +755,7 @@ void http_func( SOCKET client_socket ) else { POLLOG_INFO << "HTTP server: I can't handle pagetype '" << pagetype << "'\n"; + http_internal_error( sck, page ); } } } diff --git a/pol-core/pol/uolisten.cpp b/pol-core/pol/uolisten.cpp index 5bb7f3a04a..a732582547 100644 --- a/pol-core/pol/uolisten.cpp +++ b/pol-core/pol/uolisten.cpp @@ -107,14 +107,14 @@ void uo_client_listener_thread( void* arg ) while ( !Clib::exit_signalled ) { unsigned int timeout = 2; - unsigned int utimeout = 0; + unsigned int mstimeout = 0; if ( !ls->login_clients.empty() ) { timeout = 0; - utimeout = 200000; + mstimeout = 200; } Clib::Socket newsck; - if ( SL.GetConnection( &newsck, timeout, utimeout ) && newsck.connected() ) + if ( SL.GetConnection( &newsck, timeout, mstimeout ) && newsck.connected() ) { // create an appropriate Client object if ( Plib::systemstate.config.use_single_thread_login ) From 31ed3a003335c7089546b63a12aa75d99d0cea5d Mon Sep 17 00:00:00 2001 From: Fernando Rozenblit Date: Sun, 25 Aug 2019 23:48:40 +0200 Subject: [PATCH 3/5] Fixed the check for max line length and some other minor issues --- pol-core/clib/network/wnsckt.cpp | 44 +++++++++++++++++++++----------- pol-core/clib/network/wnsckt.h | 12 +++++++-- pol-core/pol/polwww.cpp | 7 +++-- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/pol-core/clib/network/wnsckt.cpp b/pol-core/clib/network/wnsckt.cpp index fb8bfad9eb..1350b0ea0c 100644 --- a/pol-core/clib/network/wnsckt.cpp +++ b/pol-core/clib/network/wnsckt.cpp @@ -670,7 +670,6 @@ bool SocketLineReader::try_readline( std::string& out, bool* timed_out ) if ( pos_newline == std::string::npos ) { std::array buffer; - buffer.fill( '\0' ); int res = -1; if ( !_socket.has_incoming_data( _waitms, &res ) ) @@ -690,19 +689,29 @@ bool SocketLineReader::try_readline( std::string& out, bool* timed_out ) auto buffer_end = std::remove_if( buffer.begin(), buffer.begin() + bytes_read, is_invalid_readline_char ); - // append only the valid characters to the buffer - _currentLine.append( buffer.data(), std::distance( buffer.begin(), buffer_end ) ); - if ( _maxLinelength > 0 && _currentLine.size() > _maxLinelength ) - { - out = _currentLine; - _currentLine.clear(); + auto valid_char_count = std::distance( buffer.begin(), buffer_end ); + + // nothing gained from these bytes + if ( !valid_char_count ) return false; - } + + // append only the valid characters to the buffer + _currentLine.append( buffer.data(), valid_char_count ); // update position pos_newline = _currentLine.find_first_of( "\r\n", oldSize ); } + // note that std::string::npos is larger than any other number, so the conditon below will be + // false only if there is a newline before the maximum line length or if the current line is still + // small. + if ( _maxLinelength > 0 && pos_newline > _maxLinelength && _currentLine.size() > _maxLinelength ) + { + out = _currentLine; + _currentLine.clear(); + return false; + } + // Haven't found it yet if ( pos_newline == std::string::npos ) return false; @@ -711,7 +720,6 @@ bool SocketLineReader::try_readline( std::string& out, bool* timed_out ) if ( _currentLine[pos_newline] == '\r' ) end_newline++; - out = _currentLine.substr( 0, pos_newline ); _currentLine.erase( 0, end_newline ); @@ -722,9 +730,10 @@ bool SocketLineReader::try_readline( std::string& out, bool* timed_out ) bool SocketLineReader::readline( std::string& out, bool* timed_out ) { out = ""; - - const int max_timeouts = (_timeout_secs*1000) / _waitms; - + if ( timed_out ) + *timed_out = false; + + const int max_timeouts = ( _timeout_secs * 1000 ) / _waitms; bool single_timed_out = false; int timeout_left = max_timeouts; @@ -746,9 +755,14 @@ bool SocketLineReader::readline( std::string& out, bool* timed_out ) { timeout_left--; if ( timeout_left <= 0 ) - { - _socket.close(); - *timed_out = true; + { + if ( _disconnect_on_timeout || !timed_out ) + _socket.close(); + + if ( timed_out ) + *timed_out = true; + + return false; } } else diff --git a/pol-core/clib/network/wnsckt.h b/pol-core/clib/network/wnsckt.h index 14ab9bb55d..e274520daf 100644 --- a/pol-core/clib/network/wnsckt.h +++ b/pol-core/clib/network/wnsckt.h @@ -83,8 +83,12 @@ class Socket class SocketLineReader { public: - SocketLineReader( Socket& socket, unsigned int timeout_secs = 0, unsigned int max_linelength = 0 ) - : _socket( socket ), _waitms(500), _timeout_secs(timeout_secs),_maxLinelength( max_linelength ) + SocketLineReader( Socket& socket, unsigned int timeout_secs = 0, unsigned int max_linelength = 0, bool disconnect_on_timeout = true ) + : _socket( socket ), + _waitms( 500 ), + _timeout_secs( timeout_secs ), + _maxLinelength( max_linelength ), + _disconnect_on_timeout(disconnect_on_timeout) { } bool try_readline( std::string& out, bool* timed_out = nullptr); @@ -94,6 +98,8 @@ class SocketLineReader void set_wait( unsigned int waitms ) { _waitms = waitms; } void set_timeout( unsigned int timeout_secs ) { _timeout_secs = timeout_secs; } + bool set_disconnect_on_timeout(bool disconnect) { _disconnect_on_timeout = disconnect; } + private: Socket& _socket; std::string _currentLine; @@ -101,6 +107,8 @@ class SocketLineReader unsigned int _waitms; unsigned int _timeout_secs; unsigned int _maxLinelength; + + bool _disconnect_on_timeout; }; } // namespace Clib diff --git a/pol-core/pol/polwww.cpp b/pol-core/pol/polwww.cpp index 35eabe10c4..ba2f6a3548 100644 --- a/pol-core/pol/polwww.cpp +++ b/pol-core/pol/polwww.cpp @@ -601,7 +601,7 @@ void send_binary( Clib::Socket& sck, const std::string& page, const std::string& void http_func( SOCKET client_socket ) { Clib::Socket sck( client_socket ); - Clib::SocketLineReader lineReader( sck, 5, 3000 ); + Clib::SocketLineReader lineReader( sck, 5, 3000, false ); // we take care of disconnecting at timeout std::string get; std::string auth; @@ -631,7 +631,10 @@ void http_func( SOCKET client_socket ) } if ( timed_out ) - INFO_PRINT << "HTTP connection timed out\n"; + { + INFO_PRINT << "HTTP connection " << sck.getpeername() << " timed out\n"; + sck.close(); + } if ( !sck.connected() ) return; From 9074fdb7e628a69eb3a33559c765fb64dad83931 Mon Sep 17 00:00:00 2001 From: Fernando Rozenblit Date: Mon, 26 Aug 2019 00:22:35 +0200 Subject: [PATCH 4/5] style issues :p --- pol-core/clib/network/wnsckt.h | 11 ++++++----- pol-core/pol/polwww.cpp | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pol-core/clib/network/wnsckt.h b/pol-core/clib/network/wnsckt.h index e274520daf..b64a1508bb 100644 --- a/pol-core/clib/network/wnsckt.h +++ b/pol-core/clib/network/wnsckt.h @@ -83,27 +83,28 @@ class Socket class SocketLineReader { public: - SocketLineReader( Socket& socket, unsigned int timeout_secs = 0, unsigned int max_linelength = 0, bool disconnect_on_timeout = true ) + SocketLineReader( Socket& socket, unsigned int timeout_secs = 0, unsigned int max_linelength = 0, + bool disconnect_on_timeout = true ) : _socket( socket ), _waitms( 500 ), _timeout_secs( timeout_secs ), _maxLinelength( max_linelength ), - _disconnect_on_timeout(disconnect_on_timeout) + _disconnect_on_timeout( disconnect_on_timeout ) { } - bool try_readline( std::string& out, bool* timed_out = nullptr); + bool try_readline( std::string& out, bool* timed_out = nullptr ); bool readline( std::string& out, bool* timed_out = nullptr ); void set_max_linelength( unsigned int max_linelength ) { _maxLinelength = max_linelength; } void set_wait( unsigned int waitms ) { _waitms = waitms; } void set_timeout( unsigned int timeout_secs ) { _timeout_secs = timeout_secs; } - bool set_disconnect_on_timeout(bool disconnect) { _disconnect_on_timeout = disconnect; } + bool set_disconnect_on_timeout( bool disconnect ) { _disconnect_on_timeout = disconnect; } private: Socket& _socket; std::string _currentLine; - + unsigned int _waitms; unsigned int _timeout_secs; unsigned int _maxLinelength; diff --git a/pol-core/pol/polwww.cpp b/pol-core/pol/polwww.cpp index ba2f6a3548..ad38a8215e 100644 --- a/pol-core/pol/polwww.cpp +++ b/pol-core/pol/polwww.cpp @@ -601,7 +601,8 @@ void send_binary( Clib::Socket& sck, const std::string& page, const std::string& void http_func( SOCKET client_socket ) { Clib::Socket sck( client_socket ); - Clib::SocketLineReader lineReader( sck, 5, 3000, false ); // we take care of disconnecting at timeout + Clib::SocketLineReader lineReader( sck, 5, 3000, + false ); // we take care of disconnecting at timeout std::string get; std::string auth; From 94dbd9f80a47ab74d9e53b7c74b3b4ecf8345803 Mon Sep 17 00:00:00 2001 From: Fernando Rozenblit Date: Mon, 26 Aug 2019 07:46:22 +0200 Subject: [PATCH 5/5] Fixed wrong return type --- pol-core/clib/network/wnsckt.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pol-core/clib/network/wnsckt.h b/pol-core/clib/network/wnsckt.h index b64a1508bb..fd6b78fcd8 100644 --- a/pol-core/clib/network/wnsckt.h +++ b/pol-core/clib/network/wnsckt.h @@ -99,7 +99,7 @@ class SocketLineReader void set_wait( unsigned int waitms ) { _waitms = waitms; } void set_timeout( unsigned int timeout_secs ) { _timeout_secs = timeout_secs; } - bool set_disconnect_on_timeout( bool disconnect ) { _disconnect_on_timeout = disconnect; } + void set_disconnect_on_timeout( bool disconnect ) { _disconnect_on_timeout = disconnect; } private: Socket& _socket;