Servus  1.5.1
C++ network oriented utilities including a zeroconf implementation
servus::URI Class Reference

The URI class parses the given uri using the generic syntax from RFC3986 and RFC6570. More...

#include <uri.h>

+ Collaboration diagram for servus::URI:

Public Types

typedef std::map< std::string, std::string > KVMap
 
typedef KVMap::const_iterator ConstKVIter
 

Public Member Functions

 URI ()
 Construct an empty URI. More...
 
 URI (const std::string &uri)
 
 URI (const char *uri)
 
 URI (const URI &from)
 Copy-construct an URI. More...
 
URIoperator= (const URI &rhs)
 Assign the data from another URI. More...
 
bool operator== (const URI &rhs) const
 Equals operator.
 
bool operator!= (const URI &rhs) const
 Not equals operator.
 
Getters for uri data
const std::string & getScheme () const
 
const std::string & getUserinfo () const
 
uint16_t getPort () const
 
const std::string & getHost () const
 
std::string getAuthority () const
 Return the compound authority part of the URI. More...
 
const std::string & getPath () const
 
const std::string & getQuery () const
 
const std::string & getFragment () const
 
Setters for uri data.
void setScheme (const std::string &scheme)
 
void setUserInfo (const std::string &userinfo)
 
void setHost (const std::string &host)
 
void setPort (uint16_t port)
 
void setPath (const std::string &path)
 
void setQuery (const std::string &query)
 
void setFragment (const std::string &fragment)
 
Access to key-value data in query.
ConstKVIter queryBegin () const
 
ConstKVIter queryEnd () const
 
ConstKVIter findQuery (const std::string &key) const
 
void addQuery (const std::string &key, const std::string &value)
 Add a key-value pair to the query. More...
 

Detailed Description

The URI class parses the given uri using the generic syntax from RFC3986 and RFC6570.

* http://bob@www.example.com:8080/path/?key=value&foo=bar#fragment
* ^   ^  ^  ^               ^    ^     ^                 ^
* a   b  c  d               e    f     g                 h
*
* URI part  Range   String
* scheme    [a, b)  "http"
* userinfo [c, d) bob
* host  [d, e)  "www.example.com"
* port (e, f) 8080
* path  [f, g)  "/path/"
* query (g, h)  "key=value&foo=bar"
* fragment  (h,-) "fragment"
* 

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:

/* Copyright (c) 2013-2016, Ahmet.Bilgili@epfl.ch
* Juan Hernando <jhernando@fi.upm.es>
*
* This file is part of Servus <https://github.com/HBPVIS/Servus>
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License version 3.0 as published
* by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define BOOST_TEST_MODULE servus_uri
#include <boost/test/unit_test.hpp>
#include <servus/uri.h>
BOOST_AUTO_TEST_CASE(uri_parts)
{
const std::string uriStr =
"http://bob@www.example.com:8080/path/?key=value&foo=bar#fragment";
BOOST_REQUIRE_NO_THROW(servus::URI uri(uriStr));
servus::URI uri(uriStr);
BOOST_CHECK_EQUAL(uri.getScheme(), "http");
BOOST_CHECK_EQUAL(uri.getHost(), "www.example.com");
BOOST_CHECK_EQUAL(uri.getUserinfo(), "bob");
BOOST_CHECK_EQUAL(uri.getPort(), 8080);
BOOST_CHECK_EQUAL(uri.getAuthority(), "bob@www.example.com:8080");
BOOST_CHECK_EQUAL(uri.getPath(), "/path/");
BOOST_CHECK_EQUAL(uri.getQuery(), "key=value&foo=bar");
BOOST_CHECK_EQUAL(uri.getFragment(), "fragment");
const servus::URI hostPortURI("foo://hostname:12345");
BOOST_CHECK_EQUAL(hostPortURI.getScheme(), "foo");
BOOST_CHECK_EQUAL(hostPortURI.getHost(), "hostname");
BOOST_CHECK(hostPortURI.getUserinfo().empty());
BOOST_CHECK_EQUAL(hostPortURI.getPort(), 12345);
BOOST_CHECK_EQUAL(hostPortURI.getAuthority(), "hostname:12345");
const servus::URI userHostURI("foo://alice@hostname");
BOOST_CHECK_EQUAL(userHostURI.getScheme(), "foo");
BOOST_CHECK_EQUAL(userHostURI.getHost(), "hostname");
BOOST_CHECK_EQUAL(userHostURI.getUserinfo(), "alice");
BOOST_CHECK_EQUAL(userHostURI.getPort(), 0);
BOOST_CHECK_EQUAL(userHostURI.getAuthority(), "alice@hostname");
const servus::URI uppercaseURI("FOO://");
BOOST_CHECK_EQUAL(uppercaseURI.getScheme(), "foo");
servus::URI noauthority("scheme:///path");
BOOST_CHECK_EQUAL(noauthority.getScheme(), "scheme");
BOOST_CHECK(noauthority.getHost().empty());
BOOST_CHECK(noauthority.getAuthority().empty());
BOOST_CHECK_EQUAL(noauthority.getPath(), "/path");
BOOST_CHECK(noauthority.getQuery().empty());
BOOST_CHECK(noauthority.getFragment().empty());
servus::URI query("scheme:///?query=no_fragment");
BOOST_CHECK_EQUAL(query.getScheme(), "scheme");
BOOST_CHECK_EQUAL(query.getPath(), "/");
BOOST_CHECK(query.getHost().empty());
BOOST_CHECK_EQUAL(query.getQuery(), "query=no_fragment");
BOOST_CHECK(query.getFragment().empty());
servus::URI fragment("scheme:///#fragment,no,query");
BOOST_CHECK_EQUAL(fragment.getScheme(), "scheme");
BOOST_CHECK_EQUAL(fragment.getPath(), "/");
BOOST_CHECK(fragment.getHost().empty());
BOOST_CHECK(fragment.getQuery().empty());
BOOST_CHECK_EQUAL(fragment.getFragment(), "fragment,no,query");
}
BOOST_AUTO_TEST_CASE(setters)
{
uri.setScheme("foo");
BOOST_CHECK_EQUAL(uri.getScheme(), "foo");
uri.setHost("host");
BOOST_CHECK_EQUAL(uri.getHost(), "host");
uri.setPort(12345);
BOOST_CHECK_EQUAL(uri.getPort(), 12345);
uri.setPath("path");
BOOST_CHECK_EQUAL(uri.getPath(), "path");
uri.setFragment("fragment");
BOOST_CHECK_EQUAL(uri.getFragment(), "fragment");
// The query setter is tested independently.
}
BOOST_AUTO_TEST_CASE(empty_uri)
{
servus::URI empty;
BOOST_CHECK(empty.getScheme().empty());
BOOST_CHECK(empty.getHost().empty());
BOOST_CHECK(empty.getUserinfo().empty());
BOOST_CHECK_EQUAL(empty.getPort(), 0);
BOOST_CHECK(empty.getPath().empty());
BOOST_CHECK(empty.getQuery().empty());
BOOST_CHECK(empty.getFragment().empty());
}
BOOST_AUTO_TEST_CASE(file_uris)
{
servus::URI file1("/bla.txt");
BOOST_CHECK_EQUAL(file1.getPath(), "/bla.txt");
BOOST_CHECK(file1.getHost().empty());
BOOST_CHECK(file1.getScheme().empty());
BOOST_CHECK(file1.getQuery().empty());
BOOST_CHECK(file1.getFragment().empty());
servus::URI file2("bla.txt");
BOOST_CHECK_EQUAL(file2.getPath(), "bla.txt");
BOOST_CHECK(file2.getHost().empty());
BOOST_CHECK(file2.getScheme().empty());
BOOST_CHECK(file2.getQuery().empty());
BOOST_CHECK(file2.getFragment().empty());
servus::URI file3("file:///bla.txt");
BOOST_CHECK_EQUAL(file3.getPath(), "/bla.txt");
BOOST_CHECK(file3.getHost().empty());
BOOST_CHECK_EQUAL(file3.getScheme(), "file");
BOOST_CHECK(file3.getQuery().empty());
BOOST_CHECK(file3.getFragment().empty());
// This is an exception to the generic syntax
servus::URI file4("file://bla.txt");
BOOST_CHECK_EQUAL(file4.getPath(), "bla.txt");
BOOST_CHECK(file4.getHost().empty());
BOOST_CHECK_EQUAL(file4.getScheme(), "file");
BOOST_CHECK(file4.getQuery().empty());
BOOST_CHECK(file4.getFragment().empty());
servus::URI file5("scheme://bla.txt");
BOOST_CHECK_EQUAL(file5.getHost(), "bla.txt");
BOOST_CHECK(file5.getPath().empty());
BOOST_CHECK_EQUAL(file5.getScheme(), "scheme");
BOOST_CHECK(file5.getQuery().empty());
BOOST_CHECK(file5.getFragment().empty());
}
BOOST_AUTO_TEST_CASE(uri_query)
{
const std::string uriStr =
"http://bob@www.example.com:8080/path/?key=value&list=10,53#fragment";
servus::URI uri(uriStr);
BOOST_REQUIRE(uri.findQuery("key") != uri.queryEnd());
BOOST_REQUIRE(uri.findQuery("list") != uri.queryEnd());
BOOST_CHECK(uri.findQuery("bar") == uri.queryEnd());
BOOST_CHECK_EQUAL(uri.findQuery("key")->second, "value");
BOOST_CHECK_EQUAL(uri.findQuery("list")->second, "10,53");
uri.addQuery("hans", "dampf");
BOOST_REQUIRE(uri.findQuery("hans") != uri.queryEnd());
BOOST_CHECK_EQUAL(uri.findQuery("key")->second, "value");
BOOST_CHECK_EQUAL(uri.findQuery("list")->second, "10,53");
BOOST_CHECK_EQUAL(uri.findQuery("hans")->second, "dampf");
BOOST_CHECK(uri.getQuery().find("hans=dampf") != std::string::npos);
uri.setQuery("firstkey=val1&secondkey=768");
BOOST_CHECK_EQUAL(uri.getQuery(), "firstkey=val1&secondkey=768");
BOOST_CHECK(uri.findQuery("key") == uri.queryEnd());
BOOST_CHECK(uri.findQuery("list") == uri.queryEnd());
BOOST_CHECK(uri.findQuery("hans") == uri.queryEnd());
BOOST_REQUIRE(uri.findQuery("firstkey") != uri.queryEnd());
BOOST_REQUIRE(uri.findQuery("secondkey") != uri.queryEnd());
BOOST_CHECK_EQUAL(uri.findQuery("firstkey")->second, "val1");
BOOST_CHECK_EQUAL(uri.findQuery("secondkey")->second, "768");
}
BOOST_AUTO_TEST_CASE(uri_comparisons)
{
const std::string uriStr =
"http://bob@www.example.com:8080/path/?key=value&foo=bar#fragment";
servus::URI uri(uriStr);
BOOST_CHECK(uri == uri);
BOOST_CHECK(uri == servus::URI(uriStr));
BOOST_CHECK(uri !=
servus::URI("http://bob@www.example.com:8080/path/?key=value"));
BOOST_CHECK(uri != servus::URI("http://bob@www.example.com:8030/path/"
"?key=value&foo=bar#fragment"));
BOOST_CHECK(
uri != servus::URI(
"http://bob@foo.com:8080/path/?key=value&foo=bar#fragment"));
std::stringstream sstr;
sstr << uri;
BOOST_CHECK_EQUAL(sstr.str(), uriStr);
sstr.str("");
sstr << servus::URI("http://www.example.com/path");
BOOST_CHECK_EQUAL(sstr.str(), "http://www.example.com/path");
sstr.str("");
sstr << servus::URI("/path");
BOOST_CHECK_EQUAL(sstr.str(), "/path");
}
BOOST_AUTO_TEST_CASE(invalid_uri)
{
BOOST_CHECK_THROW(servus::URI uri("bad_schema://"), std::exception);
BOOST_CHECK_THROW(servus::URI uri("8ad-schema://"), std::exception);
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:"), std::exception);
BOOST_CHECK_THROW(servus::URI skypeCrasher("http://:"), std::exception);
}
BOOST_AUTO_TEST_CASE(corner_cases)
{
servus::URI uri1("path/foo:bar");
BOOST_CHECK_EQUAL(uri1.getPath(), "path/foo:bar");
servus::URI uri2("//path/foo:bar");
BOOST_CHECK_EQUAL(uri2.getPath(), "//path/foo:bar");
servus::URI uri3("?/##");
BOOST_CHECK_EQUAL(uri3.getQuery(), "/");
BOOST_CHECK_EQUAL(uri3.getFragment(), "#");
servus::URI uri4("#:");
BOOST_CHECK_EQUAL(uri4.getFragment(), ":");
servus::URI uri5("/foo#?");
BOOST_CHECK_EQUAL(uri5.getPath(), "/foo");
BOOST_CHECK_EQUAL(uri5.getFragment(), "?");
servus::URI uri6("foo://*:0");
BOOST_CHECK_EQUAL(uri6.getScheme(), "foo");
BOOST_CHECK_EQUAL(uri6.getHost(), "*");
}
BOOST_AUTO_TEST_CASE(print)
{
BOOST_CHECK_EQUAL(std::to_string(uri), "");
uri.setPath("/path");
BOOST_CHECK_EQUAL(std::to_string(uri), "/path");
uri.setUserInfo("user");
// No hostname yet, user info ignored.
BOOST_CHECK_EQUAL(std::to_string(uri), "/path");
uri.setPort(1024);
// No hostname yet, port ignored.
BOOST_CHECK_EQUAL(std::to_string(uri), "/path");
uri.setHost("localhost");
BOOST_CHECK_EQUAL(std::to_string(uri), "user@localhost:1024/path");
uri.setScheme("foo");
BOOST_CHECK_EQUAL(std::to_string(uri), "foo://user@localhost:1024/path");
uri.setHost("");
BOOST_CHECK_EQUAL(std::to_string(uri), "foo:///path");
uri.setHost("localhost");
uri.setFragment("fragment");
BOOST_CHECK_EQUAL(std::to_string(uri),
"foo://user@localhost:1024/path#fragment");
uri.setFragment("");
uri.addQuery("key", "value");
BOOST_CHECK_EQUAL(std::to_string(uri),
"foo://user@localhost:1024/path?key=value");
uri.setFragment("fragment");
BOOST_CHECK_EQUAL(std::to_string(uri),
"foo://user@localhost:1024/path?key=value#fragment");
}
BOOST_AUTO_TEST_CASE(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");
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);
}
BOOST_AUTO_TEST_CASE(query_without_value)
{
const servus::URI uri("?foo=&bar=foo&blubb");
BOOST_REQUIRE(uri.findQuery("foo") != uri.queryEnd());
BOOST_REQUIRE(uri.findQuery("bar") != uri.queryEnd());
BOOST_REQUIRE(uri.findQuery("blubb") != uri.queryEnd());
BOOST_CHECK_EQUAL(uri.findQuery("bar")->second, "foo");
BOOST_CHECK(uri.findQuery("foo")->second.empty());
BOOST_CHECK(uri.findQuery("blubb")->second.empty());
}

Definition at line 62 of file uri.h.

Constructor & Destructor Documentation

servus::URI::URI ( )

Construct an empty URI.

servus::URI::URI ( const std::string &  uri)
explicit
Parameters
uriURI string to parse.
Exceptions
std::exceptionfor incomplete URIs, and std::invalid_argument if the port is not a number.
servus::URI::URI ( const char *  uri)
explicit

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

servus::URI::URI ( const URI from)

Copy-construct an URI.

Member Function Documentation

void servus::URI::addQuery ( const std::string &  key,
const std::string &  value 
)

Add a key-value pair to the query.

ConstKVIter servus::URI::findQuery ( const std::string &  key) const
Returns
a const iterator to the given key, or queryEnd().
std::string servus::URI::getAuthority ( ) const

Return the compound authority part of the URI.

User info added only if not empty, port number added only if it's different from 0.

URI& servus::URI::operator= ( const URI rhs)

Assign the data from another URI.

ConstKVIter servus::URI::queryBegin ( ) const
Returns
a const iterator to the beginning of the query map.
ConstKVIter servus::URI::queryEnd ( ) const
Returns
a const iterator to end beginning of the query map.

The documentation for this class was generated from the following file: