2021-02-09 23:02:05 -05:00
# include "sockets.h"
# include <array>
# include <memory>
# include <mutex>
constexpr auto max_buffer = 1024 ;
decltype ( net : : message_queue ) net : : message_queue { } ;
2021-02-15 15:06:48 -05:00
auto inline mutex ( ) - > std : : recursive_mutex &
{
static std : : recursive_mutex mutex { } ; //allows for multiple thread access to our recv.
return mutex ;
}
2021-02-09 23:02:05 -05:00
auto net : : recv ( const SOCKET socket ) noexcept - > std : : string
{
2021-02-15 15:06:48 -05:00
std : : unique_lock < std : : recursive_mutex > lock ( mutex ( ) ) ;
2021-02-09 23:02:05 -05:00
try
{
auto & my_queue = message_queue [ socket ] ;
if ( ! my_queue . empty ( ) )
{
std : : string message = my_queue . front ( ) ; // ReSharper disable All Just shut up resharper. I would use auto here normally, but the linter wants to make message a reference.....even though it won't be valid next line.
my_queue . pop ( ) ;
lock . unlock ( ) ;
return message ;
}
lock . unlock ( ) ; //release lock so as to not deadlock on recv.
std : : string data ;
auto length = max_buffer ;
while ( length = = max_buffer ) /* queue all future messages from the stream */
{
char message_buffer [ max_buffer ] = { 0 } ;
length = : : recv ( socket , message_buffer , max_buffer , 0 ) ;
if ( length < 0 )
throw std : : runtime_error ( " Closed Socket " ) ;
for ( auto i = 0 ; i < length ; + + i )
if ( message_buffer [ i ] ) /*append until null. [free message framing ;)]*/
{
data + = message_buffer [ i ] ;
}
else
{
lock . lock ( ) ;
my_queue . push ( data ) ;
data . clear ( ) ;
lock . unlock ( ) ;
}
}
if ( ! data . empty ( ) ) //improperly terminated message, don't drop it!
{
lock . lock ( ) ;
my_queue . push ( data ) ;
data . clear ( ) ;
lock . unlock ( ) ;
}
if ( my_queue . empty ( ) ) /* empty response */
{
return " " ;
}
return recv ( socket ) ; //recurse.
} catch ( std : : exception & err ) /* shouldn't happen. */
{
return { } ; //empty
}
}
auto net : : system ( const std : : string & command ) noexcept - > std : : string
{
try
{
std : : array < char , max_buffer > output { 0 } ; //size is arbitrary
std : : string data { } ;
const popen_up pipe ( popen ( command . c_str ( ) , " r " ) ) ;
if ( ! pipe )
return " " ;
while ( fgets ( output . data ( ) , output . size ( ) , pipe . get ( ) ) ! = nullptr )
data + = output . data ( ) ;
return data ;
} catch ( std : : exception & )
{
return " " ;
}
}
# if WIN32
auto net : : closesocket ( const SOCKET socket ) noexcept - > unsigned
{
message_queue . erase ( socket ) ;
return : : closesocket ( socket ) ;
}
auto net : : prologue ( ) noexcept - > void
{
WSADATA wsa_data ;
const auto result = WSAStartup ( win_sock_ver , & wsa_data ) ;
if ( result )
{
std : : cout < < " FATAL ERROR IN PROLOGUE " < < result < < std : : endl ;
exit ( - 1 ) ;
}
}
auto net : : epilogue ( ) noexcept - > void { WSACleanup ( ) ; }
# else
auto net : : closesocket ( const SOCKET socket ) noexcept - > unsigned
{
2021-02-15 15:06:48 -05:00
std : : unique_lock < std : : recursive_mutex > lock ( mutex ( ) ) ;
2021-02-09 23:02:05 -05:00
message_queue . erase ( socket ) ;
return close ( socket ) ;
}
auto net : : prologue ( ) noexcept - > void { } //not required on Linux.
auto net : : epilogue ( ) noexcept - > void { } //not required on Linux.
# endif