Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cookie parsing and "path parameters" #309

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions source/corvusoft/restbed/detail/request_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ namespace restbed

std::multimap< std::string, std::string > m_headers { };

std::map< std::string, std::string > m_cookie_parameters { };

std::map< std::string, std::string > m_path_parameters { };

std::multimap< std::string, std::string > m_query_parameters { };
Expand Down
3 changes: 1 addition & 2 deletions source/corvusoft/restbed/detail/resource_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

//System Includes
#include <map>
#include <set>
#include <string>
#include <vector>
#include <functional>
Expand All @@ -33,7 +32,7 @@ namespace restbed

struct ResourceImpl
{
std::set< std::string > m_paths { };
std::vector< std::string > m_paths { };

std::set< std::string > m_methods { };

Expand Down
110 changes: 86 additions & 24 deletions source/corvusoft/restbed/detail/service_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
//External Includes

//System Namespaces
using std::set;
using std::vector;
using std::map;
using std::free;
using std::pair;
Expand Down Expand Up @@ -79,6 +79,7 @@ namespace restbed
{
namespace detail
{
std::map<std::string, int> ServiceImpl::m_route_order = { };
ServiceImpl::ServiceImpl( void ) : m_uptime( steady_clock::time_point::min( ) ),
m_logger( nullptr ),
m_supported_methods( ),
Expand Down Expand Up @@ -373,7 +374,7 @@ namespace restbed
}
}

bool ServiceImpl::has_unique_paths( const set< string >& paths ) const
bool ServiceImpl::has_unique_paths( const vector< string >& paths ) const
{
if ( paths.empty( ) )
{
Expand Down Expand Up @@ -558,7 +559,7 @@ namespace restbed
void ServiceImpl::extract_path_parameters( const string& sanitised_path, const shared_ptr< const Request >& request ) const
{
smatch matches;
static const regex pattern( "^\\{([a-zA-Z0-9_\\-]+): ?.*\\}$" );
static const regex pattern( "^\\{([a-zA-Z0-9_\\-]+): ?(.*)\\}$" );

const auto folders = String::split( request->get_path( ), '/' );
const auto declarations = String::split( m_settings->get_root( ) + "/" + m_resource_paths.at( sanitised_path ), '/' );
Expand All @@ -570,7 +571,17 @@ namespace restbed
if ( declaration.front( ) == '{' and declaration.back( ) == '}' )
{
regex_match( declaration, matches, pattern );
request->m_pimpl->m_path_parameters.insert( make_pair( matches[ 1 ].str( ), folders[ index ] ) );
if ( matches[ 2 ].str( ) == "TO_END" ) {
std::ostringstream oss;
std::copy( folders.begin()+index, folders.end()-1, std::ostream_iterator<std::string>( oss, "/" ) );
oss << folders.back( );
request->m_pimpl->m_path_parameters.insert( make_pair( matches[ 1 ].str( ), oss.str() ) );
break;
}
else
{
request->m_pimpl->m_path_parameters.insert( make_pair( matches[ 1 ].str( ), folders[ index ] ) );
}
}
}
}
Expand Down Expand Up @@ -631,30 +642,54 @@ namespace restbed
const auto path_folders = String::split( request->get_path( ), '/' );
const auto route_folders = String::split( m_settings->get_root( ) + "/" + route.first, '/' );

if ( path_folders.empty( ) and route_folders.empty( ) )
if ( path_folders == route_folders )
{
return true;
}

bool match = false;

if ( path_folders.size( ) == route_folders.size( ) )
if ( route_folders.empty() and !path_folders.empty() )
{
for ( size_t index = 0; index < path_folders.size( ); index++ )
return false;
}

bool match = false;

bool to_end = route_folders.back( ).find( "TO_END" ) not_eq std::string::npos;

for ( size_t index = 0; index < route_folders.size( ); index++ )
{
if(
to_end and route_folders[ index ] == "TO_END"
and
(
index == 0 //if needs full path
or
(
( route_folders.size() > 1 and path_folders.size() > 1 )
and
regex_match( path_folders[ index-1 ], regex( route_folders[ index-1 ] ) )
)
)
){
return true;
}

if( path_folders.empty() or path_folders.size() != route_folders.size() ){
break;
}

if ( m_settings->get_case_insensitive_uris( ) )
{
if ( m_settings->get_case_insensitive_uris( ) )
{
match = regex_match( path_folders[ index ], regex( route_folders[ index ], icase ) );
}
else
{
match = regex_match( path_folders[ index ], regex( route_folders[ index ] ) );
}

if ( not match )
{
break;
}
match = regex_match( path_folders[ index ], regex( route_folders[ index ], icase ) );
}
else
{
match = regex_match( path_folders[ index ], regex( route_folders[ index ] ) );
}

if ( not match )
{
break;
}
}

Expand Down Expand Up @@ -707,7 +742,7 @@ namespace restbed
};
}

const multimap< string, string > ServiceImpl::parse_request_headers( istream& stream )
const multimap< string, string > ServiceImpl::parse_request_headers( istream& stream, const shared_ptr< Session > session )
{
smatch matches;
string data = "";
Expand All @@ -722,6 +757,33 @@ namespace restbed
}

headers.insert( make_pair( matches[ 1 ].str( ), matches[ 2 ].str( ) ) );

if ( matches[1].str() == "Cookie" || matches[1].str() == "Set-Cookie" ) {
std::string cookie_header = matches[2].str();
smatch cookiev_matches;
static const regex cookiev_pattern( "([\\s]*<?([^;\\s>]+)>?[\\s]*=[\\s]*<?([^;>]+)>?[\\s]*;?)|[\\s]*<?([^;\\s>]+)>?[\\s]*;?" );
string::const_iterator searchStart( cookie_header.cbegin() );

regex_match( cookie_header, cookiev_matches, cookiev_pattern );

while ( regex_search( searchStart, cookie_header.cend(), cookiev_matches, cookiev_pattern ) )
{
if ( cookiev_matches[4].str().empty() ){
session->m_pimpl->m_request->m_pimpl->m_cookie_parameters.insert(
make_pair( cookiev_matches[2].str(), cookiev_matches[3].str() )
);
}
else {
session->m_pimpl->m_request->m_pimpl->m_cookie_parameters.insert(
make_pair( cookiev_matches[4].str(), "yes" )
);
}
searchStart += cookiev_matches.position() + cookiev_matches.length();
}
if ( session->m_pimpl->m_request->m_pimpl->m_cookie_parameters.empty() ){
throw runtime_error( "Your client has issued a malformed cookie header. That’s all we know." );
}
}
}

return headers;
Expand All @@ -745,7 +807,7 @@ namespace restbed

session->m_pimpl->m_request->m_pimpl->m_path = Uri::decode( uri.get_path( ) );
session->m_pimpl->m_request->m_pimpl->m_method = items.at( "method" );
session->m_pimpl->m_request->m_pimpl->m_headers = parse_request_headers( stream );
session->m_pimpl->m_request->m_pimpl->m_headers = parse_request_headers( stream, session );
session->m_pimpl->m_request->m_pimpl->m_query_parameters = uri.get_query_parameters( );

char* locale = strdup( setlocale( LC_NUMERIC, nullptr ) );
Expand Down
17 changes: 13 additions & 4 deletions source/corvusoft/restbed/detail/service_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ namespace restbed
//Friends

//Definitions
int m_last_route_pos;

static std::map<std::string, int> m_route_order;

struct SortByInsertion {
bool operator() (const std::string& lv, const std::string& rv) const {
return m_route_order[lv] < m_route_order[rv] ? true : false;
}
};

//Constructors
ServiceImpl( void );
Expand All @@ -81,7 +90,7 @@ namespace restbed

void not_found( const std::shared_ptr< Session > session ) const;

bool has_unique_paths( const std::set< std::string >& paths ) const;
bool has_unique_paths( const std::vector< std::string >& paths ) const;

void log( const Logger::Level level, const std::string& message ) const;

Expand Down Expand Up @@ -109,7 +118,7 @@ namespace restbed

static const std::map< std::string, std::string > parse_request_line( std::istream& stream );

static const std::multimap< std::string, std::string > parse_request_headers( std::istream& stream );
static const std::multimap< std::string, std::string > parse_request_headers( std::istream& stream, const std::shared_ptr< Session > session );

void parse_request( const std::error_code& error, std::size_t length, const std::shared_ptr< Session > session ) const;

Expand Down Expand Up @@ -153,9 +162,9 @@ namespace restbed
#endif
std::shared_ptr< asio::ip::tcp::acceptor > m_acceptor;

std::map< std::string, std::string > m_resource_paths;
std::map< std::string, std::string, SortByInsertion > m_resource_paths;

std::map< std::string, std::shared_ptr< const Resource > > m_resource_routes;
std::map< std::string, std::shared_ptr< const Resource >, SortByInsertion > m_resource_routes;

std::function< void ( void ) > m_ready_handler;

Expand Down
69 changes: 69 additions & 0 deletions source/corvusoft/restbed/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,75 @@ namespace restbed
return Common::get_parameters( name, m_pimpl->m_headers );
}

float Request::get_cookie_parameter( const string& name, const float default_value ) const
{
float parameter = 0;

try
{
parameter = stof( get_cookie_parameter( name ) );
}
catch ( const out_of_range )
{
parameter = default_value;
}
catch ( const invalid_argument )
{
parameter = default_value;
}

return parameter;
}

double Request::get_cookie_parameter( const string& name, const double default_value ) const
{
double parameter = 0;

try
{
parameter = stod( get_cookie_parameter( name ) );
}
catch ( const out_of_range )
{
parameter = default_value;
}
catch ( const invalid_argument )
{
parameter = default_value;
}

return parameter;
}

string Request::get_cookie_parameter( const string& name, const string& default_value ) const
{
if ( name.empty( ) )
{
return default_value;
}

const auto parameters = Common::get_parameters( name, m_pimpl->m_cookie_parameters );
return ( parameters.empty( ) ) ? default_value : parameters.begin( )->second;
}

string Request::get_cookie_parameter( const string& name, const function< string ( const string& ) >& transform ) const
{
if ( name.empty( ) )
{
return String::empty;
}

const auto parameters = Common::get_parameters( name, m_pimpl->m_cookie_parameters );
const auto value = ( parameters.empty( ) ) ? String::empty : parameters.begin( )->second;

return Common::transform( value, transform );
}

map< string, string > Request::get_cookie_parameters( const string& name ) const
{
return Common::get_parameters( name, m_pimpl->m_cookie_parameters );
}

float Request::get_query_parameter( const string& name, const float default_value ) const
{
float parameter = 0;
Expand Down
17 changes: 17 additions & 0 deletions source/corvusoft/restbed/request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,23 @@ namespace restbed
}

std::multimap< std::string, std::string > get_headers( const std::string& name = "" ) const;

float get_cookie_parameter( const std::string& name, const float default_value ) const;

double get_cookie_parameter( const std::string& name, const double default_value ) const;

std::string get_cookie_parameter( const std::string& name, const std::string& default_value = "" ) const;

std::string get_cookie_parameter( const std::string& name, const std::function< std::string ( const std::string& ) >& transform ) const;

template< typename Type, typename std::enable_if< std::is_arithmetic< Type >::value, Type >::type = 0 >
Type get_cookie_parameter( const std::string& name, const Type default_value ) const
{
return Common::parse_parameter( get_cookie_parameter( name ), default_value );
}

std::map< std::string, std::string > get_cookie_parameters( const std::string& name = "" ) const;


float get_query_parameter( const std::string& name, const float default_value ) const;

Expand Down
4 changes: 2 additions & 2 deletions source/corvusoft/restbed/resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
//External Includes

//System Namespaces
using std::set;
using std::vector;
using std::string;
using std::multimap;
using std::function;
Expand Down Expand Up @@ -74,7 +74,7 @@ namespace restbed
m_pimpl->m_paths = { value };
}

void Resource::set_paths( const set< string >& values )
void Resource::set_paths( const vector< string >& values )
{
m_pimpl->m_paths = values;
}
Expand Down
4 changes: 2 additions & 2 deletions source/corvusoft/restbed/resource.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

//System Includes
#include <map>
#include <set>
#include <vector>
#include <string>
#include <functional>

Expand Down Expand Up @@ -56,7 +56,7 @@ namespace restbed
//Setters
void set_path( const std::string& value );

void set_paths( const std::set< std::string >& values );
void set_paths( const std::vector< std::string >& values );

void set_default_header( const std::string& name, const std::string& value );

Expand Down
Loading