Instantly share code, notes, and snippets.
Last active
December 31, 2015 10:39
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save mntone/7974234 to your computer and use it in GitHub Desktop.
Open Niconico 用 Cookie Parser 実装。C++ REST SDK にバックポートの可能性あり。現状は修正 BSD license です。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "pch.h" | |
#include "cookie_parser.h" | |
using namespace mntone::niconico::details; | |
bool cookie_parser::validate( const string_t& string ) const | |
{ | |
const char_t* cookie_str = string.c_str(); | |
const char_t* name_begin = nullptr; | |
const char_t* name_end = nullptr; | |
const char_t* value_begin = nullptr; | |
const char_t* value_end = nullptr; | |
const char_t* expires_begin = nullptr; | |
const char_t* expires_end = nullptr; | |
const char_t* max_age_begin = nullptr; | |
const char_t* max_age_end = nullptr; | |
const char_t* domain_begin = nullptr; | |
const char_t* domain_end = nullptr; | |
const char_t* path_begin = nullptr; | |
const char_t* path_end = nullptr; | |
bool secure_only = false; | |
bool http_only = false; | |
while( inner_parse( | |
cookie_str, | |
name_begin, name_end, | |
value_begin, value_end, | |
expires_begin, expires_end, | |
max_age_begin, max_age_end, | |
domain_begin, domain_end, | |
path_begin, path_end, | |
secure_only, | |
http_only ) ); | |
return cookie_str == string.c_str() + string.length(); | |
} | |
bool cookie_parser::parse( const string_t& string, std::vector<_cookie_components>& components_items ) const | |
{ | |
const char_t* cookie_str = string.c_str(); | |
const char_t* name_begin = nullptr; | |
const char_t* name_end = nullptr; | |
const char_t* value_begin = nullptr; | |
const char_t* value_end = nullptr; | |
const char_t* expires_begin = nullptr; | |
const char_t* expires_end = nullptr; | |
const char_t* max_age_begin = nullptr; | |
const char_t* max_age_end = nullptr; | |
const char_t* domain_begin = nullptr; | |
const char_t* domain_end = nullptr; | |
const char_t* path_begin = nullptr; | |
const char_t* path_end = nullptr; | |
bool secure_only = false; | |
bool http_only = false; | |
while( inner_parse( | |
cookie_str, | |
name_begin, name_end, | |
value_begin, value_end, | |
expires_begin, expires_end, | |
max_age_begin, max_age_end, | |
domain_begin, domain_end, | |
path_begin, path_end, | |
secure_only, | |
http_only ) ) | |
{ | |
_cookie_components components; | |
if( name_begin ) | |
components.name_.assign( name_begin, name_end ); | |
else | |
components.name_.clear(); | |
if( value_begin ) | |
components.value_.assign( value_begin, value_end ); | |
else | |
components.value_.clear(); | |
if( max_age_begin ) | |
{ | |
components.persistent_ = true; | |
stringstream_t buf; | |
buf.write( max_age_begin, max_age_end - max_age_begin ); | |
datetime::interval_type interval; | |
buf >> interval; | |
components.expiry_time_ = datetime::utc_now() + interval; | |
} | |
else if( expires_begin ) | |
{ | |
components.persistent_ = true; | |
string_t expires( expires_begin, expires_end ); | |
components.expiry_time_ = datetime::from_string( expires ); | |
} | |
else | |
{ | |
components.persistent_ = false; | |
components.expiry_time_ = datetime() + std::numeric_limits<datetime::interval_type>::max(); | |
} | |
// TODO: 正準化 | |
if( domain_begin ) | |
{ | |
components.host_only_ = false; | |
components.domain_.assign( domain_begin, domain_end ); | |
std::transform( | |
std::begin( components.domain_ ), | |
std::end( components.domain_ ), | |
std::begin( components.domain_ ), | |
[]( const utility::char_t c ) { return toulower( c ); } ); | |
} | |
else | |
{ | |
components.host_only_ = true; | |
components.domain_ = U( "[canonicalized request-host]" ); | |
} | |
if( path_begin ) | |
components.path_.assign( path_begin, path_end ); | |
else | |
components.path_ = U( "[default-path]" ); | |
components.secure_only_ = secure_only; | |
components.http_only_ = http_only; | |
components_items.push_back( components ); | |
} | |
return components_items.size() != 0 ? true : false; | |
} | |
bool cookie_parser::inner_parse( | |
const char_t*& cookie_str, | |
const char_t*& name_begin, const char_t*& name_end, | |
const char_t*& value_begin, const char_t*& value_end, | |
const char_t*& expires_begin, const char_t*& expires_end, | |
const char_t*& max_age_begin, const char_t*& max_age_end, | |
const char_t*& domain_begin, const char_t*& domain_end, | |
const char_t*& path_begin, const char_t*& path_end, | |
bool& secure_only, | |
bool& http_only ) const | |
{ | |
name_begin = nullptr; | |
name_end = nullptr; | |
value_begin = nullptr; | |
value_end = nullptr; | |
path_begin = nullptr; | |
path_end = nullptr; | |
max_age_begin = nullptr; | |
max_age_end = nullptr; | |
domain_begin = nullptr; | |
domain_end = nullptr; | |
expires_begin = nullptr; | |
expires_end = nullptr; | |
secure_only = false; | |
http_only = false; | |
auto& p = cookie_str; | |
name_begin = p; | |
{ | |
for( ; *p != U( ',' ) && *p != U( '\0' ); ++p ) | |
{ | |
if( *p == U( '=' ) ) | |
{ | |
name_end = p++; | |
break; | |
} | |
} | |
if( !name_end ) | |
return false; | |
trim( name_begin, name_end ); | |
if( !std::all_of( name_begin, name_end, &is_token ) ) | |
return false; | |
} | |
value_begin = p; | |
{ | |
for( ;; ++p ) | |
{ | |
if( *p == U( ';' ) || *p == U( ',' ) || *p == U( '\0' ) ) | |
{ | |
value_end = p++; | |
break; | |
} | |
} | |
if( !value_end ) | |
return false; | |
if( *value_begin == U( '"' ) ) | |
{ | |
if( *--value_end != U( '"' ) ) | |
return false; | |
++value_begin; | |
} | |
trim( value_begin, value_end ); | |
if( !std::all_of( value_begin, value_end, &is_octet ) ) | |
return false; | |
} | |
while( *( p - 1 ) != U( ',' ) && *( p - 1 ) != U( '\0' ) ) | |
{ | |
const char_t* begin = p; | |
const char_t* end = nullptr; | |
for( ;; ++p ) | |
{ | |
if( *p == U( '=' ) || *p == U( ';' ) || *p == U( ',' ) || *p == U( '\0' ) ) | |
{ | |
end = p++; | |
break; | |
} | |
} | |
if( !end ) | |
return false; | |
trim( begin, end ); | |
string_t key( begin, end ); | |
std::transform( std::begin( key ), std::end( key ), std::begin( key ), &toulower ); | |
if( key == U( "expires" ) ) | |
{ | |
expires_begin = p; | |
bool first_comma = false; | |
for( ;; ++p ) | |
{ | |
if( *p == U( ',' ) ) | |
{ | |
if( first_comma ) | |
{ | |
expires_end = p++; | |
break; | |
} | |
else | |
first_comma = true; | |
} | |
if( *p == U( ';' ) || *p == U( '\0' ) ) | |
{ | |
expires_end = p++; | |
break; | |
} | |
} | |
if( !expires_end ) | |
return false; | |
trim( expires_begin, expires_end ); | |
} | |
else if( key == U( "max-age" ) ) | |
{ | |
max_age_begin = p; | |
for( ;; ++p ) | |
{ | |
if( *p == U( ';' ) || *p == U( ',' ) || *p == U( '\0' ) ) | |
{ | |
max_age_end = p++; | |
break; | |
} | |
} | |
if( !max_age_end ) | |
return false; | |
trim( max_age_begin, max_age_end ); | |
if( !( isudigit( *max_age_begin ) || *max_age_begin == U( '-' ) ) | |
&& !std::all_of( max_age_begin + 1, max_age_end, &isudigit ) ) | |
return false; | |
} | |
else if( key == U( "domain" ) ) | |
{ | |
domain_begin = p; | |
for( ;; ++p ) | |
{ | |
if( *p == U( ';' ) || *p == U( ',' ) || *p == U( '\0' ) ) | |
{ | |
domain_end = p++; | |
break; | |
} | |
} | |
if( !domain_end ) | |
return false; | |
trim( domain_begin, domain_end ); | |
if( domain_begin == domain_end ) | |
{ | |
domain_begin = nullptr; | |
domain_end = nullptr; | |
} | |
else if( *domain_begin == U( '.' ) ) | |
++domain_begin; | |
} | |
else if( key == U( "path" ) ) | |
{ | |
path_begin = p; | |
for( ;; ++p ) | |
{ | |
if( *p == U( ';' ) || *p == U( ',' ) || *p == U( '\0' ) ) | |
{ | |
path_end = p++; | |
break; | |
} | |
} | |
if( !path_end ) | |
return false; | |
trim( path_begin, path_end ); | |
if( path_begin == path_end || *path_begin != U( '/' ) ) | |
{ | |
path_begin = nullptr; | |
path_end = nullptr; | |
} | |
} | |
else if( key == U( "secure" ) ) | |
secure_only = true; | |
else if( key == U( "httponly" ) ) | |
http_only = true; | |
} | |
return true; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment