Skip to content

Commit

Permalink
Merge pull request #24 from JadeMatrix/v0.8.3
Browse files Browse the repository at this point in the history
v0.8.3
  • Loading branch information
JadeMatrix authored Mar 21, 2018
2 parents f907962 + 6357f2d commit 32a8bbe
Show file tree
Hide file tree
Showing 18 changed files with 4,259 additions and 45 deletions.
28 changes: 28 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,39 @@ PROJECT(
CXX
)

FIND_LIBRARY( UNITTEST_LIBRARY UnitTest++ )

INCLUDE_DIRECTORIES(
src
)


# Tests ########################################################################

if( UNITTEST_LIBRARY )
FIND_LIBRARY( CURL_LIBRARY curl )
ADD_EXECUTABLE(
tests
../tests/tests.cpp
../tests/base64_tests.cpp
../tests/connection_tests.cpp
../tests/request_tests.cpp
../tests/response_tests.cpp
../tests/server_tests.cpp
../tests/type_tests.cpp
../tests/url_encode_tests.cpp
../tests/async_utils.cpp
)
TARGET_LINK_LIBRARIES(
tests
${UNITTEST_LIBRARY}
${CURL_LIBRARY}
)
endif( UNITTEST_LIBRARY )


# Examples #####################################################################

ADD_EXECUTABLE(
hello_world
../examples/hello_world.cpp
Expand Down
8 changes: 8 additions & 0 deletions doc/Classes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ Exceptions

As parsing the offending request almost certainly failed midway, garbage data will likely in the connection's buffer. Currently, the only safe way to handle this exception is to close the connection.

.. cpp:class:: response_marshall_error : std::runtime_error
Thrown by :cpp:class:`response`'s constructor when the response arguments cannot be marshalled into a valid HTTP response:

* One of the header names is an empty string
* One of the header names contains a character other than A-Z, a-z, 0-9, or _
* Any header value is an empty string

.. cpp:class:: url_decode_error : std::runtime_error
Thrown by :cpp:func:`url_decode()` when the input is not a valid `URL- or percent-encoded <https://en.wikipedia.org/wiki/Percent-encoding>`_ string.
Expand Down
2 changes: 1 addition & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
# The short X.Y version.
version = u'0.8'
# The full version, including alpha/beta/rc tags.
release = u'0.8.2'
release = u'0.8.3'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
4 changes: 2 additions & 2 deletions examples/multiple_clients.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const show::headers_type::value_type server_header = {
};


void handle_connection( std::shared_ptr< show::connection > connection )
void handle_connection( std::unique_ptr< show::connection > connection )
{
try
{
Expand Down Expand Up @@ -92,7 +92,7 @@ int main( int argc, char* argv[] )
{
std::thread worker(
handle_connection,
std::shared_ptr< show::connection >(
std::unique_ptr< show::connection >(
new show::connection( test_server.serve() )
)
);
Expand Down
101 changes: 76 additions & 25 deletions src/show.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ namespace show
static const std::string name = "SHOW";
static const int major = 0;
static const int minor = 8;
static const int revision = 2;
static const std::string string = "0.8.2";
static const int revision = 3;
static const std::string string = "0.8.3";
}


Expand Down Expand Up @@ -77,7 +77,7 @@ namespace show
inline char _ASCII_upper( char c )
{
if( c >= 'a' && c <= 'z' )
c |= ~0x20;
c &= ~0x20;
return c;
}
inline std::string _ASCII_upper( std::string s )
Expand Down Expand Up @@ -340,9 +340,10 @@ namespace show
int timeout( int );
};

class socket_error : public std::runtime_error { using runtime_error::runtime_error; };
class request_parse_error : public std::runtime_error { using runtime_error::runtime_error; };
class url_decode_error : public std::runtime_error { using runtime_error::runtime_error; };
class socket_error : public std::runtime_error { using runtime_error::runtime_error; };
class request_parse_error : public std::runtime_error { using runtime_error::runtime_error; };
class response_marshall_error : public std::runtime_error { using runtime_error::runtime_error; };
class url_decode_error : public std::runtime_error { using runtime_error::runtime_error; };

// Does not inherit from std::exception as these aren't meant to signal
// strict error states
Expand Down Expand Up @@ -798,7 +799,7 @@ namespace show
bool in_endline_seq = false;
// See https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
bool check_for_multiline_header = false;

bool path_begun = false;
std::stack< std::string > key_buffer_stack;
std::string key_buffer, value_buffer;

Expand Down Expand Up @@ -859,24 +860,36 @@ namespace show
case '?':
parse_state = READING_QUERY_ARGS;
break;
case '\n':
parse_state = READING_HEADER_NAME;
break;
case ' ':
parse_state = READING_PROTOCOL;
break;
case '/':
if( _path.size() > 0 )
if( path_begun )
try
{
*_path.rbegin() = url_decode( *_path.rbegin() );
if( _path.size() < 1 )
_path.push_back( "" );
*_path.rbegin() = url_decode(
*_path.rbegin()
);
_path.push_back( "" );
}
catch( const url_decode_error& ude )
{
throw request_parse_error( ude.what() );
}
else
path_begun = true;
break;
default:
if( _path.size() < 1 )
{
path_begun = true;
_path.push_back( std::string( &current_char, 1 ) );
}
else
_path[ _path.size() - 1 ] += current_char;
break;
Expand Down Expand Up @@ -972,7 +985,7 @@ namespace show
}
default:
if( !(
( current_char >= 'a' && current_char <= 'z' )
( current_char >= 'a' && current_char <= 'z' )
|| ( current_char >= 'A' && current_char <= 'Z' )
|| ( current_char >= '0' && current_char <= '9' )
|| current_char == '-'
Expand Down Expand Up @@ -1004,8 +1017,14 @@ namespace show
break;
case ' ':
case '\t':
if( value_buffer.size() < 1 )
break;
if( check_for_multiline_header )
check_for_multiline_header = false;
if(
value_buffer.size() > 0
&& *value_buffer.rbegin() != ' '
)
value_buffer += ' ';
break;
default:
if( check_for_multiline_header )
{
Expand Down Expand Up @@ -1052,12 +1071,18 @@ namespace show
else
try
{
std::size_t convert_stopped;
std::size_t value_size =
content_length_header -> second[ 0 ].size();
_content_length = std::stoull(
content_length_header -> second[ 0 ],
nullptr,
0
&convert_stopped,
10
);
_unknown_content_length = NO;
if( convert_stopped < value_size )
_unknown_content_length = MAYBE;
else
_unknown_content_length = NO;
}
catch( const std::invalid_argument& e )
{
Expand Down Expand Up @@ -1184,18 +1209,41 @@ namespace show
++map_iter
)
{
if( map_iter -> first.size() < 1 )
throw response_marshall_error( "empty header name" );
else for( auto c : map_iter -> first )
if( !(
( c >= 'a' && c <= 'z' )
|| ( c >= 'A' && c <= 'Z' )
|| ( c >= '0' && c <= '9' )
|| c == '-'
) )
throw response_marshall_error( "invalid header name" );

for(
auto vector_iter = map_iter -> second.begin();
vector_iter != map_iter -> second.end();
++vector_iter
)
{
headers_stream
<< map_iter -> first
<< ": "
<< *vector_iter
<< "\r\n"
;
if( vector_iter -> size() < 1 )
throw response_marshall_error( "empty header value" );

headers_stream << map_iter -> first << ": ";
bool insert_newline = false;
for( auto c : *vector_iter )
if( c == '\r' || c == '\n' )
insert_newline = true;
else
{
if( insert_newline )
{
headers_stream << "\r\n ";
insert_newline = false;
}
headers_stream << c;
}
headers_stream << "\r\n";
}
}
headers_stream << "\r\n";
Expand Down Expand Up @@ -1274,7 +1322,7 @@ namespace show
this -> timeout( timeout );

sockaddr_in6 socket_address;
memset(&socket_address, 0, sizeof(socket_address));
std::memset( &socket_address, 0, sizeof( socket_address ) );
socket_address.sin6_family = AF_INET6;
socket_address.sin6_port = htons( port );
// socket_address.sin6_addr.s_addr = in6addr_any;
Expand Down Expand Up @@ -1470,13 +1518,16 @@ namespace show
{
hex_convert_space[ 0 ] = o[ i + 1 ];
hex_convert_space[ 1 ] = o[ i + 2 ];

std::size_t convert_stopped;
decoded += ( char )std::stoi(
hex_convert_space,
0,
&convert_stopped,
16
);

if( convert_stopped < hex_convert_space.size() )
throw url_decode_error(
"invalid URL-encoded sequence"
);
i += 2;
}
catch( const std::invalid_argument& e )
Expand Down
30 changes: 13 additions & 17 deletions src/show/base64.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace show
std::string base64_encode(
const std::string& o,
const char* chars = base64_chars_standard
) noexcept;
);
std::string base64_decode(
const std::string& o,
const char* chars = base64_chars_standard
Expand All @@ -33,7 +33,7 @@ namespace show
inline std::string base64_encode(
const std::string& o,
const char* chars
) noexcept
)
{
unsigned char current_sextet;
std::string encoded;
Expand Down Expand Up @@ -111,7 +111,7 @@ namespace show
std::string::size_type unpadded_len = o.size();

for(
std::string::const_reverse_iterator r_iter = o.rbegin();
auto r_iter = o.rbegin();
r_iter != o.rend();
++r_iter
)
Expand Down Expand Up @@ -139,16 +139,8 @@ namespace show

for( std::string::size_type i = 0; i < b64_size; ++i )
{
if( o[ i ] == '=' )
{
if(
i < unpadded_len
&& i >= unpadded_len - 2
)
break;
else
throw base64_decode_error( "premature padding" );
}
if( o[ i ] == '=' && i + 1 < b64_size && o[ i + 1 ] != '=' )
throw base64_decode_error( "premature padding" );

std::map< char, /*unsigned*/ char >::iterator first, second;

Expand All @@ -174,7 +166,6 @@ namespace show
current_octet |= (
second -> second >> 4
) & 0x03 /* 00000011 */;
decoded += current_octet;
break;
case 1:
// i
Expand All @@ -185,7 +176,6 @@ namespace show
current_octet |= (
second -> second >> 2
) & 0x0F /* 00001111 */;
decoded += current_octet;
break;
case 2:
// i
Expand All @@ -194,14 +184,20 @@ namespace show
current_octet = ( first -> second << 6 ) & 0xC0 /* 11000000 */;
if( i + 1 < o.size() )
current_octet |= second -> second & 0x3F /* 00111111 */;
decoded += current_octet;
break;
case 3:
// i
// ****** ****** ****** ******
// -
break;
continue;
}

// Skip null bytes added by padding (but continue to next iteration
// to check for premature padding)
if( current_octet == 0x00 && o[ i + 1 ] == '=' )
continue;

decoded += current_octet;
}

return decoded;
Expand Down
Loading

0 comments on commit 32a8bbe

Please sign in to comment.