Skip to content
Merged
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
1 change: 1 addition & 0 deletions doc/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# git master {#master}

* Add a function to get the host corresponding to a given instance.
* Fix schema parsing in servus::URI: must be schema://, not only schema:

# Release 1.2 (02-11-2015) {#Release010200}

Expand Down
28 changes: 9 additions & 19 deletions servus/uri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,22 @@ class uri_parse : public std::exception
std::stringstream _error;
};

enum URIPart { SCHEME = 0, AUTHORITY, PATH, QUERY, FRAGMENT, HIERARCHY };
enum URIPart { SCHEME = 0, AUTHORITY, PATH, QUERY, FRAGMENT };

bool _parseURIPart( std::string& input, const URIPart& part,
std::string& output )
{
#ifndef NDEBUG
const char requireFirst[] = { 0, 0, 0, '?', '#' };
#endif
const char* const separators[] = { ":", "/?#", "?#", "#", "" };
const char* const separators[] = { "://", "/?#", "?#", "#", "" };
const char* const disallowed[] = { "/?#", 0, 0, 0, 0 };
const bool fullSeparator[] = { true, false, false, false, false };
const bool needsSeparator[] = { true, false, false, false, false };
const size_t skip[] = { 0, 0, 0, 1, 1 };
const size_t postSkip[] = { 1, 0, 0, 0, 0 };
const size_t pos = input.find_first_of( separators[part] );
const size_t postSkip[] = { 3, 0, 0, 0, 0 };
const size_t pos = fullSeparator[part] ? input.find( separators[part] )
: input.find_first_of( separators[part] );

if( pos == std::string::npos )
{
Expand Down Expand Up @@ -194,9 +196,8 @@ class URI
private:
URIData _uriData;

void _parseURI( const std::string& uri )
void _parseURI( std::string input )
{
std::string input = uri;
URIPart part = SCHEME;
while( !input.empty( ))
{
Expand All @@ -214,23 +215,11 @@ class URI
throw std::invalid_argument("");
}
part = _uriData.scheme == "file" || _uriData.scheme.empty() ?
PATH : HIERARCHY;
PATH : AUTHORITY;
// from http://en.wikipedia.org/wiki/File_URI_scheme:
// "file:///foo.txt" is okay, while "file://foo.txt"
// is not, although some interpreters manage to handle
// the latter. We are "some".
if( _uriData.scheme == "file" && input.substr( 0, 2 ) == "//" )
input = input.substr( 2 );
break;
case HIERARCHY:
// Distinguishing <path> from <authority path>
if( input.substr( 0, 2 ) == "//" )
{
part = AUTHORITY;
input = input.substr( 2 );
}
else
part = PATH;
break;
case AUTHORITY:
{
Expand Down Expand Up @@ -279,6 +268,7 @@ URI::URI( const URI& from )
: _impl( new detail::URI( *from._impl ))
{
}

servus::URI::~URI()
{
delete _impl;
Expand Down
3 changes: 3 additions & 0 deletions servus/uri.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ namespace detail { class URI; }
* Queries are parsed into key-value pairs and can be accessed using
* findQuery(), queryBegin() and queryEnd().
*
* We enforce schemas to have the separator "://", not only ":" which is enough
* for the RFC specification.
*
* Example: @include tests/uri.cpp
*/
class URI
Expand Down
41 changes: 23 additions & 18 deletions tests/uri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(test_uri_parts)
BOOST_CHECK_EQUAL( userHostURI.getPort(), 0 );
BOOST_CHECK_EQUAL( userHostURI.getAuthority(), "alice@hostname" );

const servus::URI uppercaseURI( "FOO:" );
const servus::URI uppercaseURI( "FOO://" );
BOOST_CHECK_EQUAL( uppercaseURI.getScheme(), "foo" );

servus::URI noauthority( "scheme:///path" );
Expand Down Expand Up @@ -145,20 +145,6 @@ BOOST_AUTO_TEST_CASE(test_file_uris)
BOOST_CHECK_EQUAL( file5.getScheme(), "scheme" );
BOOST_CHECK( file5.getQuery().empty( ));
BOOST_CHECK( file5.getFragment().empty( ));

servus::URI path1( "foo:/bla.txt" );
BOOST_CHECK( path1.getHost().empty( ));
BOOST_CHECK_EQUAL( path1.getPath(), "/bla.txt" );
BOOST_CHECK_EQUAL( path1.getScheme(), "foo" );
BOOST_CHECK( path1.getQuery().empty( ));
BOOST_CHECK( path1.getFragment().empty( ));

servus::URI path2( "foo:bla.txt" );
BOOST_CHECK( path2.getHost().empty( ));
BOOST_CHECK_EQUAL( path2.getPath(), "bla.txt" );
BOOST_CHECK_EQUAL( path2.getScheme(), "foo" );
BOOST_CHECK( path2.getQuery().empty( ));
BOOST_CHECK( path2.getFragment().empty( ));
}

BOOST_AUTO_TEST_CASE(test_uri_query)
Expand Down Expand Up @@ -210,11 +196,11 @@ BOOST_AUTO_TEST_CASE(test_uri_comparisons)

BOOST_AUTO_TEST_CASE(test_invalid_uri)
{
BOOST_CHECK_THROW( servus::URI uri( "bad_schema:" ),
BOOST_CHECK_THROW( servus::URI uri( "bad_schema://" ),
std::exception );
BOOST_CHECK_THROW( servus::URI uri( "8ad-schema:" ),
BOOST_CHECK_THROW( servus::URI uri( "8ad-schema://" ),
std::exception );
BOOST_CHECK_NO_THROW( servus::URI uri( "g00d-sch+ma:" ));
BOOST_CHECK_NO_THROW( servus::URI uri( "g00d-sch+ma://" ));
BOOST_CHECK_THROW( servus::URI uri( "http://host:port" ),
std::exception );
BOOST_CHECK_THROW( servus::URI uri( "http://host:" ),
Expand Down Expand Up @@ -281,5 +267,24 @@ BOOST_AUTO_TEST_CASE(test_print)
uri.setFragment( "fragment" );
BOOST_CHECK_EQUAL( std::to_string( uri ),
"foo://user@localhost:1024/path?key=value#fragment" );
}

BOOST_AUTO_TEST_CASE(test_host_port_without_schema)
{
const servus::URI uri( "host:12345" );
BOOST_CHECK_EQUAL( uri.getHost(), "" );
BOOST_CHECK_EQUAL( uri.getPort(), 0 );
BOOST_CHECK_EQUAL( uri.getPath(), "host:12345" );

servus::URI uri2;
uri2.setHost( "host" );
uri2.setPort( 12345 );
BOOST_CHECK( uri2.getScheme().empty( ));
BOOST_CHECK_EQUAL( uri2.getHost(), "host" );
BOOST_CHECK_EQUAL( uri2.getPort(), 12345 );

const servus::URI uri3( uri2 );
BOOST_CHECK( uri3.getScheme().empty( ));
BOOST_CHECK_EQUAL( uri3.getHost(), "host" );
BOOST_CHECK_EQUAL( uri3.getPort(), 12345 );
}