spectrum2/3rdparty/cpprestsdk/include/cpprest/base_uri.h
2015-11-19 15:19:14 +01:00

450 lines
17 KiB
C++

/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Protocol independent support for URIs.
*
* For the latest on this and related APIs, please see http://casablanca.codeplex.com.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#pragma once
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <functional>
#include "cpprest/asyncrt_utils.h"
#include "cpprest/details/basic_types.h"
namespace web {
namespace details
{
struct uri_components
{
uri_components() : m_path(_XPLATSTR("/")), m_port(-1)
{}
uri_components(const uri_components &other) :
m_scheme(other.m_scheme),
m_host(other.m_host),
m_user_info(other.m_user_info),
m_path(other.m_path),
m_query(other.m_query),
m_fragment(other.m_fragment),
m_port(other.m_port)
{}
uri_components & operator=(const uri_components &other)
{
if (this != &other)
{
m_scheme = other.m_scheme;
m_host = other.m_host;
m_user_info = other.m_user_info;
m_path = other.m_path;
m_query = other.m_query;
m_fragment = other.m_fragment;
m_port = other.m_port;
}
return *this;
}
uri_components(uri_components &&other) CPPREST_NOEXCEPT :
m_scheme(std::move(other.m_scheme)),
m_host(std::move(other.m_host)),
m_user_info(std::move(other.m_user_info)),
m_path(std::move(other.m_path)),
m_query(std::move(other.m_query)),
m_fragment(std::move(other.m_fragment)),
m_port(other.m_port)
{}
uri_components & operator=(uri_components &&other) CPPREST_NOEXCEPT
{
if (this != &other)
{
m_scheme = std::move(other.m_scheme);
m_host = std::move(other.m_host);
m_user_info = std::move(other.m_user_info);
m_path = std::move(other.m_path);
m_query = std::move(other.m_query);
m_fragment = std::move(other.m_fragment);
m_port = other.m_port;
}
return *this;
}
_ASYNCRTIMP utility::string_t join();
utility::string_t m_scheme;
utility::string_t m_host;
utility::string_t m_user_info;
utility::string_t m_path;
utility::string_t m_query;
utility::string_t m_fragment;
int m_port;
};
}
/// <summary>
/// A single exception type to represent errors in parsing, encoding, and decoding URIs.
/// </summary>
class uri_exception : public std::exception
{
public:
uri_exception(std::string msg) : m_msg(std::move(msg)) {}
~uri_exception() CPPREST_NOEXCEPT {}
const char* what() const CPPREST_NOEXCEPT
{
return m_msg.c_str();
}
private:
std::string m_msg;
};
/// <summary>
/// A flexible, protocol independent URI implementation.
///
/// URI instances are immutable. Querying the various fields on an emtpy URI will return empty strings. Querying
/// various diagnostic members on an empty URI will return false.
/// </summary>
/// <remarks>
/// This implementation accepts both URIs ('http://msn.com/path') and URI relative-references
/// ('/path?query#frag').
///
/// This implementation does not provide any scheme-specific handling -- an example of this
/// would be the following: 'http://path1/path'. This is a valid URI, but it's not a valid
/// http-uri -- that is, it's syntactically correct but does not conform to the requirements
/// of the http scheme (http requires a host).
/// We could provide this by allowing a pluggable 'scheme' policy-class, which would provide
/// extra capability for validating and canonicalizing a URI according to scheme, and would
/// introduce a layer of type-safety for URIs of differing schemes, and thus differing semantics.
///
/// One issue with implementing a scheme-independent URI facility is that of comparing for equality.
/// For instance, these URIs are considered equal 'http://msn.com', 'http://msn.com:80'. That is --
/// the 'default' port can be either omitted or explicit. Since we don't have a way to map a scheme
/// to it's default port, we don't have a way to know these are equal. This is just one of a class of
/// issues with regard to scheme-specific behavior.
/// </remarks>
class uri
{
public:
/// <summary>
/// The various components of a URI. This enum is used to indicate which
/// URI component is being encoded to the encode_uri_component. This allows
/// specific encoding to be performed.
///
/// Scheme and port don't allow '%' so they don't need to be encoded.
/// </summary>
class components
{
public:
enum component
{
user_info,
host,
path,
query,
fragment,
full_uri
};
};
/// <summary>
/// Encodes a URI component according to RFC 3986.
/// Note if a full URI is specified instead of an individual URI component all
/// characters not in the unreserved set are escaped.
/// </summary>
/// <param name="raw">The URI as a string.</param>
/// <returns>The encoded string.</returns>
_ASYNCRTIMP static utility::string_t __cdecl encode_uri(const utility::string_t &raw, uri::components::component = components::full_uri);
/// <summary>
/// Encodes a string by converting all characters except for RFC 3986 unreserved characters to their
/// hexadecimal representation.
/// </summary>
/// <param name="utf8data">The UTF-8 string data.</param>
/// <returns>The encoded string.</returns>
_ASYNCRTIMP static utility::string_t __cdecl encode_data_string(const utility::string_t &utf8data);
/// <summary>
/// Decodes an encoded string.
/// </summary>
/// <param name="encoded">The URI as a string.</param>
/// <returns>The decoded string.</returns>
_ASYNCRTIMP static utility::string_t __cdecl decode(const utility::string_t &encoded);
/// <summary>
/// Splits a path into its hierarchical components.
/// </summary>
/// <param name="path">The path as a string</param>
/// <returns>A <c>std::vector&lt;utility::string_t&gt;</c> containing the segments in the path.</returns>
_ASYNCRTIMP static std::vector<utility::string_t> __cdecl split_path(const utility::string_t &path);
/// <summary>
/// Splits a query into its key-value components.
/// </summary>
/// <param name="query">The query string</param>
/// <returns>A <c>std::map&lt;utility::string_t, utility::string_t&gt;</c> containing the key-value components of the query.</returns>
_ASYNCRTIMP static std::map<utility::string_t, utility::string_t> __cdecl split_query(const utility::string_t &query);
/// <summary>
/// Validates a string as a URI.
/// </summary>
/// <param name="uri_string">The URI string to be validated.</param>
/// <returns><c>true</c> if the given string represents a valid URI, <c>false</c> otherwise.</returns>
_ASYNCRTIMP static bool __cdecl validate(const utility::string_t &uri_string);
/// <summary>
/// Creates an empty uri
/// </summary>
uri() { m_uri = _XPLATSTR("/");};
/// <summary>
/// Creates a URI from the given encoded string. This will throw an exception if the string
/// does not contain a valid URI. Use uri::validate if processing user-input.
/// </summary>
/// <param name="uri_string">A pointer to an encoded string to create the URI instance.</param>
_ASYNCRTIMP uri(const utility::char_t *uri_string);
/// <summary>
/// Creates a URI from the given encoded string. This will throw an exception if the string
/// does not contain a valid URI. Use uri::validate if processing user-input.
/// </summary>
/// <param name="uri_string">An encoded URI string to create the URI instance.</param>
_ASYNCRTIMP uri(const utility::string_t &uri_string);
/// <summary>
/// Copy constructor.
/// </summary>
uri(const uri &other) :
m_uri(other.m_uri),
m_components(other.m_components)
{}
/// <summary>
/// Copy assignment operator.
/// </summary>
uri & operator=(const uri &other)
{
if (this != &other)
{
m_uri = other.m_uri;
m_components = other.m_components;
}
return *this;
}
/// <summary>
/// Move constructor.
/// </summary>
uri(uri &&other) CPPREST_NOEXCEPT :
m_uri(std::move(other.m_uri)),
m_components(std::move(other.m_components))
{}
/// <summary>
/// Move assignment operator
/// </summary>
uri & operator=(uri &&other) CPPREST_NOEXCEPT
{
if (this != &other)
{
m_uri = std::move(other.m_uri);
m_components = std::move(other.m_components);
}
return *this;
}
/// <summary>
/// Get the scheme component of the URI as an encoded string.
/// </summary>
/// <returns>The URI scheme as a string.</returns>
const utility::string_t &scheme() const { return m_components.m_scheme; }
/// <summary>
/// Get the user information component of the URI as an encoded string.
/// </summary>
/// <returns>The URI user information as a string.</returns>
const utility::string_t &user_info() const { return m_components.m_user_info; }
/// <summary>
/// Get the host component of the URI as an encoded string.
/// </summary>
/// <returns>The URI host as a string.</returns>
const utility::string_t &host() const { return m_components.m_host; }
/// <summary>
/// Get the port component of the URI. Returns -1 if no port is specified.
/// </summary>
/// <returns>The URI port as an integer.</returns>
int port() const { return m_components.m_port; }
/// <summary>
/// Get the path component of the URI as an encoded string.
/// </summary>
/// <returns>The URI path as a string.</returns>
const utility::string_t &path() const { return m_components.m_path; }
/// <summary>
/// Get the query component of the URI as an encoded string.
/// </summary>
/// <returns>The URI query as a string.</returns>
const utility::string_t &query() const { return m_components.m_query; }
/// <summary>
/// Get the fragment component of the URI as an encoded string.
/// </summary>
/// <returns>The URI fragment as a string.</returns>
const utility::string_t &fragment() const { return m_components.m_fragment; }
/// <summary>
/// Creates a new uri object with the same authority portion as this one, omitting the resource and query portions.
/// </summary>
/// <returns>The new uri object with the same authority.</returns>
_ASYNCRTIMP uri authority() const;
/// <summary>
/// Gets the path, query, and fragment portion of this uri, which may be empty.
/// </summary>
/// <returns>The new URI object with the path, query and fragment portion of this URI.</returns>
_ASYNCRTIMP uri resource() const;
/// <summary>
/// An empty URI specifies no components, and serves as a default value
/// </summary>
bool is_empty() const
{
return this->m_uri.empty() || this->m_uri == _XPLATSTR("/");
}
/// <summary>
/// A loopback URI is one which refers to a hostname or ip address with meaning only on the local machine.
/// </summary>
/// <remarks>
/// Examples include "locahost", or ip addresses in the loopback range (127.0.0.0/24).
/// </remarks>
/// <returns><c>true</c> if this URI references the local host, <c>false</c> otherwise.</returns>
bool is_host_loopback() const
{
return !is_empty() && ((host() == _XPLATSTR("localhost")) || (host().size() > 4 && host().substr(0,4) == _XPLATSTR("127.")));
}
/// <summary>
/// A wildcard URI is one which refers to all hostnames that resolve to the local machine (using the * or +)
/// </summary>
/// <example>
/// http://*:80
/// </example>
bool is_host_wildcard() const
{
return !is_empty() && (this->host() == _XPLATSTR("*") || this->host() == _XPLATSTR("+"));
}
/// <summary>
/// A portable URI is one with a hostname that can be resolved globally (used from another machine).
/// </summary>
/// <returns><c>true</c> if this URI can be resolved globally (used from another machine), <c>false</c> otherwise.</returns>
/// <remarks>
/// The hostname "localhost" is a reserved name that is guaranteed to resolve to the local machine,
/// and cannot be used for inter-machine communication. Likewise the hostnames "*" and "+" on Windows
/// represent wildcards, and do not map to a resolvable address.
/// </remarks>
bool is_host_portable() const
{
return !(is_empty() || is_host_loopback() || is_host_wildcard());
}
// <summary>
/// A default port is one where the port is unspecified, and will be determined by the operating system.
/// The choice of default port may be dictated by the scheme (http -> 80) or not.
/// </summary>
/// <returns><c>true</c> if this URI instance has a default port, <c>false</c> otherwise.</returns>
bool is_port_default() const
{
return !is_empty() && this->port() == 0;
}
/// <summary>
/// An "authority" URI is one with only a scheme, optional userinfo, hostname, and (optional) port.
/// </summary>
/// <returns><c>true</c> if this is an "authority" URI, <c>false</c> otherwise.</returns>
bool is_authority() const
{
return !is_empty() && is_path_empty() && query().empty() && fragment().empty();
}
/// <summary>
/// Returns whether the other URI has the same authority as this one
/// </summary>
/// <param name="other">The URI to compare the authority with.</param>
/// <returns><c>true</c> if both the URI's have the same authority, <c>false</c> otherwise.</returns>
bool has_same_authority(const uri &other) const
{
return !is_empty() && this->authority() == other.authority();
}
/// <summary>
/// Returns whether the path portion of this URI is empty
/// </summary>
/// <returns><c>true</c> if the path portion of this URI is empty, <c>false</c> otherwise.</returns>
bool is_path_empty() const
{
return path().empty() || path() == _XPLATSTR("/");
}
/// <summary>
/// Returns the full (encoded) URI as a string.
/// </summary>
/// <returns>The full encoded URI string.</returns>
utility::string_t to_string() const
{
return m_uri;
}
_ASYNCRTIMP bool operator == (const uri &other) const;
bool operator < (const uri &other) const
{
return m_uri < other.m_uri;
}
bool operator != (const uri &other) const
{
return !(this->operator == (other));
}
private:
friend class uri_builder;
// Encodes all characters not in given set determined by given function.
static utility::string_t encode_impl(const utility::string_t &raw, const std::function<bool(int)>& should_encode);
utility::string_t m_uri;
details::uri_components m_components;
};
} // namespace web