// Copyright Dean Michael Berris 2007.
// Copyright Michael Dickey 2008.
// Copyright Kim Grasman 2008.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef __NETWORK_UTILS_URI_20080916_HPP__
#define __NETWORK_UTILS_URI_20080916_HPP__
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/sequence/intrinsic/at_key.hpp>
#include <boost/spirit.hpp>
#include <boost/spirit/phoenix.hpp>
#include <boost/network/uri_fwd.hpp>
#include <boost/network/message/traits.hpp>
#include <boost/network/detail/directive_base.hpp>
namespace boost { namespace network {
/** uri.hpp
*
* This file implements the basic request object required
* by the HTTP client implementation. The uri
* object encapsulates a URI which is parsed at runtime.
* The data is broken up according to the URI specifications
* RFC 3986 -- http://www.ietf.org/rfc/rfc3986.txt -- using
* Fusion, and exposed as a typed struct.
*/
template <
class Tag
>
class basic_uri {
public:
typedef Tag tag;
typedef typename string<tag>::type string_type;
basic_uri() {
}
explicit basic_uri(string_type const & uri_) {
parse(uri_);
}
bool valid() const {
return (!scheme().empty() && !host().empty());
}
string_type const& scheme() const {
return fusion::at_key<typename parser_tags::scheme>(parts);
}
string_type& scheme() {
return fusion::at_key<typename parser_tags::scheme>(parts);
}
string_type const& user_info() const {
return fusion::at_key<typename parser_tags::user_info>(parts);
}
string_type& user_info() {
return fusion::at_key<typename parser_tags::user_info>(parts);
}
string_type const& host() const {
return fusion::at_key<typename parser_tags::host>(parts);
}
string_type& host() {
return fusion::at_key<typename parser_tags::host>(parts);
}
unsigned int& port() {
return fusion::at_key<typename parser_tags::port>(parts);
}
unsigned int port() const {
return fusion::at_key<typename parser_tags::port>(parts);
}
string_type& path() {
return fusion::at_key<typename parser_tags::path>(parts);
}
string_type const& path() const {
return fusion::at_key<typename parser_tags::path>(parts);
}
string_type& query() {
return fusion::at_key<typename parser_tags::query>(parts);
}
string_type const& query() const {
return fusion::at_key<typename parser_tags::query>(parts);
}
string_type& fragment() {
return fusion::at_key<typename parser_tags::fragment>(parts);
}
string_type const& fragment() const {
return fusion::at_key<typename parser_tags::fragment>(parts);
}
string_type const authority() const {
typename ostringstream<tag>::type builder;
if (!scheme().empty())
builder << scheme() << "://";
if (!user_info().empty())
builder << user_info() << "@";
if (!host().empty())
builder << host();
if (port() != 0)
builder << ":" << port();
return builder.str();
}
bool operator==(basic_uri const& other) const {
return (scheme() == other.scheme() &&
host() == other.host() &&
port() == other.port() &&
path() == other.path() &&
query() == other.query() &&
fragment() == other.fragment());
}
string_type str() const {
if (!valid())
return string_type();
typename ostringstream<tag>::type builder;
builder << authority();
builder << path();
if (!query().empty())
builder << "?" << query();
if (!fragment().empty())
builder << "#" << fragment();
return builder.str();
}
basic_uri& operator<<(const basic_uri& other) {
parse(other.str());
return *this;
}
private:
void parse(string_type const& uri_) {
using namespace boost::spirit;
using namespace phoenix;
string_type possible_user_info;
// Run the parser
boost::spirit::parse(
uri_.begin(), uri_.end(),
// the parser
(
(+alnum_p) [
var(fusion::at_key<typename parser_tags::scheme>(parts))
= construct_<string_type>(arg1, arg2)
]
>> str_p("://")
)
>>
!((+(alnum_p | '.' | '-' | '_')) [
// build up what might be user info...
var(possible_user_info) = construct_<string_type>(arg1, arg2)
] >> ch_p('@')) [
// ... and store it as soon as we match the @
var(fusion::at_key<typename parser_tags::user_info>(parts))
= var(possible_user_info)
]
>>
(+(alnum_p | '.' | '-' | '_')) [
var(fusion::at_key<typename parser_tags::host>(parts))
= construct_<string_type>(arg1, arg2)
]
>>
!(
ch_p(':')
>>
uint_p [
var(fusion::at_key<typename parser_tags::port>(parts))
= arg1
])
>>
*(ch_p('/') >> (+(alnum_p | '/' | '%' | '_' | '-' | '.'))) [
var(fusion::at_key<typename parser_tags::path>(parts))
= construct_<string_type>(arg1, arg2)
]
>>
!(
ch_p('?')
>>
(+(alnum_p | '&' | '=' | '%' | '_' )) [
var(fusion::at_key<typename parser_tags::query>(parts))
= construct_<string_type>(arg1, arg2)
]
)
>> !(ch_p('#')
>>
(+(alnum_p | '_' | '%' | '-')) [
var(fusion::at_key<typename parser_tags::fragment>(parts))
= construct_<string_type>(arg1, arg2)
]
)
>>
end_p
, nothing_p // no skip parser
);
}
struct parser_tags {
struct scheme {};
struct user_info {};
struct host {};
struct port {};
struct path {};
struct query {};
struct fragment {};
};
typedef fusion::map<
fusion::pair< typename parser_tags::scheme, string_type >,
fusion::pair< typename parser_tags::user_info, string_type >,
fusion::pair< typename parser_tags::host, string_type >,
fusion::pair< typename parser_tags::port, unsigned int >,
fusion::pair< typename parser_tags::path, string_type >,
fusion::pair< typename parser_tags::query, string_type >,
fusion::pair< typename parser_tags::fragment, string_type >
> uri_parts_type;
uri_parts_type parts;
};
} // namespace network
} // namespace boost
#include <boost/network/uri/directives.hpp>
// pull in directives header file
#endif // __NETWORK_UTILS_URI_20080916_HPP__