mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-21 11:35:53 +00:00
Beast.HTTP:
New classes are introduced to represent HTTP messages and their associated bodies. The parser interface is reworked to use CRTP, error codes, and trait checks. New classes: * basic_headers Models field/value pairs in a HTTP message. * message Models a HTTP message, body behavior defined by template argument. Parsed message carries metadata generated during parsing. * parser Produces parsed messages. * empty_body, string_body, basic_streambuf_body Classes used to represent content bodies in various ways. New functions: * read, async_read, write, async_write Read and write HTTP messages on a socket. New concepts: * Body: Represents the HTTP Content-Body. * Field: A HTTP header field. * FieldSequence: A forward sequence of fields. * Reader: Parses a Body from a stream of bytes. * Writer: Serializes a Body to buffers. basic_parser changes: * add write methods which throw exceptions instead * error_code passed via parameter instead of return value * fold private member calls into existing callbacks * basic_parser uses CRTP instead of virtual members * add documentation on Derived requirements for CRTP impl/http-parser changes: * joyent renamed to nodejs to reflect upstream changes
This commit is contained in:
6
Jamroot
6
Jamroot
@@ -41,6 +41,10 @@ project beast
|
|||||||
<include>.
|
<include>.
|
||||||
#<use>/boost//headers
|
#<use>/boost//headers
|
||||||
<library>/boost/system//boost_system
|
<library>/boost/system//boost_system
|
||||||
|
<library>/boost/filesystem//boost_filesystem
|
||||||
|
<library>/boost/program_options//boost_program_options
|
||||||
|
# <library>ssl
|
||||||
|
# <library>crypto
|
||||||
<define>BOOST_ALL_NO_LIB=1
|
<define>BOOST_ALL_NO_LIB=1
|
||||||
<threading>multi
|
<threading>multi
|
||||||
<link>static
|
<link>static
|
||||||
@@ -61,6 +65,8 @@ project beast
|
|||||||
<os>HPUX:<library>ipv6
|
<os>HPUX:<library>ipv6
|
||||||
<os>QNXNTO:<library>socket
|
<os>QNXNTO:<library>socket
|
||||||
<os>HAIKU:<library>network
|
<os>HAIKU:<library>network
|
||||||
|
<toolset>msvc:<define>_SCL_SECURE_NO_WARNINGS=1
|
||||||
|
<toolset>msvc:<define>_CRT_SECURE_NO_WARNINGS=1
|
||||||
: usage-requirements
|
: usage-requirements
|
||||||
<include>.
|
<include>.
|
||||||
:
|
:
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
# Beast: An amazing cross platform library
|
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
#ifndef BEAST_UTILITY_CI_CHAR_TRAITS_H_INCLUDED
|
#ifndef BEAST_UTILITY_CI_CHAR_TRAITS_H_INCLUDED
|
||||||
#define BEAST_UTILITY_CI_CHAR_TRAITS_H_INCLUDED
|
#define BEAST_UTILITY_CI_CHAR_TRAITS_H_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
@@ -34,16 +35,15 @@ struct ci_less
|
|||||||
{
|
{
|
||||||
static bool const is_transparent = true;
|
static bool const is_transparent = true;
|
||||||
|
|
||||||
template <class String>
|
|
||||||
bool
|
bool
|
||||||
operator() (String const& lhs, String const& rhs) const
|
operator()(boost::string_ref const& lhs,
|
||||||
|
boost::string_ref const& rhs) const noexcept
|
||||||
{
|
{
|
||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
using char_type = typename String::value_type;
|
return std::lexicographical_compare(
|
||||||
return std::lexicographical_compare (
|
|
||||||
begin(lhs), end(lhs), begin(rhs), end(rhs),
|
begin(lhs), end(lhs), begin(rhs), end(rhs),
|
||||||
[] (char_type lhs, char_type rhs)
|
[](char lhs, char rhs)
|
||||||
{
|
{
|
||||||
return std::tolower(lhs) < std::tolower(rhs);
|
return std::tolower(lhs) < std::tolower(rhs);
|
||||||
}
|
}
|
||||||
|
|||||||
39
beast/http.h
Normal file
39
beast/http.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/basic_parser.h>
|
||||||
|
#include <beast/http/chunk_encode.h>
|
||||||
|
#include <beast/http/empty_body.h>
|
||||||
|
#include <beast/http/error.h>
|
||||||
|
#include <beast/http/fields.h>
|
||||||
|
#include <beast/http/headers.h>
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <beast/http/parser.h>
|
||||||
|
#include <beast/http/read.h>
|
||||||
|
#include <beast/http/reason.h>
|
||||||
|
#include <beast/http/resume_context.h>
|
||||||
|
#include <beast/http/streambuf_body.h>
|
||||||
|
#include <beast/http/string_body.h>
|
||||||
|
#include <beast/http/type_check.h>
|
||||||
|
#include <beast/http/write.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
195
beast/http/URL.h
195
beast/http/URL.h
@@ -1,195 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
|
||||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
//==============================================================================
|
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_URL_H_INCLUDED
|
|
||||||
#define BEAST_HTTP_URL_H_INCLUDED
|
|
||||||
|
|
||||||
#include <ios>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
|
|
||||||
/** A URL.
|
|
||||||
The accompanying robust parser is hardened against all forms of attack.
|
|
||||||
*/
|
|
||||||
class URL
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
/** Construct a URL from it's components. */
|
|
||||||
URL (
|
|
||||||
std::string schema_,
|
|
||||||
std::string host_,
|
|
||||||
std::uint16_t port_,
|
|
||||||
std::string port_string_,
|
|
||||||
std::string path_,
|
|
||||||
std::string query_ = "",
|
|
||||||
std::string fragment_ = "",
|
|
||||||
std::string userinfo_ = "");
|
|
||||||
|
|
||||||
/** Construct an empty URL. */
|
|
||||||
explicit URL () = default;
|
|
||||||
|
|
||||||
/** Copy construct a URL. */
|
|
||||||
URL (URL const& other) = default;
|
|
||||||
|
|
||||||
/** Copy assign a URL. */
|
|
||||||
URL& operator= (URL const& other) = default;
|
|
||||||
|
|
||||||
/** Move construct a URL. */
|
|
||||||
URL (URL&& other) = default;
|
|
||||||
|
|
||||||
/** Returns `true` if this is an empty URL. */
|
|
||||||
bool
|
|
||||||
empty () const;
|
|
||||||
|
|
||||||
/** Returns the scheme of the URL.
|
|
||||||
If no scheme was specified, the string will be empty.
|
|
||||||
*/
|
|
||||||
std::string const&
|
|
||||||
scheme () const;
|
|
||||||
|
|
||||||
/** Returns the host of the URL.
|
|
||||||
If no host was specified, the string will be empty.
|
|
||||||
*/
|
|
||||||
std::string const&
|
|
||||||
host () const;
|
|
||||||
|
|
||||||
/** Returns the port number as an integer.
|
|
||||||
If no port was specified, the value will be zero.
|
|
||||||
*/
|
|
||||||
std::uint16_t
|
|
||||||
port () const;
|
|
||||||
|
|
||||||
/** Returns the port number as a string.
|
|
||||||
If no port was specified, the string will be empty.
|
|
||||||
*/
|
|
||||||
std::string const&
|
|
||||||
port_string () const;
|
|
||||||
|
|
||||||
/** Returns the path of the URL.
|
|
||||||
If no path was specified, the string will be empty.
|
|
||||||
*/
|
|
||||||
std::string const&
|
|
||||||
path () const;
|
|
||||||
|
|
||||||
/** Returns the query parameters portion of the URL.
|
|
||||||
If no query parameters were present, the string will be empty.
|
|
||||||
*/
|
|
||||||
std::string const&
|
|
||||||
query () const;
|
|
||||||
|
|
||||||
/** Returns the URL fragment, if any. */
|
|
||||||
std::string const&
|
|
||||||
fragment () const;
|
|
||||||
|
|
||||||
/** Returns the user information, if any. */
|
|
||||||
std::string const&
|
|
||||||
userinfo () const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string m_scheme;
|
|
||||||
std::string m_host;
|
|
||||||
std::uint16_t m_port = 0;
|
|
||||||
std::string m_port_string;
|
|
||||||
std::string m_path;
|
|
||||||
std::string m_query;
|
|
||||||
std::string m_fragment;
|
|
||||||
std::string m_userinfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Attempt to parse a string into a URL */
|
|
||||||
std::pair<bool, URL>
|
|
||||||
parse_URL(std::string const&);
|
|
||||||
|
|
||||||
/** Retrieve the full URL as a single string. */
|
|
||||||
std::string
|
|
||||||
to_string(URL const& url);
|
|
||||||
|
|
||||||
/** Output stream conversion. */
|
|
||||||
std::ostream&
|
|
||||||
operator<< (std::ostream& os, URL const& url);
|
|
||||||
|
|
||||||
/** URL comparisons. */
|
|
||||||
/** @{ */
|
|
||||||
inline bool
|
|
||||||
operator== (URL const& lhs, URL const& rhs)
|
|
||||||
{
|
|
||||||
return to_string (lhs) == to_string (rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
operator!= (URL const& lhs, URL const& rhs)
|
|
||||||
{
|
|
||||||
return to_string (lhs) != to_string (rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
operator< (URL const& lhs, URL const& rhs)
|
|
||||||
{
|
|
||||||
return to_string (lhs) < to_string (rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator> (URL const& lhs, URL const& rhs)
|
|
||||||
{
|
|
||||||
return to_string (rhs) < to_string (lhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
operator<= (URL const& lhs, URL const& rhs)
|
|
||||||
{
|
|
||||||
return ! (to_string (rhs) < to_string (lhs));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool
|
|
||||||
operator>= (URL const& lhs, URL const& rhs)
|
|
||||||
{
|
|
||||||
return ! (to_string (lhs) < to_string (rhs));
|
|
||||||
}
|
|
||||||
/** @} */
|
|
||||||
|
|
||||||
/** boost::hash support */
|
|
||||||
template <class Hasher>
|
|
||||||
inline
|
|
||||||
void
|
|
||||||
hash_append (Hasher& h, URL const& url)
|
|
||||||
{
|
|
||||||
using beast::hash_append;
|
|
||||||
hash_append (h, to_string (url));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash <beast::URL>
|
|
||||||
{
|
|
||||||
std::size_t operator() (beast::URL const& url) const
|
|
||||||
{
|
|
||||||
return std::hash<std::string>{} (to_string (url));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#endif
|
|
||||||
447
beast/http/basic_headers.h
Normal file
447
beast/http/basic_headers.h
Normal file
@@ -0,0 +1,447 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_BASIC_HEADERS_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_BASIC_HEADERS_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/detail/writes.h>
|
||||||
|
#include <beast/asio/type_check.h>
|
||||||
|
#include <beast/ci_char_traits.h>
|
||||||
|
#include <beast/empty_base_optimization.h>
|
||||||
|
#include <boost/intrusive/list.hpp>
|
||||||
|
#include <boost/intrusive/set.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <beast/cxx17/type_traits.h> // <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
class basic_headers;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class basic_headers_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct value_type
|
||||||
|
{
|
||||||
|
std::string first;
|
||||||
|
std::string second;
|
||||||
|
|
||||||
|
value_type(boost::string_ref const& name_,
|
||||||
|
boost::string_ref const& value_)
|
||||||
|
: first(name_)
|
||||||
|
, second(value_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::string_ref
|
||||||
|
name() const
|
||||||
|
{
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::string_ref
|
||||||
|
value() const
|
||||||
|
{
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template<class Allocator>
|
||||||
|
friend class beast::http::basic_headers;
|
||||||
|
|
||||||
|
struct element
|
||||||
|
: boost::intrusive::set_base_hook <
|
||||||
|
boost::intrusive::link_mode <
|
||||||
|
boost::intrusive::normal_link>>
|
||||||
|
, boost::intrusive::list_base_hook <
|
||||||
|
boost::intrusive::link_mode <
|
||||||
|
boost::intrusive::normal_link>>
|
||||||
|
{
|
||||||
|
value_type data;
|
||||||
|
|
||||||
|
element(boost::string_ref const& name,
|
||||||
|
boost::string_ref const& value)
|
||||||
|
: data(name, value)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct less : private ci_less
|
||||||
|
{
|
||||||
|
template<class String>
|
||||||
|
bool
|
||||||
|
operator()(String const& lhs, element const& rhs) const
|
||||||
|
{
|
||||||
|
return ci_less::operator()(lhs, rhs.data.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class String>
|
||||||
|
bool
|
||||||
|
operator()(element const& lhs, String const& rhs) const
|
||||||
|
{
|
||||||
|
return ci_less::operator()(lhs.data.first, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator()(element const& lhs, element const& rhs) const
|
||||||
|
{
|
||||||
|
return beast::ci_less::operator()(
|
||||||
|
lhs.data.first, rhs.data.first);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using list_t = typename boost::intrusive::make_list<
|
||||||
|
element, boost::intrusive::constant_time_size<false>>::type;
|
||||||
|
|
||||||
|
using set_t = typename boost::intrusive::make_set<
|
||||||
|
element, boost::intrusive::constant_time_size<true>,
|
||||||
|
boost::intrusive::compare<less>>::type;
|
||||||
|
|
||||||
|
// data
|
||||||
|
set_t set_;
|
||||||
|
list_t list_;
|
||||||
|
|
||||||
|
basic_headers_base(set_t&& set, list_t&& list)
|
||||||
|
: set_(std::move(set))
|
||||||
|
, list_(std::move(list))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
class const_iterator;
|
||||||
|
|
||||||
|
using iterator = const_iterator;
|
||||||
|
|
||||||
|
basic_headers_base() = default;
|
||||||
|
|
||||||
|
/// Returns an iterator to the beginning of the field sequence.
|
||||||
|
iterator
|
||||||
|
begin() const;
|
||||||
|
|
||||||
|
/// Returns an iterator to the end of the field sequence.
|
||||||
|
iterator
|
||||||
|
end() const;
|
||||||
|
|
||||||
|
/// Returns an iterator to the beginning of the field sequence.
|
||||||
|
iterator
|
||||||
|
cbegin() const;
|
||||||
|
|
||||||
|
/// Returns an iterator to the end of the field sequence.
|
||||||
|
iterator
|
||||||
|
cend() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class basic_headers_base::const_iterator
|
||||||
|
{
|
||||||
|
using iter_type = list_t::const_iterator;
|
||||||
|
|
||||||
|
iter_type it_;
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
friend class beast::http::basic_headers;
|
||||||
|
|
||||||
|
friend class basic_headers_base;
|
||||||
|
|
||||||
|
const_iterator(iter_type it)
|
||||||
|
: it_(it)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type =
|
||||||
|
typename basic_headers_base::value_type;
|
||||||
|
using pointer = value_type const*;
|
||||||
|
using reference = value_type const&;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category =
|
||||||
|
std::bidirectional_iterator_tag;
|
||||||
|
|
||||||
|
const_iterator() = default;
|
||||||
|
const_iterator(const_iterator&& other) = default;
|
||||||
|
const_iterator(const_iterator const& other) = default;
|
||||||
|
const_iterator& operator=(const_iterator&& other) = default;
|
||||||
|
const_iterator& operator=(const_iterator const& other) = default;
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return it_ == other.it_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator!=(const_iterator const& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
reference
|
||||||
|
operator*() const
|
||||||
|
{
|
||||||
|
return it_->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return &**this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator&
|
||||||
|
operator++()
|
||||||
|
{
|
||||||
|
++it_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator&
|
||||||
|
operator--()
|
||||||
|
{
|
||||||
|
--it_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const_iterator
|
||||||
|
operator--(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
--(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Container to store HTTP headers.
|
||||||
|
|
||||||
|
Meets the requirements of `FieldSequence`.
|
||||||
|
*/
|
||||||
|
template<class Allocator>
|
||||||
|
class basic_headers
|
||||||
|
#if ! GENERATING_DOCS
|
||||||
|
: private empty_base_optimization<
|
||||||
|
typename std::allocator_traits<Allocator>::
|
||||||
|
template rebind_alloc<
|
||||||
|
detail::basic_headers_base::element>>
|
||||||
|
, public detail::basic_headers_base
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
using alloc_type = typename
|
||||||
|
std::allocator_traits<Allocator>::
|
||||||
|
template rebind_alloc<
|
||||||
|
detail::basic_headers_base::element>;
|
||||||
|
|
||||||
|
using alloc_traits =
|
||||||
|
std::allocator_traits<alloc_type>;
|
||||||
|
|
||||||
|
using size_type =
|
||||||
|
typename std::allocator_traits<Allocator>::size_type;
|
||||||
|
|
||||||
|
void
|
||||||
|
delete_all();
|
||||||
|
|
||||||
|
void
|
||||||
|
move_assign(basic_headers&, std::false_type);
|
||||||
|
|
||||||
|
void
|
||||||
|
move_assign(basic_headers&, std::true_type);
|
||||||
|
|
||||||
|
void
|
||||||
|
copy_assign(basic_headers const&, std::false_type);
|
||||||
|
|
||||||
|
void
|
||||||
|
copy_assign(basic_headers const&, std::true_type);
|
||||||
|
|
||||||
|
template<class FieldSequence>
|
||||||
|
void
|
||||||
|
copy_from(FieldSequence const& fs)
|
||||||
|
{
|
||||||
|
for(auto const& e : fs)
|
||||||
|
insert(e.first, e.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// The type of allocator used.
|
||||||
|
using allocator_type = Allocator;
|
||||||
|
|
||||||
|
/// Default constructor.
|
||||||
|
basic_headers() = default;
|
||||||
|
|
||||||
|
/// Destructor
|
||||||
|
~basic_headers();
|
||||||
|
|
||||||
|
/** Construct the headers.
|
||||||
|
|
||||||
|
@param alloc The allocator to use.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
basic_headers(Allocator const& alloc);
|
||||||
|
|
||||||
|
/** Move constructor.
|
||||||
|
|
||||||
|
The moved-from object becomes an empty field sequence.
|
||||||
|
|
||||||
|
@param other The object to move from.
|
||||||
|
*/
|
||||||
|
basic_headers(basic_headers&& other);
|
||||||
|
|
||||||
|
/** Move assignment.
|
||||||
|
|
||||||
|
The moved-from object becomes an empty field sequence.
|
||||||
|
|
||||||
|
@param other The object to move from.
|
||||||
|
*/
|
||||||
|
basic_headers& operator=(basic_headers&& other);
|
||||||
|
|
||||||
|
/// Copy constructor.
|
||||||
|
basic_headers(basic_headers const&);
|
||||||
|
|
||||||
|
/// Copy assignment.
|
||||||
|
basic_headers& operator=(basic_headers const&);
|
||||||
|
|
||||||
|
/// Copy constructor.
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_headers(basic_headers<OtherAlloc> const&);
|
||||||
|
|
||||||
|
/// Copy assignment.
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_headers& operator=(basic_headers<OtherAlloc> const&);
|
||||||
|
|
||||||
|
/// Construct from a field sequence.
|
||||||
|
template<class FwdIt>
|
||||||
|
basic_headers(FwdIt first, FwdIt last);
|
||||||
|
|
||||||
|
/// Returns `true` if the field sequence contains no elements.
|
||||||
|
bool
|
||||||
|
empty() const
|
||||||
|
{
|
||||||
|
return set_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the number of elements in the field sequence.
|
||||||
|
std::size_t
|
||||||
|
size() const
|
||||||
|
{
|
||||||
|
return set_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if the specified field exists. */
|
||||||
|
bool
|
||||||
|
exists(boost::string_ref const& name) const
|
||||||
|
{
|
||||||
|
return set_.find(name, less{}) != set_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns an iterator to the case-insensitive matching header. */
|
||||||
|
iterator
|
||||||
|
find(boost::string_ref const& name) const;
|
||||||
|
|
||||||
|
/** Returns the value for a case-insensitive matching header, or "" */
|
||||||
|
boost::string_ref
|
||||||
|
operator[](boost::string_ref const& name) const;
|
||||||
|
|
||||||
|
/** Clear the contents of the basic_headers. */
|
||||||
|
void
|
||||||
|
clear() noexcept;
|
||||||
|
|
||||||
|
/** Remove a field.
|
||||||
|
|
||||||
|
@return The number of fields removed.
|
||||||
|
*/
|
||||||
|
std::size_t
|
||||||
|
erase(boost::string_ref const& name);
|
||||||
|
|
||||||
|
/** Insert a field value.
|
||||||
|
|
||||||
|
If a field value already exists the new value will be
|
||||||
|
extended as per RFC2616 Section 4.2.
|
||||||
|
*/
|
||||||
|
// VFALCO TODO Consider allowing rvalue references for std::move?
|
||||||
|
void
|
||||||
|
insert(boost::string_ref const& name,
|
||||||
|
boost::string_ref const& value);
|
||||||
|
|
||||||
|
/** Insert a field value.
|
||||||
|
|
||||||
|
If a field value already exists the new value will be
|
||||||
|
extended as per RFC2616 Section 4.2.
|
||||||
|
*/
|
||||||
|
template<class T,
|
||||||
|
class = std::enable_if_t<
|
||||||
|
! std::is_constructible<boost::string_ref, T>::value>>
|
||||||
|
void
|
||||||
|
insert(boost::string_ref name, T const& value)
|
||||||
|
{
|
||||||
|
insert(name,
|
||||||
|
boost::lexical_cast<std::string>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Replace a field value.
|
||||||
|
|
||||||
|
The current field value, if any, is removed. Then the
|
||||||
|
specified value is inserted as if by insert(field, value).
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
replace(boost::string_ref const& name,
|
||||||
|
boost::string_ref const& value);
|
||||||
|
|
||||||
|
/** Replace a field value.
|
||||||
|
|
||||||
|
The current field value, if any, is removed. Then the
|
||||||
|
specified value is inserted as if by insert(field, value).
|
||||||
|
*/
|
||||||
|
template<class T,
|
||||||
|
class = std::enable_if_t<
|
||||||
|
! std::is_constructible<boost::string_ref, T>::value>>
|
||||||
|
void
|
||||||
|
replace(boost::string_ref const& name, T const& value)
|
||||||
|
{
|
||||||
|
replace(name,
|
||||||
|
boost::lexical_cast<std::string>(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/basic_headers.ipp>
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -21,66 +21,84 @@
|
|||||||
#define BEAST_HTTP_BASIC_PARSER_H_INCLUDED
|
#define BEAST_HTTP_BASIC_PARSER_H_INCLUDED
|
||||||
|
|
||||||
#include <beast/http/method.h>
|
#include <beast/http/method.h>
|
||||||
|
#include <beast/http/src/nodejs_parser.h>
|
||||||
|
#include <beast/asio/type_check.h>
|
||||||
#include <boost/asio/buffer.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
#include <boost/system/error_code.hpp>
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <beast/cxx17/type_traits.h> // <type_traits>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
namespace joyent {
|
|
||||||
struct http_parser;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
|
/** Parser for producing HTTP requests and responses.
|
||||||
|
|
||||||
|
Callbacks:
|
||||||
|
|
||||||
|
If a is an object of type Derived, and the call expression is
|
||||||
|
valid then the stated effects will take place:
|
||||||
|
|
||||||
|
a.on_start()
|
||||||
|
|
||||||
|
Called once when a new message begins.
|
||||||
|
|
||||||
|
a.on_field(std::string field, std::string value)
|
||||||
|
|
||||||
|
Called for each field
|
||||||
|
|
||||||
|
a.on_headers_complete(error_code&)
|
||||||
|
|
||||||
|
Called when all the header fields have been received, but
|
||||||
|
before any part of the body if any is received.
|
||||||
|
|
||||||
|
a.on_request(method_t method, std::string url,
|
||||||
|
int major, int minor, bool keep_alive, bool upgrade)
|
||||||
|
|
||||||
|
Called for requests when all the headers have been received.
|
||||||
|
This will precede any content body.
|
||||||
|
|
||||||
|
When keep_alive is false:
|
||||||
|
* Server roles respond with a "Connection: close" header.
|
||||||
|
* Client roles close the connection.
|
||||||
|
|
||||||
|
a.on_response(int status, std::string text,
|
||||||
|
int major, int minor, bool keep_alive,
|
||||||
|
bool upgrade)
|
||||||
|
|
||||||
|
Called for responses when all the headers have been received.
|
||||||
|
This will precede any content body.
|
||||||
|
|
||||||
|
When keep_alive is `false`:
|
||||||
|
* Client roles close the connection.
|
||||||
|
* Server roles respond with a "Connection: close" header.
|
||||||
|
|
||||||
|
This function should return `true` if upgrade is false and
|
||||||
|
a content body is expected. When upgrade is true, no
|
||||||
|
content-body is expected, and the return value is ignored.
|
||||||
|
|
||||||
|
a.on_body(void const* data, std::size_t bytes, error_code&)
|
||||||
|
|
||||||
|
Called zero or more times for the content body. Any transfer
|
||||||
|
encoding is already decoded in the memory pointed to by data.
|
||||||
|
|
||||||
|
a.on_complete()
|
||||||
|
|
||||||
|
Called when parsing completes successfully.
|
||||||
|
|
||||||
|
The parser uses traits to determine if the callback is possible.
|
||||||
|
If the Derived type omits the callbacks, they are simply skipped
|
||||||
|
with no compilation error.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
VFALCO TODO is_call_possible, enable_if_t on Derived calls
|
||||||
|
use boost::string_ref instead of std::string
|
||||||
|
*/
|
||||||
|
template<class Derived>
|
||||||
class basic_parser
|
class basic_parser
|
||||||
{
|
{
|
||||||
private:
|
http_parser state_;
|
||||||
// These structures must exactly match the
|
boost::system::error_code* ec_;
|
||||||
// declarations in joyent http_parser.h include
|
|
||||||
//
|
|
||||||
struct state_t
|
|
||||||
{
|
|
||||||
unsigned int type : 2;
|
|
||||||
unsigned int flags : 6;
|
|
||||||
unsigned int state : 8;
|
|
||||||
unsigned int header_state : 8;
|
|
||||||
unsigned int index : 8;
|
|
||||||
std::uint32_t nread;
|
|
||||||
std::uint64_t content_length;
|
|
||||||
unsigned short http_major;
|
|
||||||
unsigned short http_minor;
|
|
||||||
unsigned int status_code : 16;
|
|
||||||
unsigned int method : 8;
|
|
||||||
unsigned int http_errno : 7;
|
|
||||||
unsigned int upgrade : 1;
|
|
||||||
void *data;
|
|
||||||
};
|
|
||||||
|
|
||||||
using data_cb_t = int (*) (
|
|
||||||
state_t*, const char *at, size_t length);
|
|
||||||
using cb_t = int (*) (state_t*);
|
|
||||||
|
|
||||||
struct hooks_t
|
|
||||||
{
|
|
||||||
cb_t on_message_begin;
|
|
||||||
data_cb_t on_url;
|
|
||||||
data_cb_t on_status;
|
|
||||||
data_cb_t on_header_field;
|
|
||||||
data_cb_t on_header_value;
|
|
||||||
cb_t on_headers_complete;
|
|
||||||
data_cb_t on_body;
|
|
||||||
cb_t on_message_complete;
|
|
||||||
cb_t on_chunk_header;
|
|
||||||
cb_t on_chunk_complete;
|
|
||||||
};
|
|
||||||
|
|
||||||
char state_ [sizeof(state_t)];
|
|
||||||
char hooks_ [sizeof(hooks_t)];
|
|
||||||
|
|
||||||
bool complete_ = false;
|
bool complete_ = false;
|
||||||
std::string url_;
|
std::string url_;
|
||||||
std::string status_;
|
std::string status_;
|
||||||
@@ -90,20 +108,36 @@ private:
|
|||||||
public:
|
public:
|
||||||
using error_code = boost::system::error_code;
|
using error_code = boost::system::error_code;
|
||||||
|
|
||||||
virtual
|
/** Move constructor.
|
||||||
~basic_parser() = default;
|
|
||||||
|
The state of the moved-from object is undefined,
|
||||||
|
but safe to destroy.
|
||||||
|
*/
|
||||||
|
basic_parser(basic_parser&& other);
|
||||||
|
|
||||||
|
/** Move assignment.
|
||||||
|
|
||||||
|
The state of the moved-from object is undefined,
|
||||||
|
but safe to destroy.
|
||||||
|
*/
|
||||||
|
basic_parser&
|
||||||
|
operator=(basic_parser&& other);
|
||||||
|
|
||||||
|
/** Copy constructor. */
|
||||||
|
basic_parser(basic_parser const& other);
|
||||||
|
|
||||||
|
/** Copy assignment. */
|
||||||
|
basic_parser& operator=(basic_parser const& other);
|
||||||
|
|
||||||
/** Construct the parser.
|
/** Construct the parser.
|
||||||
If `request` is `true` this sets up the parser to
|
|
||||||
process an HTTP request.
|
@param request If `true`, the parser is setup for a request.
|
||||||
*/
|
*/
|
||||||
explicit
|
explicit
|
||||||
basic_parser (bool request) noexcept;
|
basic_parser(bool request) noexcept;
|
||||||
|
|
||||||
basic_parser&
|
|
||||||
operator= (basic_parser&& other);
|
|
||||||
|
|
||||||
/** Returns `true` if parsing is complete.
|
/** Returns `true` if parsing is complete.
|
||||||
|
|
||||||
This is only defined when no errors have been returned.
|
This is only defined when no errors have been returned.
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
@@ -113,153 +147,406 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Write data to the parser.
|
/** Write data to the parser.
|
||||||
@param data A buffer containing the data to write
|
|
||||||
@param bytes The size of the buffer pointed to by data.
|
|
||||||
@return A pair with bool success, and the number of bytes consumed.
|
|
||||||
*/
|
|
||||||
std::pair <error_code, std::size_t>
|
|
||||||
write (void const* data, std::size_t bytes);
|
|
||||||
|
|
||||||
/** Write a set of buffer data to the parser.
|
@param data A pointer to a buffer representing the input sequence.
|
||||||
The return value includes the error code if any,
|
@param size The number of bytes in the buffer pointed to by data.
|
||||||
and the number of bytes consumed in the input sequence.
|
|
||||||
@param buffers The buffers to write. These must meet the
|
@throws boost::system::system_error Thrown on failure.
|
||||||
requirements of ConstBufferSequence.
|
|
||||||
@return A pair with bool success, and the number of bytes consumed.
|
@return The number of bytes consumed in the input sequence.
|
||||||
*/
|
*/
|
||||||
template <class ConstBufferSequence>
|
std::size_t
|
||||||
std::pair <error_code, std::size_t>
|
write(void const* data, std::size_t size)
|
||||||
write (ConstBufferSequence const& buffers);
|
{
|
||||||
|
error_code ec;
|
||||||
|
auto const used = write(data, size, ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write data to the parser.
|
||||||
|
|
||||||
|
@param data A pointer to a buffer representing the input sequence.
|
||||||
|
@param size The number of bytes in the buffer pointed to by data.
|
||||||
|
@param ec Set to the error, if any error occurred.
|
||||||
|
|
||||||
|
@return The number of bytes consumed in the input sequence.
|
||||||
|
*/
|
||||||
|
std::size_t
|
||||||
|
write(void const* data, std::size_t size,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Write data to the parser.
|
||||||
|
|
||||||
|
@param buffers An object meeting the requirements of
|
||||||
|
ConstBufferSequence that represents the input sequence.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
|
||||||
|
@return The number of bytes consumed in the input sequence.
|
||||||
|
*/
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
write(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
auto const used = write(buffers, ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write data to the parser.
|
||||||
|
|
||||||
|
@param buffers An object meeting the requirements of
|
||||||
|
ConstBufferSequence that represents the input sequence.
|
||||||
|
@param ec Set to the error, if any error occurred.
|
||||||
|
|
||||||
|
@return The number of bytes consumed in the input sequence.
|
||||||
|
*/
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::size_t
|
||||||
|
write(ConstBufferSequence const& buffers,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
/** Called to indicate the end of file.
|
/** Called to indicate the end of file.
|
||||||
|
|
||||||
HTTP needs to know where the end of the stream is. For example,
|
HTTP needs to know where the end of the stream is. For example,
|
||||||
sometimes servers send responses without Content-Length and
|
sometimes servers send responses without Content-Length and
|
||||||
expect the client to consume input (for the body) until EOF.
|
expect the client to consume input (for the body) until EOF.
|
||||||
Callbacks and errors will still be processed as usual.
|
Callbacks and errors will still be processed as usual.
|
||||||
|
|
||||||
@note This is typically called when a socket read returns eof.
|
@note This is typically called when a socket read returns eof.
|
||||||
@return `true` if the message is complete.
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
*/
|
*/
|
||||||
error_code
|
|
||||||
write_eof();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/** Called once when a new message begins. */
|
|
||||||
virtual
|
|
||||||
void
|
void
|
||||||
on_start() = 0;
|
write_eof()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
write_eof(ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
/** Called for each header field. */
|
/** Called to indicate the end of file.
|
||||||
virtual
|
|
||||||
void
|
|
||||||
on_field (std::string const& field, std::string const& value) = 0;
|
|
||||||
|
|
||||||
/** Called for requests when all the headers have been received.
|
HTTP needs to know where the end of the stream is. For example,
|
||||||
This will precede any content body.
|
sometimes servers send responses without Content-Length and
|
||||||
When keep_alive is false:
|
expect the client to consume input (for the body) until EOF.
|
||||||
* Server roles respond with a "Connection: close" header.
|
Callbacks and errors will still be processed as usual.
|
||||||
* Client roles close the connection.
|
|
||||||
When upgrade is true, no content-body is expected, and the
|
|
||||||
return value is ignored.
|
|
||||||
|
|
||||||
@param method The HTTP method specified in the request line
|
@note This is typically called when a socket read returns eof.
|
||||||
@param major The HTTP major version number
|
|
||||||
@param minor The HTTP minor version number
|
@param ec Set to the error, if any error occurred.
|
||||||
@param url The URL specified in the request line
|
|
||||||
@param keep_alive `false` if this is the last message.
|
|
||||||
@param upgrade `true` if the Upgrade header is specified
|
|
||||||
@return `true` If upgrade is false and a content body is expected.
|
|
||||||
*/
|
*/
|
||||||
virtual
|
|
||||||
bool
|
|
||||||
on_request (method_t method, std::string const& url,
|
|
||||||
int major, int minor, bool keep_alive, bool upgrade) = 0;
|
|
||||||
|
|
||||||
/** Called for responses when all the headers have been received.
|
|
||||||
This will precede any content body.
|
|
||||||
When keep_alive is `false`:
|
|
||||||
* Client roles close the connection.
|
|
||||||
* Server roles respond with a "Connection: close" header.
|
|
||||||
When upgrade is true, no content-body is expected, and the
|
|
||||||
return value is ignored.
|
|
||||||
|
|
||||||
@param status The numerical HTTP status code in the response line
|
|
||||||
@param text The status text in the response line
|
|
||||||
@param major The HTTP major version number
|
|
||||||
@param minor The HTTP minor version number
|
|
||||||
@param keep_alive `false` if this is the last message.
|
|
||||||
@param upgrade `true` if the Upgrade header is specified
|
|
||||||
@return `true` If upgrade is false and a content body is expected.
|
|
||||||
*/
|
|
||||||
virtual
|
|
||||||
bool
|
|
||||||
on_response (int status, std::string const& text,
|
|
||||||
int major, int minor, bool keep_alive, bool upgrade) = 0;
|
|
||||||
|
|
||||||
/** Called zero or more times for the content body.
|
|
||||||
Any transfer encoding is already decoded in the
|
|
||||||
memory pointed to by data.
|
|
||||||
|
|
||||||
@param data A memory block containing the next decoded
|
|
||||||
chunk of the content body.
|
|
||||||
@param bytes The number of bytes pointed to by data.
|
|
||||||
*/
|
|
||||||
virtual
|
|
||||||
void
|
void
|
||||||
on_body (void const* data, std::size_t bytes) = 0;
|
write_eof(error_code& ec);
|
||||||
|
|
||||||
/** Called once when the message is complete. */
|
|
||||||
virtual
|
|
||||||
void
|
|
||||||
on_complete() = 0;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void check_header();
|
Derived&
|
||||||
|
impl()
|
||||||
|
{
|
||||||
|
return *static_cast<Derived*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
int do_message_start ();
|
template<class C>
|
||||||
int do_url (char const* in, std::size_t bytes);
|
class has_on_start_t
|
||||||
int do_status (char const* in, std::size_t bytes);
|
{
|
||||||
int do_header_field (char const* in, std::size_t bytes);
|
template<class T, class R =
|
||||||
int do_header_value (char const* in, std::size_t bytes);
|
decltype(std::declval<T>().on_start(), std::true_type{})>
|
||||||
int do_headers_complete ();
|
static R check(int);
|
||||||
int do_body (char const* in, std::size_t bytes);
|
template <class>
|
||||||
int do_message_complete ();
|
static std::false_type check(...);
|
||||||
int do_chunk_header();
|
using type = decltype(check<C>(0));
|
||||||
int do_chunk_complete();
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_start =
|
||||||
|
std::bool_constant<has_on_start_t<C>::value>;
|
||||||
|
|
||||||
static int cb_message_start (joyent::http_parser*);
|
void
|
||||||
static int cb_url (joyent::http_parser*, char const*, std::size_t);
|
call_on_start(std::true_type)
|
||||||
static int cb_status (joyent::http_parser*, char const*, std::size_t);
|
{
|
||||||
static int cb_header_field (joyent::http_parser*, char const*, std::size_t);
|
impl().on_start();
|
||||||
static int cb_header_value (joyent::http_parser*, char const*, std::size_t);
|
}
|
||||||
static int cb_headers_complete (joyent::http_parser*);
|
|
||||||
static int cb_body (joyent::http_parser*, char const*, std::size_t);
|
void
|
||||||
static int cb_message_complete (joyent::http_parser*);
|
call_on_start(std::false_type)
|
||||||
static int cb_chunk_header (joyent::http_parser*);
|
{
|
||||||
static int cb_chunk_complete (joyent::http_parser*);
|
}
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_field_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_field(
|
||||||
|
std::declval<std::string const&>(),
|
||||||
|
std::declval<std::string const&>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_field =
|
||||||
|
std::bool_constant<has_on_field_t<C>::value>;
|
||||||
|
|
||||||
|
void
|
||||||
|
call_on_field(std::string const& field,
|
||||||
|
std::string const& value, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_field(field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
call_on_field(std::string const&, std::string const&,
|
||||||
|
std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_headers_complete_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_headers_complete(
|
||||||
|
std::declval<error_code&>()), std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_headers_complete =
|
||||||
|
std::bool_constant<has_on_headers_complete_t<C>::value>;
|
||||||
|
|
||||||
|
void
|
||||||
|
call_on_headers_complete(error_code& ec, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_headers_complete(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
call_on_headers_complete(error_code&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_request_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_request(
|
||||||
|
std::declval<method_t>(), std::declval<std::string>(),
|
||||||
|
std::declval<int>(), std::declval<int>(),
|
||||||
|
std::declval<bool>(), std::declval<bool>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_request =
|
||||||
|
std::bool_constant<has_on_request_t<C>::value>;
|
||||||
|
|
||||||
|
void
|
||||||
|
call_on_request(method_t method, std::string url,
|
||||||
|
int major, int minor, bool keep_alive, bool upgrade,
|
||||||
|
std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_request(
|
||||||
|
method, url, major, minor, keep_alive, upgrade);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
call_on_request(method_t, std::string, int, int, bool, bool,
|
||||||
|
std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_response_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_response(
|
||||||
|
std::declval<int>(), std::declval<std::string>,
|
||||||
|
std::declval<int>(), std::declval<int>(),
|
||||||
|
std::declval<bool>(), std::declval<bool>()),
|
||||||
|
std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
#if 0
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
#else
|
||||||
|
// VFALCO Trait seems broken for http::parser
|
||||||
|
using type = std::true_type;
|
||||||
|
#endif
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_response =
|
||||||
|
std::bool_constant<has_on_response_t<C>::value>;
|
||||||
|
|
||||||
|
bool
|
||||||
|
call_on_response(int status, std::string text,
|
||||||
|
int major, int minor, bool keep_alive, bool upgrade,
|
||||||
|
std::true_type)
|
||||||
|
{
|
||||||
|
return impl().on_response(
|
||||||
|
status, text, major, minor, keep_alive, upgrade);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
call_on_response(int, std::string, int, int, bool, bool,
|
||||||
|
std::false_type)
|
||||||
|
{
|
||||||
|
// VFALCO Certainly incorrect
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_body_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_body(
|
||||||
|
std::declval<void const*>(), std::declval<std::size_t>(),
|
||||||
|
std::declval<error_code&>()), std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_body =
|
||||||
|
std::bool_constant<has_on_body_t<C>::value>;
|
||||||
|
|
||||||
|
void
|
||||||
|
call_on_body(void const* data, std::size_t bytes,
|
||||||
|
error_code& ec, std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_body(data, bytes, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
call_on_body(void const*, std::size_t,
|
||||||
|
error_code&, std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class C>
|
||||||
|
class has_on_complete_t
|
||||||
|
{
|
||||||
|
template<class T, class R =
|
||||||
|
decltype(std::declval<T>().on_complete(), std::true_type{})>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<C>(0));
|
||||||
|
public:
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
template<class C>
|
||||||
|
using has_on_complete =
|
||||||
|
std::bool_constant<has_on_complete_t<C>::value>;
|
||||||
|
|
||||||
|
void
|
||||||
|
call_on_complete(std::true_type)
|
||||||
|
{
|
||||||
|
impl().on_complete();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
call_on_complete(std::false_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
check_header();
|
||||||
|
|
||||||
|
static int cb_message_start(http_parser*);
|
||||||
|
static int cb_url(http_parser*, char const*, std::size_t);
|
||||||
|
static int cb_status(http_parser*, char const*, std::size_t);
|
||||||
|
static int cb_header_field(http_parser*, char const*, std::size_t);
|
||||||
|
static int cb_header_value(http_parser*, char const*, std::size_t);
|
||||||
|
static int cb_headers_complete(http_parser*);
|
||||||
|
static int cb_body(http_parser*, char const*, std::size_t);
|
||||||
|
static int cb_message_complete(http_parser*);
|
||||||
|
static int cb_chunk_header(http_parser*);
|
||||||
|
static int cb_chunk_complete(http_parser*);
|
||||||
|
|
||||||
|
struct hooks_t : http_parser_settings
|
||||||
|
{
|
||||||
|
hooks_t()
|
||||||
|
{
|
||||||
|
http_parser_settings_init(this);
|
||||||
|
on_message_begin = &basic_parser::cb_message_start;
|
||||||
|
on_url = &basic_parser::cb_url;
|
||||||
|
on_status = &basic_parser::cb_status;
|
||||||
|
on_header_field = &basic_parser::cb_header_field;
|
||||||
|
on_header_value = &basic_parser::cb_header_value;
|
||||||
|
on_headers_complete = &basic_parser::cb_headers_complete;
|
||||||
|
on_body = &basic_parser::cb_body;
|
||||||
|
on_message_complete = &basic_parser::cb_message_complete;
|
||||||
|
on_chunk_header = &basic_parser::cb_chunk_header;
|
||||||
|
on_chunk_complete = &basic_parser::cb_chunk_complete;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static
|
||||||
|
http_parser_settings const*
|
||||||
|
hooks();
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class ConstBufferSequence>
|
template<class Derived>
|
||||||
auto
|
template<class ConstBufferSequence>
|
||||||
basic_parser::write (ConstBufferSequence const& buffers) ->
|
std::size_t
|
||||||
std::pair <error_code, std::size_t>
|
basic_parser<Derived>::write(
|
||||||
|
ConstBufferSequence const& buffers, error_code& ec)
|
||||||
{
|
{
|
||||||
std::pair <error_code, std::size_t> result ({}, 0);
|
static_assert(beast::is_ConstBufferSequence<
|
||||||
|
ConstBufferSequence>::value,
|
||||||
|
"ConstBufferSequence requirements not met");
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
std::size_t bytes_used = 0;
|
||||||
for (auto const& buffer : buffers)
|
for (auto const& buffer : buffers)
|
||||||
{
|
{
|
||||||
std::size_t bytes_consumed;
|
auto const n = write(
|
||||||
std::tie (result.first, bytes_consumed) =
|
buffer_cast<void const*>(buffer),
|
||||||
write (boost::asio::buffer_cast <void const*> (buffer),
|
buffer_size(buffer), ec);
|
||||||
boost::asio::buffer_size (buffer));
|
if(ec)
|
||||||
if (result.first)
|
return 0;
|
||||||
break;
|
bytes_used += n;
|
||||||
result.second += bytes_consumed;
|
if(complete())
|
||||||
if (complete())
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return result;
|
return bytes_used;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
http_parser_settings const*
|
||||||
|
basic_parser<Derived>::hooks()
|
||||||
|
{
|
||||||
|
static hooks_t const h;
|
||||||
|
return &h;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/basic_parser.ipp>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace deprecated_http {
|
||||||
|
|
||||||
/** Container for the HTTP content-body. */
|
/** Container for the HTTP content-body. */
|
||||||
class body
|
class body
|
||||||
@@ -144,7 +144,7 @@ body::data() const
|
|||||||
return buf_->data();
|
return buf_->data();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // http
|
} // deprecated_http
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -148,8 +148,8 @@ chunk_encoded_buffers<Buffers>::to_hex(
|
|||||||
|
|
||||||
template <class Buffers>
|
template <class Buffers>
|
||||||
chunk_encoded_buffers<Buffers>::const_iterator::const_iterator()
|
chunk_encoded_buffers<Buffers>::const_iterator::const_iterator()
|
||||||
: where_(Where::end)
|
: buffers_(nullptr)
|
||||||
, buffers_(nullptr)
|
, where_(Where::end)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@ chunk_encoded_buffers<Buffers>::const_iterator::operator--() ->
|
|||||||
const_iterator&
|
const_iterator&
|
||||||
{
|
{
|
||||||
assert(buffers_);
|
assert(buffers_);
|
||||||
assert(where_ != Where::begin);
|
assert(where_ != Where::head);
|
||||||
if (where_ == Where::end)
|
if (where_ == Where::end)
|
||||||
where_ = Where::input;
|
where_ = Where::input;
|
||||||
else if (iter_ != buffers_->buffers_.begin())
|
else if (iter_ != buffers_->buffers_.begin())
|
||||||
@@ -247,37 +247,44 @@ chunk_encoded_buffers<Buffers>::const_iterator::const_iterator(
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // detail
|
||||||
|
|
||||||
/** Returns a chunk-encoded BufferSequence.
|
/** Returns a chunk-encoded BufferSequence.
|
||||||
|
|
||||||
See:
|
See:
|
||||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
|
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.6.1
|
||||||
|
|
||||||
@tparam Buffers A type meeting the requirements of BufferSequence.
|
|
||||||
@param buffers The input buffer sequence.
|
@param buffers The input buffer sequence.
|
||||||
|
|
||||||
@param final_chunk `true` If this should include a final-chunk.
|
@param final_chunk `true` If this should include a final-chunk.
|
||||||
@return A chunk-encoded ConstBufferSeqeunce representing the input.
|
|
||||||
|
@return A chunk-encoded ConstBufferSequence representing the input.
|
||||||
*/
|
*/
|
||||||
/** @{ */
|
template<class ConstBufferSequence>
|
||||||
template <class Buffers>
|
#if GENERATING_DOCS
|
||||||
detail::chunk_encoded_buffers<Buffers>
|
implementation_defined
|
||||||
chunk_encode (Buffers const& buffers,
|
#else
|
||||||
|
detail::chunk_encoded_buffers<ConstBufferSequence>
|
||||||
|
#endif
|
||||||
|
chunk_encode(ConstBufferSequence const& buffers,
|
||||||
bool final_chunk = false)
|
bool final_chunk = false)
|
||||||
{
|
{
|
||||||
return detail::chunk_encoded_buffers<
|
return detail::chunk_encoded_buffers<
|
||||||
Buffers>(buffers, final_chunk);
|
ConstBufferSequence>{buffers, final_chunk};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a chunked encoding final chunk.
|
/// Returns a chunked encoding final chunk.
|
||||||
inline
|
inline
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
implementation_defined
|
||||||
|
#else
|
||||||
boost::asio::const_buffers_1
|
boost::asio::const_buffers_1
|
||||||
|
#endif
|
||||||
chunk_encode_final()
|
chunk_encode_final()
|
||||||
{
|
{
|
||||||
return boost::asio::const_buffers_1(
|
return boost::asio::const_buffers_1(
|
||||||
"0\r\n\r\n", 5);
|
"0\r\n\r\n", 5);
|
||||||
}
|
}
|
||||||
/** @} */
|
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
|
|||||||
83
beast/http/detail/error.h
Normal file
83
beast/http/detail/error.h
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_DETAIL_ERROR_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_DETAIL_ERROR_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/src/nodejs_parser.h>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class message_category
|
||||||
|
: public boost::system::error_category
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
const char*
|
||||||
|
name() const noexcept override
|
||||||
|
{
|
||||||
|
return "http error";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
message(int ev) const override
|
||||||
|
{
|
||||||
|
return http_errno_description(
|
||||||
|
static_cast<http_errno>(ev));
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::system::error_condition
|
||||||
|
default_error_condition(int ev) const noexcept override
|
||||||
|
{
|
||||||
|
return boost::system::error_condition{ev, *this};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
equivalent(int ev,
|
||||||
|
boost::system::error_condition const& condition
|
||||||
|
) const noexcept override
|
||||||
|
{
|
||||||
|
return condition.value() == ev &&
|
||||||
|
&condition.category() == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
equivalent(boost::system::error_code const& error,
|
||||||
|
int ev) const noexcept override
|
||||||
|
{
|
||||||
|
return error.value() == ev &&
|
||||||
|
&error.category() == this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
auto
|
||||||
|
make_error(int http_errno)
|
||||||
|
{
|
||||||
|
static message_category const mc{};
|
||||||
|
return boost::system::error_code{http_errno, mc};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
133
beast/http/detail/write_preparation.h
Normal file
133
beast/http/detail/write_preparation.h
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_WRITE_PREPARATION_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_WRITE_PREPARATION_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class has_content_length_value
|
||||||
|
{
|
||||||
|
template<class U, class R = typename std::is_convertible<
|
||||||
|
decltype(std::declval<U>().content_length()),
|
||||||
|
std::size_t>>
|
||||||
|
static R check(int);
|
||||||
|
template <class>
|
||||||
|
static std::false_type check(...);
|
||||||
|
using type = decltype(check<T>(0));
|
||||||
|
public:
|
||||||
|
// `true` if `T` meets the requirements.
|
||||||
|
static bool const value = type::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Determines if the writer can provide the content length
|
||||||
|
template<class T>
|
||||||
|
using has_content_length =
|
||||||
|
std::integral_constant<bool,
|
||||||
|
has_content_length_value<T>::value>;
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
struct write_preparation
|
||||||
|
{
|
||||||
|
using headers_type =
|
||||||
|
basic_headers<std::allocator<char>>;
|
||||||
|
|
||||||
|
message<isRequest, Body, Headers> const& msg;
|
||||||
|
typename Body::writer w;
|
||||||
|
streambuf sb;
|
||||||
|
bool chunked;
|
||||||
|
bool close;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
write_preparation(
|
||||||
|
message<isRequest, Body, Headers> const& msg_)
|
||||||
|
: msg(msg_)
|
||||||
|
, w(msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(error_code& ec)
|
||||||
|
{
|
||||||
|
w.init(ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
// VFALCO TODO This implementation requires making a
|
||||||
|
// copy of the headers, we can do better.
|
||||||
|
// VFALCO Should we be using handler_alloc?
|
||||||
|
headers_type h(msg.headers.begin(), msg.headers.end());
|
||||||
|
set_content_length(h, has_content_length<
|
||||||
|
typename Body::writer>{});
|
||||||
|
|
||||||
|
// VFALCO TODO Keep-Alive
|
||||||
|
|
||||||
|
if(close)
|
||||||
|
{
|
||||||
|
if(msg.version >= 11)
|
||||||
|
h.insert("Connection", "close");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(msg.version < 11)
|
||||||
|
h.insert("Connection", "keep-alive");
|
||||||
|
}
|
||||||
|
|
||||||
|
msg.write_firstline(sb);
|
||||||
|
write_fields(sb, h);
|
||||||
|
detail::write(sb, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
set_content_length(headers_type& h,
|
||||||
|
std::true_type)
|
||||||
|
{
|
||||||
|
close = false;
|
||||||
|
chunked = false;
|
||||||
|
h.insert("Content-Length", w.content_length());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
set_content_length(headers_type& h,
|
||||||
|
std::false_type)
|
||||||
|
{
|
||||||
|
if(msg.version >= 11)
|
||||||
|
{
|
||||||
|
close = false;
|
||||||
|
chunked = true;
|
||||||
|
h.insert("Transfer-Encoding", "chunked");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
close = true;
|
||||||
|
chunked = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
76
beast/http/detail/writes.h
Normal file
76
beast/http/detail/writes.h
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_WRITES_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_WRITES_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/type_check.h>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class Streambuf, class T,
|
||||||
|
class = std::enable_if_t<is_Streambuf<Streambuf>::value>>
|
||||||
|
void
|
||||||
|
write(Streambuf& streambuf, T&& t)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
auto const& s =
|
||||||
|
boost::lexical_cast<std::string>(
|
||||||
|
std::forward<T>(t));
|
||||||
|
streambuf.commit(buffer_copy(
|
||||||
|
streambuf.prepare(s.size()), buffer(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf, std::size_t N,
|
||||||
|
class = std::enable_if_t< (N>0) &&
|
||||||
|
is_Streambuf<Streambuf>::value>>
|
||||||
|
void
|
||||||
|
write(Streambuf& streambuf, char const(&s)[N])
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
streambuf.commit(buffer_copy(
|
||||||
|
streambuf.prepare(N), buffer(s, N-1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf,
|
||||||
|
class = std::enable_if_t<is_Streambuf<Streambuf>::value>>
|
||||||
|
void
|
||||||
|
write(Streambuf& streambuf, boost::string_ref const& s)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
streambuf.commit(buffer_copy(
|
||||||
|
streambuf.prepare(s.size()),
|
||||||
|
buffer(s.data(), s.size())));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
87
beast/http/empty_body.h
Normal file
87
beast/http/empty_body.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_EMPTY_BODY_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_EMPTY_BODY_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/error.h>
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** An empty content-body.
|
||||||
|
*/
|
||||||
|
struct empty_body
|
||||||
|
{
|
||||||
|
struct value_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
struct reader
|
||||||
|
{
|
||||||
|
template<bool isRequest, class Allocator>
|
||||||
|
explicit
|
||||||
|
reader(message<isRequest, empty_body, Allocator>&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write(void const*, std::size_t, error_code&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct writer
|
||||||
|
{
|
||||||
|
template<bool isRequest, class Allocator>
|
||||||
|
explicit
|
||||||
|
writer(message<isRequest, empty_body, Allocator> const& m)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(error_code& ec)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
content_length() const
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Write>
|
||||||
|
boost::tribool
|
||||||
|
operator()(resume_context&&, error_code&, Write&& write)
|
||||||
|
{
|
||||||
|
write(boost::asio::null_buffers{});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -17,18 +17,17 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#if BEAST_INCLUDE_BEASTCONFIG
|
#ifndef BEAST_HTTP_ERROR_H_INCLUDED
|
||||||
#include <BeastConfig.h>
|
#define BEAST_HTTP_ERROR_H_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <beast/http/impl/basic_parser.cpp>
|
|
||||||
#include <beast/http/impl/joyent_parser.cpp>
|
|
||||||
#include <beast/http/impl/method.cpp>
|
|
||||||
#include <beast/http/impl/URL.cpp>
|
|
||||||
|
|
||||||
#include <beast/http/tests/chunked_encoder.test.cpp>
|
|
||||||
#include <beast/http/tests/parser.test.cpp>
|
|
||||||
#include <beast/http/tests/rfc2616.test.cpp>
|
|
||||||
#include <beast/http/tests/URL.test.cpp>
|
|
||||||
#include <beast/http/tests/urls_large_data.cpp>
|
|
||||||
|
|
||||||
133
beast/http/fields.h
Normal file
133
beast/http/fields.h
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_FIELDS_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_FIELDS_H_INCLUDED
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wmissing-braces"
|
||||||
|
#endif // defined(__clang__)
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
|
auto const&
|
||||||
|
common_fields()
|
||||||
|
{
|
||||||
|
// Must be sorted
|
||||||
|
static std::array<char const*, 82> constexpr h{
|
||||||
|
"Accept"
|
||||||
|
,"Accept-Charset"
|
||||||
|
,"Accept-Datetime"
|
||||||
|
,"Accept-Encoding"
|
||||||
|
,"Accept-Language"
|
||||||
|
,"Accept-Ranges"
|
||||||
|
,"Access-Control-Allow-Credentials"
|
||||||
|
,"Access-Control-Allow-Headers"
|
||||||
|
,"Access-Control-Allow-Methods"
|
||||||
|
,"Access-Control-Allow-Origin"
|
||||||
|
,"Access-Control-Expose-Headers"
|
||||||
|
,"Access-Control-Max-Age"
|
||||||
|
,"Access-Control-Request-Headers"
|
||||||
|
,"Access-Control-Request-Method"
|
||||||
|
,"Age"
|
||||||
|
,"Allow"
|
||||||
|
,"Authorization"
|
||||||
|
,"Cache-Control"
|
||||||
|
,"Connection"
|
||||||
|
,"Content-Disposition"
|
||||||
|
,"Content-Encoding"
|
||||||
|
,"Content-Language"
|
||||||
|
,"Content-Length"
|
||||||
|
,"Content-Location"
|
||||||
|
,"Content-MD5"
|
||||||
|
,"Content-Range"
|
||||||
|
,"Content-Type"
|
||||||
|
,"Cookie"
|
||||||
|
,"DNT"
|
||||||
|
,"Date"
|
||||||
|
,"ETag"
|
||||||
|
,"Expect"
|
||||||
|
,"Expires"
|
||||||
|
,"From"
|
||||||
|
,"Front-End-Https"
|
||||||
|
,"Host"
|
||||||
|
,"If-Match"
|
||||||
|
,"If-Modified-Since"
|
||||||
|
,"If-None-Match"
|
||||||
|
,"If-Range"
|
||||||
|
,"If-Unmodified-Since"
|
||||||
|
,"Keep-Alive"
|
||||||
|
,"Last-Modified"
|
||||||
|
,"Link"
|
||||||
|
,"Location"
|
||||||
|
,"Max-Forwards"
|
||||||
|
,"Origin"
|
||||||
|
,"P3P"
|
||||||
|
,"Pragma"
|
||||||
|
,"Proxy-Authenticate"
|
||||||
|
,"Proxy-Authorization"
|
||||||
|
,"Proxy-Connection"
|
||||||
|
,"Range"
|
||||||
|
,"Referer"
|
||||||
|
,"Refresh"
|
||||||
|
,"Retry-After"
|
||||||
|
,"Server"
|
||||||
|
,"Set-Cookie"
|
||||||
|
,"Strict-Transport-Security"
|
||||||
|
,"TE"
|
||||||
|
,"Timestamp"
|
||||||
|
,"Trailer"
|
||||||
|
,"Transfer-Encoding"
|
||||||
|
,"Upgrade"
|
||||||
|
,"User-Agent"
|
||||||
|
,"VIP"
|
||||||
|
,"Vary"
|
||||||
|
,"Via"
|
||||||
|
,"WWW-Authenticate"
|
||||||
|
,"Warning"
|
||||||
|
,"X-Accel-Redirect"
|
||||||
|
,"X-Content-Security-Policy-Report-Only"
|
||||||
|
,"X-Content-Type-Options"
|
||||||
|
,"X-Forwarded-For"
|
||||||
|
,"X-Forwarded-Proto"
|
||||||
|
,"X-Frame-Options"
|
||||||
|
,"X-Powered-By"
|
||||||
|
,"X-Real-IP"
|
||||||
|
,"X-Requested-With"
|
||||||
|
,"X-UA-Compatible"
|
||||||
|
,"X-Wap-Profile"
|
||||||
|
,"X-XSS-Protection"
|
||||||
|
};
|
||||||
|
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif // defined(__clang__)
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -20,390 +20,17 @@
|
|||||||
#ifndef BEAST_HTTP_HEADERS_H_INCLUDED
|
#ifndef BEAST_HTTP_HEADERS_H_INCLUDED
|
||||||
#define BEAST_HTTP_HEADERS_H_INCLUDED
|
#define BEAST_HTTP_HEADERS_H_INCLUDED
|
||||||
|
|
||||||
#include <beast/ci_char_traits.h>
|
#include <beast/http/basic_headers.h>
|
||||||
#include <boost/intrusive/list.hpp>
|
#include <memory>
|
||||||
#include <boost/intrusive/set.hpp>
|
|
||||||
#include <boost/iterator/transform_iterator.hpp>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cctype>
|
|
||||||
#include <map>
|
|
||||||
#include <ostream>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
/** Holds a collection of HTTP headers. */
|
template<class Allocator>
|
||||||
class headers
|
using headers = basic_headers<Allocator>;
|
||||||
{
|
|
||||||
public:
|
|
||||||
using value_type = std::pair<std::string, std::string>;
|
|
||||||
|
|
||||||
private:
|
using http_headers =
|
||||||
struct element
|
basic_headers<std::allocator<char>>;
|
||||||
: boost::intrusive::set_base_hook <
|
|
||||||
boost::intrusive::link_mode <
|
|
||||||
boost::intrusive::normal_link>>
|
|
||||||
, boost::intrusive::list_base_hook <
|
|
||||||
boost::intrusive::link_mode <
|
|
||||||
boost::intrusive::normal_link>>
|
|
||||||
{
|
|
||||||
template <class = void>
|
|
||||||
element (std::string const& f, std::string const& v);
|
|
||||||
|
|
||||||
value_type data;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct less : private beast::ci_less
|
|
||||||
{
|
|
||||||
template <class String>
|
|
||||||
bool
|
|
||||||
operator() (String const& lhs, element const& rhs) const;
|
|
||||||
|
|
||||||
template <class String>
|
|
||||||
bool
|
|
||||||
operator() (element const& lhs, String const& rhs) const;
|
|
||||||
|
|
||||||
bool
|
|
||||||
operator() (element const& lhs, element const& rhs) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct transform
|
|
||||||
: public std::unary_function <element, value_type>
|
|
||||||
{
|
|
||||||
value_type const&
|
|
||||||
operator() (element const& e) const
|
|
||||||
{
|
|
||||||
return e.data;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using list_t = boost::intrusive::make_list <element,
|
|
||||||
boost::intrusive::constant_time_size <false>
|
|
||||||
>::type;
|
|
||||||
|
|
||||||
using set_t = boost::intrusive::make_set <element,
|
|
||||||
boost::intrusive::constant_time_size <true>,
|
|
||||||
boost::intrusive::compare<less>
|
|
||||||
>::type;
|
|
||||||
|
|
||||||
list_t list_;
|
|
||||||
set_t set_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using iterator = boost::transform_iterator <transform,
|
|
||||||
list_t::const_iterator>;
|
|
||||||
using const_iterator = iterator;
|
|
||||||
|
|
||||||
~headers()
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
headers() = default;
|
|
||||||
|
|
||||||
headers (headers&& other);
|
|
||||||
headers& operator= (headers&& other);
|
|
||||||
|
|
||||||
headers (headers const& other);
|
|
||||||
headers& operator= (headers const& other);
|
|
||||||
|
|
||||||
/** Returns an iterator to headers in order of appearance. */
|
|
||||||
/** @{ */
|
|
||||||
iterator
|
|
||||||
begin() const;
|
|
||||||
|
|
||||||
iterator
|
|
||||||
end() const;
|
|
||||||
|
|
||||||
iterator
|
|
||||||
cbegin() const;
|
|
||||||
|
|
||||||
iterator
|
|
||||||
cend() const;
|
|
||||||
/** @} */
|
|
||||||
|
|
||||||
/** Returns an iterator to the case-insensitive matching header. */
|
|
||||||
template <class = void>
|
|
||||||
iterator
|
|
||||||
find (std::string const& field) const;
|
|
||||||
|
|
||||||
/** Returns the value for a case-insensitive matching header, or "" */
|
|
||||||
template <class = void>
|
|
||||||
std::string const&
|
|
||||||
operator[] (std::string const& field) const;
|
|
||||||
|
|
||||||
/** Clear the contents of the headers. */
|
|
||||||
template <class = void>
|
|
||||||
void
|
|
||||||
clear() noexcept;
|
|
||||||
|
|
||||||
/** Remove a field.
|
|
||||||
@return The number of fields removed.
|
|
||||||
*/
|
|
||||||
template <class = void>
|
|
||||||
std::size_t
|
|
||||||
erase (std::string const& field);
|
|
||||||
|
|
||||||
/** Append a field value.
|
|
||||||
If a field value already exists the new value will be
|
|
||||||
extended as per RFC2616 Section 4.2.
|
|
||||||
*/
|
|
||||||
// VFALCO TODO Consider allowing rvalue references for std::move
|
|
||||||
template <class = void>
|
|
||||||
void
|
|
||||||
append (std::string const& field, std::string const& value);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
std::string
|
|
||||||
to_string (headers const& h);
|
|
||||||
|
|
||||||
// HACK!
|
|
||||||
template <class = void>
|
|
||||||
std::map <std::string, std::string>
|
|
||||||
build_map (headers const& h);
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
headers::element::element (
|
|
||||||
std::string const& f, std::string const& v)
|
|
||||||
{
|
|
||||||
data.first = f;
|
|
||||||
data.second = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class String>
|
|
||||||
bool
|
|
||||||
headers::less::operator() (
|
|
||||||
String const& lhs, element const& rhs) const
|
|
||||||
{
|
|
||||||
return beast::ci_less::operator() (lhs, rhs.data.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class String>
|
|
||||||
bool
|
|
||||||
headers::less::operator() (
|
|
||||||
element const& lhs, String const& rhs) const
|
|
||||||
{
|
|
||||||
return beast::ci_less::operator() (lhs.data.first, rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
bool
|
|
||||||
headers::less::operator() (
|
|
||||||
element const& lhs, element const& rhs) const
|
|
||||||
{
|
|
||||||
return beast::ci_less::operator() (lhs.data.first, rhs.data.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
inline
|
|
||||||
headers::headers (headers&& other)
|
|
||||||
: list_ (std::move (other.list_))
|
|
||||||
, set_ (std::move (other.set_))
|
|
||||||
{
|
|
||||||
other.list_.clear();
|
|
||||||
other.set_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
headers&
|
|
||||||
headers::operator= (headers&& other)
|
|
||||||
{
|
|
||||||
list_ = std::move(other.list_);
|
|
||||||
set_ = std::move(other.set_);
|
|
||||||
other.list_.clear();
|
|
||||||
other.set_.clear();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
headers::headers (headers const& other)
|
|
||||||
{
|
|
||||||
for (auto const& e : other.list_)
|
|
||||||
append (e.data.first, e.data.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
headers&
|
|
||||||
headers::operator= (headers const& other)
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
for (auto const& e : other.list_)
|
|
||||||
append (e.data.first, e.data.second);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
headers::iterator
|
|
||||||
headers::begin() const
|
|
||||||
{
|
|
||||||
return {list_.cbegin(), transform{}};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
headers::iterator
|
|
||||||
headers::end() const
|
|
||||||
{
|
|
||||||
return {list_.cend(), transform{}};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
headers::iterator
|
|
||||||
headers::cbegin() const
|
|
||||||
{
|
|
||||||
return {list_.cbegin(), transform{}};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
headers::iterator
|
|
||||||
headers::cend() const
|
|
||||||
{
|
|
||||||
return {list_.cend(), transform{}};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
headers::iterator
|
|
||||||
headers::find (std::string const& field) const
|
|
||||||
{
|
|
||||||
auto const iter (set_.find (field, less{}));
|
|
||||||
if (iter == set_.end())
|
|
||||||
return {list_.end(), transform{}};
|
|
||||||
return {list_.iterator_to (*iter), transform{}};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
std::string const&
|
|
||||||
headers::operator[] (std::string const& field) const
|
|
||||||
{
|
|
||||||
static std::string none;
|
|
||||||
auto const found (find (field));
|
|
||||||
if (found == end())
|
|
||||||
return none;
|
|
||||||
return found->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
void
|
|
||||||
headers::clear() noexcept
|
|
||||||
{
|
|
||||||
for (auto iter (list_.begin()); iter != list_.end();)
|
|
||||||
delete &(*iter++);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
std::size_t
|
|
||||||
headers::erase (std::string const& field)
|
|
||||||
{
|
|
||||||
auto const iter = set_.find(field, less{});
|
|
||||||
if (iter == set_.end())
|
|
||||||
return 0;
|
|
||||||
element& e = *iter;
|
|
||||||
set_.erase(set_.iterator_to(e));
|
|
||||||
list_.erase(list_.iterator_to(e));
|
|
||||||
delete &e;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
void
|
|
||||||
headers::append (std::string const& field,
|
|
||||||
std::string const& value)
|
|
||||||
{
|
|
||||||
set_t::insert_commit_data d;
|
|
||||||
auto const result (set_.insert_check (field, less{}, d));
|
|
||||||
if (result.second)
|
|
||||||
{
|
|
||||||
element* const p = new element (field, value);
|
|
||||||
list_.push_back (*p);
|
|
||||||
set_.insert_commit (*p, d);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If field already exists, append comma
|
|
||||||
// separated value as per RFC2616 section 4.2
|
|
||||||
auto& cur (result.first->data.second);
|
|
||||||
cur.reserve (cur.size() + 1 + value.size());
|
|
||||||
cur.append (1, ',');
|
|
||||||
cur.append (value);
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template <class Streambuf>
|
|
||||||
void
|
|
||||||
write (Streambuf& stream, std::string const& s)
|
|
||||||
{
|
|
||||||
stream.commit (boost::asio::buffer_copy (
|
|
||||||
stream.prepare (s.size()), boost::asio::buffer(s)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Streambuf>
|
|
||||||
void
|
|
||||||
write (Streambuf& stream, char const* s)
|
|
||||||
{
|
|
||||||
auto const len (::strlen(s));
|
|
||||||
stream.commit (boost::asio::buffer_copy (
|
|
||||||
stream.prepare (len), boost::asio::buffer (s, len)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class Streambuf>
|
|
||||||
void
|
|
||||||
write (Streambuf& stream, headers const& h)
|
|
||||||
{
|
|
||||||
for (auto const& _ : h)
|
|
||||||
{
|
|
||||||
write (stream, _.first);
|
|
||||||
write (stream, ": ");
|
|
||||||
write (stream, _.second);
|
|
||||||
write (stream, "\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
std::string
|
|
||||||
to_string (headers const& h)
|
|
||||||
{
|
|
||||||
std::string s;
|
|
||||||
std::size_t n (0);
|
|
||||||
for (auto const& e : h)
|
|
||||||
n += e.first.size() + 2 + e.second.size() + 2;
|
|
||||||
s.reserve (n);
|
|
||||||
for (auto const& e : h)
|
|
||||||
{
|
|
||||||
s.append (e.first);
|
|
||||||
s.append (": ");
|
|
||||||
s.append (e.second);
|
|
||||||
s.append ("\r\n");
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline
|
|
||||||
std::ostream&
|
|
||||||
operator<< (std::ostream& s, headers const& h)
|
|
||||||
{
|
|
||||||
s << to_string(h);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
std::map <std::string, std::string>
|
|
||||||
build_map (headers const& h)
|
|
||||||
{
|
|
||||||
std::map <std::string, std::string> c;
|
|
||||||
for (auto const& e : h)
|
|
||||||
{
|
|
||||||
auto key (e.first);
|
|
||||||
// TODO Replace with safe C++14 version
|
|
||||||
std::transform (key.begin(), key.end(), key.begin(), ::tolower);
|
|
||||||
c [key] = e.second;
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
|
|||||||
@@ -1,230 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
|
||||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
//==============================================================================
|
|
||||||
|
|
||||||
#include <beast/http/URL.h>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
|
|
||||||
URL::URL (
|
|
||||||
std::string scheme_,
|
|
||||||
std::string host_,
|
|
||||||
std::uint16_t port_,
|
|
||||||
std::string port_string_,
|
|
||||||
std::string path_,
|
|
||||||
std::string query_,
|
|
||||||
std::string fragment_,
|
|
||||||
std::string userinfo_)
|
|
||||||
: m_scheme (scheme_)
|
|
||||||
, m_host (host_)
|
|
||||||
, m_port (port_)
|
|
||||||
, m_port_string (port_string_)
|
|
||||||
, m_path (path_)
|
|
||||||
, m_query (query_)
|
|
||||||
, m_fragment (fragment_)
|
|
||||||
, m_userinfo (userinfo_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
bool
|
|
||||||
URL::empty () const
|
|
||||||
{
|
|
||||||
return m_scheme.empty ();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
const& URL::scheme () const
|
|
||||||
{
|
|
||||||
return m_scheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
const& URL::host () const
|
|
||||||
{
|
|
||||||
return m_host;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
const& URL::port_string () const
|
|
||||||
{
|
|
||||||
return m_port_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uint16_t
|
|
||||||
URL::port () const
|
|
||||||
{
|
|
||||||
return m_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
const& URL::path () const
|
|
||||||
{
|
|
||||||
return m_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
const& URL::query () const
|
|
||||||
{
|
|
||||||
return m_query;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
const& URL::fragment () const
|
|
||||||
{
|
|
||||||
return m_fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
const& URL::userinfo () const
|
|
||||||
{
|
|
||||||
return m_userinfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
std::pair<bool, URL>
|
|
||||||
parse_URL(std::string const& url)
|
|
||||||
{
|
|
||||||
std::size_t const buflen (url.size ());
|
|
||||||
char const* const buf (url.c_str ());
|
|
||||||
|
|
||||||
joyent::http_parser_url parser;
|
|
||||||
|
|
||||||
if (joyent::http_parser_parse_url (buf, buflen, false, &parser) != 0)
|
|
||||||
return std::make_pair (false, URL{});
|
|
||||||
|
|
||||||
std::string scheme;
|
|
||||||
std::string host;
|
|
||||||
std::uint16_t port (0);
|
|
||||||
std::string port_string;
|
|
||||||
std::string path;
|
|
||||||
std::string query;
|
|
||||||
std::string fragment;
|
|
||||||
std::string userinfo;
|
|
||||||
|
|
||||||
if ((parser.field_set & (1<<joyent::UF_SCHEMA)) != 0)
|
|
||||||
{
|
|
||||||
scheme = std::string (
|
|
||||||
buf + parser.field_data [joyent::UF_SCHEMA].off,
|
|
||||||
parser.field_data [joyent::UF_SCHEMA].len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((parser.field_set & (1<<joyent::UF_HOST)) != 0)
|
|
||||||
{
|
|
||||||
host = std::string (
|
|
||||||
buf + parser.field_data [joyent::UF_HOST].off,
|
|
||||||
parser.field_data [joyent::UF_HOST].len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((parser.field_set & (1<<joyent::UF_PORT)) != 0)
|
|
||||||
{
|
|
||||||
port = parser.port;
|
|
||||||
port_string = std::string (
|
|
||||||
buf + parser.field_data [joyent::UF_PORT].off,
|
|
||||||
parser.field_data [joyent::UF_PORT].len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((parser.field_set & (1<<joyent::UF_PATH)) != 0)
|
|
||||||
{
|
|
||||||
path = std::string (
|
|
||||||
buf + parser.field_data [joyent::UF_PATH].off,
|
|
||||||
parser.field_data [joyent::UF_PATH].len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((parser.field_set & (1<<joyent::UF_QUERY)) != 0)
|
|
||||||
{
|
|
||||||
query = std::string (
|
|
||||||
buf + parser.field_data [joyent::UF_QUERY].off,
|
|
||||||
parser.field_data [joyent::UF_QUERY].len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((parser.field_set & (1<<joyent::UF_FRAGMENT)) != 0)
|
|
||||||
{
|
|
||||||
fragment = std::string (
|
|
||||||
buf + parser.field_data [joyent::UF_FRAGMENT].off,
|
|
||||||
parser.field_data [joyent::UF_FRAGMENT].len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((parser.field_set & (1<<joyent::UF_USERINFO)) != 0)
|
|
||||||
{
|
|
||||||
userinfo = std::string (
|
|
||||||
buf + parser.field_data [joyent::UF_USERINFO].off,
|
|
||||||
parser.field_data [joyent::UF_USERINFO].len);
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::make_pair (true,
|
|
||||||
URL {scheme, host, port, port_string, path, query, fragment, userinfo});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
to_string (URL const& url)
|
|
||||||
{
|
|
||||||
std::string s;
|
|
||||||
|
|
||||||
if (!url.empty ())
|
|
||||||
{
|
|
||||||
// Pre-allocate enough for components and inter-component separators
|
|
||||||
s.reserve (
|
|
||||||
url.scheme ().length () + url.userinfo ().length () +
|
|
||||||
url.host ().length () + url.port_string ().length () +
|
|
||||||
url.query ().length () + url.fragment ().length () + 16);
|
|
||||||
|
|
||||||
s.append (url.scheme ());
|
|
||||||
s.append ("://");
|
|
||||||
|
|
||||||
if (!url.userinfo ().empty ())
|
|
||||||
{
|
|
||||||
s.append (url.userinfo ());
|
|
||||||
s.append ("@");
|
|
||||||
}
|
|
||||||
|
|
||||||
s.append (url.host ());
|
|
||||||
|
|
||||||
if (url.port ())
|
|
||||||
{
|
|
||||||
s.append (":");
|
|
||||||
s.append (url.port_string ());
|
|
||||||
}
|
|
||||||
|
|
||||||
s.append (url.path ());
|
|
||||||
|
|
||||||
if (!url.query ().empty ())
|
|
||||||
{
|
|
||||||
s.append ("?");
|
|
||||||
s.append (url.query ());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!url.fragment ().empty ())
|
|
||||||
{
|
|
||||||
s.append ("#");
|
|
||||||
s.append (url.fragment ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream&
|
|
||||||
operator<< (std::ostream &os, URL const& url)
|
|
||||||
{
|
|
||||||
os << to_string (url);
|
|
||||||
return os;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
312
beast/http/impl/basic_headers.ipp
Normal file
312
beast/http/impl/basic_headers.ipp
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_BASIC_HEADERS_IPP_INCLUDED
|
||||||
|
#define BEAST_HTTP_BASIC_HEADERS_IPP_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/detail/writes.h>
|
||||||
|
#include <beast/asio/type_check.h>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
basic_headers_base::begin() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return list_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
basic_headers_base::end() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return list_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
basic_headers_base::cbegin() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return list_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
basic_headers_base::cend() const ->
|
||||||
|
const_iterator
|
||||||
|
{
|
||||||
|
return list_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
delete_all()
|
||||||
|
{
|
||||||
|
for(auto it = list_.begin(); it != list_.end();)
|
||||||
|
{
|
||||||
|
auto& e = *it++;
|
||||||
|
e.~element();
|
||||||
|
alloc_traits::deallocate(
|
||||||
|
this->member(), &e, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
move_assign(basic_headers& other, std::false_type)
|
||||||
|
{
|
||||||
|
if(this->member() != other.member())
|
||||||
|
{
|
||||||
|
copy_from(other);
|
||||||
|
other.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set_ = std::move(other.set_);
|
||||||
|
list_ = std::move(other.list_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
move_assign(basic_headers& other, std::true_type)
|
||||||
|
{
|
||||||
|
this->member() = std::move(other.member());
|
||||||
|
set_ = std::move(other.set_);
|
||||||
|
list_ = std::move(other.list_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
copy_assign(basic_headers const& other, std::false_type)
|
||||||
|
{
|
||||||
|
copy_from(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
copy_assign(basic_headers const& other, std::true_type)
|
||||||
|
{
|
||||||
|
this->member() = other.member();
|
||||||
|
copy_from(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
~basic_headers()
|
||||||
|
{
|
||||||
|
delete_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
basic_headers(Allocator const& alloc)
|
||||||
|
: empty_base_optimization<
|
||||||
|
alloc_type>(alloc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
basic_headers(basic_headers&& other)
|
||||||
|
: empty_base_optimization<alloc_type>(
|
||||||
|
std::move(other.member()))
|
||||||
|
, detail::basic_headers_base(
|
||||||
|
std::move(other.set_), std::move(other.list_))
|
||||||
|
{
|
||||||
|
other.list_.clear();
|
||||||
|
other.set_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
auto
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
operator=(basic_headers&& other) ->
|
||||||
|
basic_headers&
|
||||||
|
{
|
||||||
|
if(this == &other)
|
||||||
|
return *this;
|
||||||
|
clear();
|
||||||
|
move_assign(other, std::integral_constant<bool,
|
||||||
|
alloc_traits::propagate_on_container_move_assignment::value>{});
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
basic_headers(basic_headers const& other)
|
||||||
|
: basic_headers(alloc_traits::
|
||||||
|
select_on_container_copy_construction(other.member()))
|
||||||
|
{
|
||||||
|
copy_from(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
auto
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
operator=(basic_headers const& other) ->
|
||||||
|
basic_headers&
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
copy_assign(other, std::integral_constant<bool,
|
||||||
|
alloc_traits::propagate_on_container_copy_assignment::value>{});
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
template<class OtherAlloc>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
basic_headers(basic_headers<OtherAlloc> const& other)
|
||||||
|
{
|
||||||
|
copy_from(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
template<class OtherAlloc>
|
||||||
|
auto
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
operator=(basic_headers<OtherAlloc> const& other) ->
|
||||||
|
basic_headers&
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
copy_from(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
template<class FwdIt>
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
basic_headers(FwdIt first, FwdIt last)
|
||||||
|
{
|
||||||
|
for(;first != last; ++first)
|
||||||
|
insert(first->name(), first->value());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
auto
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
find(boost::string_ref const& name) const ->
|
||||||
|
iterator
|
||||||
|
{
|
||||||
|
auto const it = set_.find(name, less{});
|
||||||
|
if(it == set_.end())
|
||||||
|
return list_.end();
|
||||||
|
return list_.iterator_to(*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
boost::string_ref
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
operator[](boost::string_ref const& name) const
|
||||||
|
{
|
||||||
|
// VFALCO This none object looks sketchy
|
||||||
|
static boost::string_ref const none;
|
||||||
|
auto const it = find(name);
|
||||||
|
if(it == end())
|
||||||
|
return none;
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
clear() noexcept
|
||||||
|
{
|
||||||
|
delete_all();
|
||||||
|
list_.clear();
|
||||||
|
set_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
std::size_t
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
erase(boost::string_ref const& name)
|
||||||
|
{
|
||||||
|
auto const it = set_.find(name, less{});
|
||||||
|
if(it == set_.end())
|
||||||
|
return 0;
|
||||||
|
auto& e = *it;
|
||||||
|
set_.erase(set_.iterator_to(e));
|
||||||
|
list_.erase(list_.iterator_to(e));
|
||||||
|
alloc_traits::deallocate(this->member(), &e, 1);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
insert(boost::string_ref const& name,
|
||||||
|
boost::string_ref const& value)
|
||||||
|
{
|
||||||
|
typename set_t::insert_commit_data d;
|
||||||
|
auto const result =
|
||||||
|
set_.insert_check(name, less{}, d);
|
||||||
|
if (result.second)
|
||||||
|
{
|
||||||
|
auto const p = alloc_traits::allocate(
|
||||||
|
this->member(), 1);
|
||||||
|
alloc_traits::construct(
|
||||||
|
this->member(), p, name, value);
|
||||||
|
list_.push_back(*p);
|
||||||
|
set_.insert_commit(*p, d);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If field already exists, insert comma
|
||||||
|
// separated value as per RFC2616 section 4.2
|
||||||
|
auto& cur = result.first->data.second;
|
||||||
|
cur.reserve(cur.size() + 1 + value.size());
|
||||||
|
cur.append(1, ',');
|
||||||
|
cur.append(value.data(), value.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
void
|
||||||
|
basic_headers<Allocator>::
|
||||||
|
replace(boost::string_ref const& name,
|
||||||
|
boost::string_ref const& value)
|
||||||
|
{
|
||||||
|
erase(name);
|
||||||
|
insert(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,327 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
|
||||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
//==============================================================================
|
|
||||||
|
|
||||||
#include <beast/http/basic_parser.h>
|
|
||||||
#include <beast/http/impl/joyent_parser.h>
|
|
||||||
#include <beast/http/rfc2616.h>
|
|
||||||
#include <boost/system/error_code.hpp>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace http {
|
|
||||||
|
|
||||||
boost::system::error_category const&
|
|
||||||
message_category() noexcept
|
|
||||||
{
|
|
||||||
class message_category_t : public boost::system::error_category
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
const char*
|
|
||||||
name() const noexcept override
|
|
||||||
{
|
|
||||||
return "http::message";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
message (int ev) const override
|
|
||||||
{
|
|
||||||
return joyent::http_errno_description (
|
|
||||||
static_cast<joyent::http_errno>(ev));
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::system::error_condition
|
|
||||||
default_error_condition (int ev) const noexcept override
|
|
||||||
{
|
|
||||||
return boost::system::error_condition (ev, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
equivalent (int ev, boost::system::error_condition const& condition
|
|
||||||
) const noexcept override
|
|
||||||
{
|
|
||||||
return condition.value() == ev &&
|
|
||||||
&condition.category() == this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
equivalent (boost::system::error_code const& error,
|
|
||||||
int ev) const noexcept override
|
|
||||||
{
|
|
||||||
return error.value() == ev &&
|
|
||||||
&error.category() == this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static message_category_t cat;
|
|
||||||
return cat;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
basic_parser::basic_parser (bool request) noexcept
|
|
||||||
{
|
|
||||||
static_assert (sizeof(joyent::http_parser) == sizeof(state_t),
|
|
||||||
"state_t size must match http_parser size");
|
|
||||||
|
|
||||||
static_assert (sizeof(joyent::http_parser_settings) == sizeof(hooks_t),
|
|
||||||
"hooks_t size must match http_parser_settings size");
|
|
||||||
|
|
||||||
auto s (reinterpret_cast <joyent::http_parser*> (&state_));
|
|
||||||
s->data = this;
|
|
||||||
|
|
||||||
auto h (reinterpret_cast <joyent::http_parser_settings*> (&hooks_));
|
|
||||||
h->on_message_begin = &basic_parser::cb_message_start;
|
|
||||||
h->on_url = &basic_parser::cb_url;
|
|
||||||
h->on_status = &basic_parser::cb_status;
|
|
||||||
h->on_header_field = &basic_parser::cb_header_field;
|
|
||||||
h->on_header_value = &basic_parser::cb_header_value;
|
|
||||||
h->on_headers_complete = &basic_parser::cb_headers_complete;
|
|
||||||
h->on_body = &basic_parser::cb_body;
|
|
||||||
h->on_message_complete = &basic_parser::cb_message_complete;
|
|
||||||
h->on_chunk_header = &basic_parser::cb_chunk_header;
|
|
||||||
h->on_chunk_complete = &basic_parser::cb_chunk_complete;
|
|
||||||
|
|
||||||
joyent::http_parser_init (s, request
|
|
||||||
? joyent::http_parser_type::HTTP_REQUEST
|
|
||||||
: joyent::http_parser_type::HTTP_RESPONSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_parser&
|
|
||||||
basic_parser::operator= (basic_parser&& other)
|
|
||||||
{
|
|
||||||
*reinterpret_cast<joyent::http_parser*>(&state_) =
|
|
||||||
*reinterpret_cast<joyent::http_parser*>(&other.state_);
|
|
||||||
reinterpret_cast<joyent::http_parser*>(&state_)->data = this;
|
|
||||||
complete_ = other.complete_;
|
|
||||||
url_ = std::move (other.url_);
|
|
||||||
status_ = std::move (other.status_);
|
|
||||||
field_ = std::move (other.field_);
|
|
||||||
value_ = std::move (other.value_);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto
|
|
||||||
basic_parser::write (void const* data, std::size_t bytes) ->
|
|
||||||
std::pair <error_code, std::size_t>
|
|
||||||
{
|
|
||||||
std::pair <error_code, std::size_t> result ({}, 0);
|
|
||||||
auto s (reinterpret_cast <joyent::http_parser*> (&state_));
|
|
||||||
auto h (reinterpret_cast <joyent::http_parser_settings const*> (&hooks_));
|
|
||||||
result.second = joyent::http_parser_execute (s, h,
|
|
||||||
static_cast <const char*> (data), bytes);
|
|
||||||
result.first = error_code{static_cast<int>(s->http_errno),
|
|
||||||
message_category()};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto
|
|
||||||
basic_parser::write_eof() ->
|
|
||||||
error_code
|
|
||||||
{
|
|
||||||
auto s (reinterpret_cast <joyent::http_parser*> (&state_));
|
|
||||||
auto h (reinterpret_cast <joyent::http_parser_settings const*> (&hooks_));
|
|
||||||
joyent::http_parser_execute (s, h, nullptr, 0);
|
|
||||||
return error_code{static_cast<int>(s->http_errno),
|
|
||||||
message_category()};
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void
|
|
||||||
basic_parser::check_header()
|
|
||||||
{
|
|
||||||
if (! value_.empty())
|
|
||||||
{
|
|
||||||
rfc2616::trim_right_in_place (value_);
|
|
||||||
on_field (field_, value_);
|
|
||||||
field_.clear();
|
|
||||||
value_.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::do_message_start ()
|
|
||||||
{
|
|
||||||
complete_ = false;
|
|
||||||
url_.clear();
|
|
||||||
status_.clear();
|
|
||||||
field_.clear();
|
|
||||||
value_.clear();
|
|
||||||
on_start();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::do_url (char const* in, std::size_t bytes)
|
|
||||||
{
|
|
||||||
url_.append (static_cast <char const*> (in), bytes);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::do_status (char const* in, std::size_t bytes)
|
|
||||||
{
|
|
||||||
status_.append (static_cast <char const*> (in), bytes);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::do_header_field (char const* in, std::size_t bytes)
|
|
||||||
{
|
|
||||||
check_header();
|
|
||||||
field_.append (static_cast <char const*> (in), bytes);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::do_header_value (char const* in, std::size_t bytes)
|
|
||||||
{
|
|
||||||
value_.append (static_cast <char const*> (in), bytes);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called when all the headers are complete but before
|
|
||||||
the content body, if present.
|
|
||||||
Returning 1 from here tells the joyent parser
|
|
||||||
that the message has no body (e.g. a HEAD request).
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
basic_parser::do_headers_complete()
|
|
||||||
{
|
|
||||||
check_header();
|
|
||||||
auto const p (reinterpret_cast <joyent::http_parser const*> (&state_));
|
|
||||||
bool const keep_alive (joyent::http_should_keep_alive (p) != 0);
|
|
||||||
if (p->type == joyent::http_parser_type::HTTP_REQUEST)
|
|
||||||
return on_request (joyent::convert_http_method (
|
|
||||||
joyent::http_method(p->method)), url_,
|
|
||||||
p->http_major, p->http_minor, keep_alive, p->upgrade) ? 0 : 1;
|
|
||||||
return on_response (p->status_code, status_,
|
|
||||||
p->http_major, p->http_minor, keep_alive, p->upgrade) ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called repeatedly for the content body. The passed buffer
|
|
||||||
has already had the transfer-encoding removed.
|
|
||||||
*/
|
|
||||||
int
|
|
||||||
basic_parser::do_body (char const* in, std::size_t bytes)
|
|
||||||
{
|
|
||||||
on_body (in, bytes);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called when the both the headers and content body (if any) are complete. */
|
|
||||||
int
|
|
||||||
basic_parser::do_message_complete ()
|
|
||||||
{
|
|
||||||
complete_ = true;
|
|
||||||
on_complete();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::do_chunk_header()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::do_chunk_complete()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::cb_message_start (joyent::http_parser* p)
|
|
||||||
{
|
|
||||||
return reinterpret_cast <basic_parser*> (
|
|
||||||
p->data)->do_message_start();
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::cb_url (joyent::http_parser* p,
|
|
||||||
char const* in, std::size_t bytes)
|
|
||||||
{
|
|
||||||
return reinterpret_cast <basic_parser*> (
|
|
||||||
p->data)->do_url (in, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::cb_status (joyent::http_parser* p,
|
|
||||||
char const* in, std::size_t bytes)
|
|
||||||
{
|
|
||||||
return reinterpret_cast <basic_parser*> (
|
|
||||||
p->data)->do_status (in, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::cb_header_field (joyent::http_parser* p,
|
|
||||||
char const* in, std::size_t bytes)
|
|
||||||
{
|
|
||||||
return reinterpret_cast <basic_parser*> (
|
|
||||||
p->data)->do_header_field (in, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::cb_header_value (joyent::http_parser* p,
|
|
||||||
char const* in, std::size_t bytes)
|
|
||||||
{
|
|
||||||
return reinterpret_cast <basic_parser*> (
|
|
||||||
p->data)->do_header_value (in, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::cb_headers_complete (joyent::http_parser* p)
|
|
||||||
{
|
|
||||||
return reinterpret_cast <basic_parser*> (
|
|
||||||
p->data)->do_headers_complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::cb_body (joyent::http_parser* p,
|
|
||||||
char const* in, std::size_t bytes)
|
|
||||||
{
|
|
||||||
return reinterpret_cast <basic_parser*> (
|
|
||||||
p->data)->do_body (
|
|
||||||
in, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::cb_message_complete (joyent::http_parser* p)
|
|
||||||
{
|
|
||||||
return reinterpret_cast <basic_parser*> (
|
|
||||||
p->data)->do_message_complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::cb_chunk_header (joyent::http_parser* p)
|
|
||||||
{
|
|
||||||
return reinterpret_cast <basic_parser*> (
|
|
||||||
p->data)->do_chunk_header();
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
basic_parser::cb_chunk_complete (joyent::http_parser* p)
|
|
||||||
{
|
|
||||||
return reinterpret_cast <basic_parser*> (
|
|
||||||
p->data)->do_chunk_complete();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // http
|
|
||||||
} // beast
|
|
||||||
260
beast/http/impl/basic_parser.ipp
Normal file
260
beast/http/impl/basic_parser.ipp
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <beast/http/src/nodejs_parser.h>
|
||||||
|
#include <beast/http/rfc2616.h>
|
||||||
|
#include <beast/http/detail/error.h>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
basic_parser<Derived>::
|
||||||
|
basic_parser(basic_parser&& other)
|
||||||
|
{
|
||||||
|
state_ = other.state_;
|
||||||
|
state_.data = this;
|
||||||
|
complete_ = other.complete_;
|
||||||
|
url_ = std::move(other.url_);
|
||||||
|
status_ = std::move(other.status_);
|
||||||
|
field_ = std::move(other.field_);
|
||||||
|
value_ = std::move(other.value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
auto
|
||||||
|
basic_parser<Derived>::operator=(basic_parser&& other) ->
|
||||||
|
basic_parser&
|
||||||
|
{
|
||||||
|
state_ = other.state_;
|
||||||
|
state_.data = this;
|
||||||
|
complete_ = other.complete_;
|
||||||
|
url_ = std::move(other.url_);
|
||||||
|
status_ = std::move(other.status_);
|
||||||
|
field_ = std::move(other.field_);
|
||||||
|
value_ = std::move(other.value_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
basic_parser<Derived>::
|
||||||
|
basic_parser(basic_parser const& other)
|
||||||
|
{
|
||||||
|
state_ = other.state_;
|
||||||
|
state_.data = this;
|
||||||
|
complete_ = other.complete_;
|
||||||
|
url_ = other.url_;
|
||||||
|
status_ = other.status_;
|
||||||
|
field_ = other.field_;
|
||||||
|
value_ = other.value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
auto
|
||||||
|
basic_parser<Derived>::
|
||||||
|
operator=(basic_parser const& other) ->
|
||||||
|
basic_parser&
|
||||||
|
{
|
||||||
|
state_ = other.state_;
|
||||||
|
state_.data = this;
|
||||||
|
complete_ = other.complete_;
|
||||||
|
url_ = other.url_;
|
||||||
|
status_ = other.status_;
|
||||||
|
field_ = other.field_;
|
||||||
|
value_ = other.value_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
basic_parser<Derived>::basic_parser(bool request) noexcept
|
||||||
|
{
|
||||||
|
state_.data = this;
|
||||||
|
http_parser_init(&state_, request
|
||||||
|
? http_parser_type::HTTP_REQUEST
|
||||||
|
: http_parser_type::HTTP_RESPONSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
std::size_t
|
||||||
|
basic_parser<Derived>::write(void const* data,
|
||||||
|
std::size_t size, error_code& ec)
|
||||||
|
{
|
||||||
|
ec_ = &ec;
|
||||||
|
auto const n = http_parser_execute(
|
||||||
|
&state_, hooks(),
|
||||||
|
static_cast<const char*>(data), size);
|
||||||
|
if(! ec)
|
||||||
|
ec = detail::make_error(
|
||||||
|
static_cast<int>(state_.http_errno));
|
||||||
|
if(ec)
|
||||||
|
return 0;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
void
|
||||||
|
basic_parser<Derived>::write_eof(error_code& ec)
|
||||||
|
{
|
||||||
|
ec_ = &ec;
|
||||||
|
http_parser_execute(
|
||||||
|
&state_, hooks(), nullptr, 0);
|
||||||
|
if(! ec)
|
||||||
|
ec = detail::make_error(
|
||||||
|
static_cast<int>(state_.http_errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
void
|
||||||
|
basic_parser<Derived>::check_header()
|
||||||
|
{
|
||||||
|
if (! value_.empty())
|
||||||
|
{
|
||||||
|
rfc2616::trim_right_in_place(value_);
|
||||||
|
call_on_field(field_, value_,
|
||||||
|
has_on_field<Derived>{});
|
||||||
|
field_.clear();
|
||||||
|
value_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
int
|
||||||
|
basic_parser<Derived>::cb_message_start(http_parser* p)
|
||||||
|
{
|
||||||
|
auto& t = *reinterpret_cast<basic_parser*>(p->data);
|
||||||
|
t.complete_ = false;
|
||||||
|
t.url_.clear();
|
||||||
|
t.status_.clear();
|
||||||
|
t.field_.clear();
|
||||||
|
t.value_.clear();
|
||||||
|
t.call_on_start(has_on_start<Derived>{});
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
int
|
||||||
|
basic_parser<Derived>::cb_url(http_parser* p,
|
||||||
|
char const* in, std::size_t bytes)
|
||||||
|
{
|
||||||
|
auto& t = *reinterpret_cast<basic_parser*>(p->data);
|
||||||
|
t.url_.append(in, bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
int
|
||||||
|
basic_parser<Derived>::cb_status(http_parser* p,
|
||||||
|
char const* in, std::size_t bytes)
|
||||||
|
{
|
||||||
|
auto& t = *reinterpret_cast<basic_parser*>(p->data);
|
||||||
|
t.status_.append(in, bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
int
|
||||||
|
basic_parser<Derived>::cb_header_field(http_parser* p,
|
||||||
|
char const* in, std::size_t bytes)
|
||||||
|
{
|
||||||
|
auto& t = *reinterpret_cast<basic_parser*>(p->data);
|
||||||
|
t.check_header();
|
||||||
|
t.field_.append(in, bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
int
|
||||||
|
basic_parser<Derived>::cb_header_value(http_parser* p,
|
||||||
|
char const* in, std::size_t bytes)
|
||||||
|
{
|
||||||
|
auto& t = *reinterpret_cast<basic_parser*>(p->data);
|
||||||
|
t.value_.append(in, bytes);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Called when all the headers are complete but before
|
||||||
|
the content body, if present.
|
||||||
|
Returning 1 from here tells the nodejs parser
|
||||||
|
that the message has no body (e.g. a HEAD request).
|
||||||
|
*/
|
||||||
|
template<class Derived>
|
||||||
|
int
|
||||||
|
basic_parser<Derived>::cb_headers_complete(http_parser* p)
|
||||||
|
{
|
||||||
|
auto& t = *reinterpret_cast<basic_parser*>(p->data);
|
||||||
|
t.check_header();
|
||||||
|
t.call_on_headers_complete(*t.ec_,
|
||||||
|
has_on_headers_complete<Derived>{});
|
||||||
|
if(*t.ec_)
|
||||||
|
return 1;
|
||||||
|
bool const keep_alive =
|
||||||
|
http_should_keep_alive(p) != 0;
|
||||||
|
if(p->type == http_parser_type::HTTP_REQUEST)
|
||||||
|
{
|
||||||
|
t.call_on_request(convert_http_method(
|
||||||
|
http_method(p->method)), t.url_,
|
||||||
|
p->http_major, p->http_minor, keep_alive,
|
||||||
|
p->upgrade, has_on_request<Derived>{});
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return t.call_on_response(p->status_code, t.status_,
|
||||||
|
p->http_major, p->http_minor, keep_alive,
|
||||||
|
p->upgrade, has_on_response<Derived>{}) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called repeatedly for the content body,
|
||||||
|
// after any transfer-encoding is applied.
|
||||||
|
template<class Derived>
|
||||||
|
int
|
||||||
|
basic_parser<Derived>::cb_body(http_parser* p,
|
||||||
|
char const* in, std::size_t bytes)
|
||||||
|
{
|
||||||
|
auto& t = *reinterpret_cast<basic_parser*>(p->data);
|
||||||
|
t.call_on_body(in, bytes, *t.ec_, has_on_body<Derived>{});
|
||||||
|
return *t.ec_ ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the both the headers
|
||||||
|
// and content body (if any) are complete.
|
||||||
|
template<class Derived>
|
||||||
|
int
|
||||||
|
basic_parser<Derived>::cb_message_complete(http_parser* p)
|
||||||
|
{
|
||||||
|
auto& t = *reinterpret_cast<basic_parser*>(p->data);
|
||||||
|
t.complete_ = true;
|
||||||
|
t.call_on_complete(has_on_complete<Derived>{});
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
int
|
||||||
|
basic_parser<Derived>::cb_chunk_header(http_parser*)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Derived>
|
||||||
|
int
|
||||||
|
basic_parser<Derived>::cb_chunk_complete(http_parser*)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
|
||||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
//==============================================================================
|
|
||||||
|
|
||||||
#include <beast/http/basic_url.h>
|
|
||||||
|
|
||||||
#include <beast/http/impl/joyent_parser.h>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace http {
|
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
void
|
|
||||||
basic_url_base::parse_impl (string_ref s, boost::system::error_code& ec)
|
|
||||||
{
|
|
||||||
joyent::http_parser_url p;
|
|
||||||
|
|
||||||
value_type const* const data (s.data());
|
|
||||||
|
|
||||||
int const error (joyent::http_parser_parse_url (
|
|
||||||
data, s.size(), false, &p));
|
|
||||||
|
|
||||||
if (error)
|
|
||||||
{
|
|
||||||
ec = boost::system::error_code (
|
|
||||||
boost::system::errc::invalid_argument,
|
|
||||||
boost::system::generic_category());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((p.field_set & (1<<joyent::UF_SCHEMA)) != 0)
|
|
||||||
{
|
|
||||||
m_scheme = string_ref (
|
|
||||||
data + p.field_data [joyent::UF_SCHEMA].off,
|
|
||||||
p.field_data [joyent::UF_SCHEMA].len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_scheme = string_ref {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((p.field_set & (1<<joyent::UF_HOST)) != 0)
|
|
||||||
{
|
|
||||||
m_host = string_ref (
|
|
||||||
data + p.field_data [joyent::UF_HOST].off,
|
|
||||||
p.field_data [joyent::UF_HOST].len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_host = string_ref {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((p.field_set & (1<<joyent::UF_PORT)) != 0)
|
|
||||||
{
|
|
||||||
m_port = p.port;
|
|
||||||
m_port_string = string_ref (
|
|
||||||
data + p.field_data [joyent::UF_PORT].off,
|
|
||||||
p.field_data [joyent::UF_PORT].len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_port = 0;
|
|
||||||
m_port_string = string_ref {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((p.field_set & (1<<joyent::UF_PATH)) != 0)
|
|
||||||
{
|
|
||||||
m_path = string_ref (
|
|
||||||
data + p.field_data [joyent::UF_PATH].off,
|
|
||||||
p.field_data [joyent::UF_PATH].len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_path = string_ref {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((p.field_set & (1<<joyent::UF_QUERY)) != 0)
|
|
||||||
{
|
|
||||||
m_query = string_ref (
|
|
||||||
data + p.field_data [joyent::UF_QUERY].off,
|
|
||||||
p.field_data [joyent::UF_QUERY].len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_query = string_ref {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((p.field_set & (1<<joyent::UF_FRAGMENT)) != 0)
|
|
||||||
{
|
|
||||||
m_fragment = string_ref (
|
|
||||||
data + p.field_data [joyent::UF_FRAGMENT].off,
|
|
||||||
p.field_data [joyent::UF_FRAGMENT].len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_fragment = string_ref {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((p.field_set & (1<<joyent::UF_USERINFO)) != 0)
|
|
||||||
{
|
|
||||||
m_userinfo = string_ref (
|
|
||||||
data + p.field_data [joyent::UF_USERINFO].off,
|
|
||||||
p.field_data [joyent::UF_USERINFO].len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_userinfo = string_ref {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // detail
|
|
||||||
} // http
|
|
||||||
} // beast
|
|
||||||
320
beast/http/impl/message.ipp
Normal file
320
beast/http/impl/message.ipp
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_MESSAGE_IPP_INCLUDED
|
||||||
|
#define BEAST_HTTP_MESSAGE_IPP_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/chunk_encode.h>
|
||||||
|
#include <beast/http/type_check.h>
|
||||||
|
#include <beast/http/detail/writes.h>
|
||||||
|
#include <beast/http/detail/write_preparation.h>
|
||||||
|
#include <beast/http/rfc2616.h>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
message<isRequest, Body, Headers>::
|
||||||
|
message()
|
||||||
|
{
|
||||||
|
static_assert(is_Body<Body>::value,
|
||||||
|
"Body requirements not met");
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
message<isRequest, Body, Headers>::
|
||||||
|
message(request_params params)
|
||||||
|
{
|
||||||
|
static_assert(is_Body<Body>::value,
|
||||||
|
"Body requirements not met");
|
||||||
|
static_assert(isRequest, "message is not a request");
|
||||||
|
this->method = params.method;
|
||||||
|
this->url = std::move(params.url);
|
||||||
|
version = params.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
message<isRequest, Body, Headers>::
|
||||||
|
message(response_params params)
|
||||||
|
{
|
||||||
|
static_assert(is_Body<Body>::value,
|
||||||
|
"Body requirements not met");
|
||||||
|
static_assert(! isRequest, "message is not a response");
|
||||||
|
this->status = params.status;
|
||||||
|
this->reason = std::move(params.reason);
|
||||||
|
version = params.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Headers>::
|
||||||
|
write_firstline(Streambuf& streambuf,
|
||||||
|
std::true_type) const
|
||||||
|
{
|
||||||
|
detail::write(streambuf, to_string(this->method));
|
||||||
|
detail::write(streambuf, " ");
|
||||||
|
detail::write(streambuf, this->url);
|
||||||
|
switch(version)
|
||||||
|
{
|
||||||
|
case 10:
|
||||||
|
detail::write(streambuf, " HTTP/1.0\r\n");
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
detail::write(streambuf, " HTTP/1.1\r\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
detail::write(streambuf, " HTTP/");
|
||||||
|
detail::write(streambuf, version / 10);
|
||||||
|
detail::write(streambuf, ".");
|
||||||
|
detail::write(streambuf, version % 10);
|
||||||
|
detail::write(streambuf, "\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
message<isRequest, Body, Headers>::
|
||||||
|
write_firstline(Streambuf& streambuf,
|
||||||
|
std::false_type) const
|
||||||
|
{
|
||||||
|
switch(version)
|
||||||
|
{
|
||||||
|
case 10:
|
||||||
|
detail::write(streambuf, "HTTP/1.0 ");
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
detail::write(streambuf, "HTTP/1.1 ");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
detail::write(streambuf, " HTTP/");
|
||||||
|
detail::write(streambuf, version / 10);
|
||||||
|
detail::write(streambuf, ".");
|
||||||
|
detail::write(streambuf, version % 10);
|
||||||
|
detail::write(streambuf, " ");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
detail::write(streambuf, this->status);
|
||||||
|
detail::write(streambuf, " ");
|
||||||
|
detail::write(streambuf, this->reason);
|
||||||
|
detail::write(streambuf, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
std::string
|
||||||
|
buffers_to_string(ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
std::string s;
|
||||||
|
s.reserve(buffer_size(buffers));
|
||||||
|
for(auto const& b : buffers)
|
||||||
|
s.append(buffer_cast<char const*>(b),
|
||||||
|
buffer_size(b));
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
// Diagnostic output only
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os,
|
||||||
|
message<isRequest, Body, Headers> const& msg)
|
||||||
|
{
|
||||||
|
static_assert(is_WritableBody<Body>::value,
|
||||||
|
"WritableBody requirements not met");
|
||||||
|
error_code ec;
|
||||||
|
detail::write_preparation<isRequest, Body, Headers> wp(msg);
|
||||||
|
wp.init(ec);
|
||||||
|
if(ec)
|
||||||
|
return os;
|
||||||
|
std::mutex m;
|
||||||
|
std::condition_variable cv;
|
||||||
|
bool ready = false;
|
||||||
|
resume_context resume{
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
ready = true;
|
||||||
|
cv.notify_one();
|
||||||
|
}};
|
||||||
|
auto copy = resume;
|
||||||
|
os << detail::buffers_to_string(wp.sb.data());
|
||||||
|
wp.sb.consume(wp.sb.size());
|
||||||
|
auto writef =
|
||||||
|
[&os, &wp](auto const& buffers)
|
||||||
|
{
|
||||||
|
if(wp.chunked)
|
||||||
|
os << detail::buffers_to_string(
|
||||||
|
chunk_encode(buffers));
|
||||||
|
else
|
||||||
|
os << detail::buffers_to_string(
|
||||||
|
buffers);
|
||||||
|
};
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto result = wp.w(std::move(copy), ec, writef);
|
||||||
|
if(ec)
|
||||||
|
return os;
|
||||||
|
if(result)
|
||||||
|
break;
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
copy = resume;
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return ready; });
|
||||||
|
ready = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wp.sb.consume(wp.sb.size());
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
auto result = wp.w(std::move(copy), ec, writef);
|
||||||
|
if(ec)
|
||||||
|
return os;
|
||||||
|
if(result)
|
||||||
|
break;
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
copy = resume;
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return ready; });
|
||||||
|
ready = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(wp.chunked)
|
||||||
|
{
|
||||||
|
// VFALCO Unfortunately the current interface to the
|
||||||
|
// Writer concept prevents us from using coalescing the
|
||||||
|
// final body chunk with the final chunk delimiter.
|
||||||
|
//
|
||||||
|
// write final chunk
|
||||||
|
os << detail::buffers_to_string(chunk_encode_final());
|
||||||
|
if(ec)
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
os << std::endl;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
set_connection(bool keep_alive,
|
||||||
|
message<isRequest, Body, Headers>& req)
|
||||||
|
{
|
||||||
|
if(req.version >= 11)
|
||||||
|
{
|
||||||
|
if(! keep_alive)
|
||||||
|
req.headers.replace("Connection", "close");
|
||||||
|
else
|
||||||
|
req.headers.erase("Connection");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(keep_alive)
|
||||||
|
req.headers.replace("Connection", "keep-alive");
|
||||||
|
else
|
||||||
|
req.headers.erase("Connection");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Body, class Headers,
|
||||||
|
class OtherBody, class OtherAllocator>
|
||||||
|
void
|
||||||
|
set_connection(bool keep_alive,
|
||||||
|
message<false, Body, Headers>& resp,
|
||||||
|
message<true, OtherBody, OtherAllocator> const& req)
|
||||||
|
{
|
||||||
|
if(req.version >= 11)
|
||||||
|
{
|
||||||
|
if(rfc2616::token_in_list(req["Connection"], "close"))
|
||||||
|
keep_alive = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(! rfc2616::token_in_list(req["Connection"], "keep-alive"))
|
||||||
|
keep_alive = false;
|
||||||
|
}
|
||||||
|
set_connection(keep_alive, resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Streambuf, class FieldSequence>
|
||||||
|
void
|
||||||
|
write_fields(Streambuf& streambuf, FieldSequence const& fields)
|
||||||
|
{
|
||||||
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
|
"Streambuf requirements not met");
|
||||||
|
//static_assert(is_FieldSequence<FieldSequence>::value,
|
||||||
|
// "FieldSequence requirements not met");
|
||||||
|
for(auto const& field : fields)
|
||||||
|
{
|
||||||
|
detail::write(streambuf, field.name());
|
||||||
|
detail::write(streambuf, ": ");
|
||||||
|
detail::write(streambuf, field.value());
|
||||||
|
detail::write(streambuf, "\r\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
bool
|
||||||
|
is_keep_alive(message<isRequest, Body, Headers> const& msg)
|
||||||
|
{
|
||||||
|
if(msg.version >= 11)
|
||||||
|
{
|
||||||
|
if(rfc2616::token_in_list(
|
||||||
|
msg.headers["Connection"], "close"))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(rfc2616::token_in_list(
|
||||||
|
msg.headers["Connection"], "keep-alive"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
bool
|
||||||
|
is_upgrade(message<isRequest, Body, Headers> const& msg)
|
||||||
|
{
|
||||||
|
if(msg.version < 11)
|
||||||
|
return false;
|
||||||
|
if(rfc2616::token_in_list(
|
||||||
|
msg.headers["Connection"], "upgrade"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/message.ipp>
|
||||||
|
|
||||||
|
#endif
|
||||||
297
beast/http/impl/read.ipp
Normal file
297
beast/http/impl/read.ipp
Normal file
@@ -0,0 +1,297 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_READ_IPP_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_READ_IPP_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/type_check.h>
|
||||||
|
#include <beast/asio/async_completion.h>
|
||||||
|
#include <beast/asio/bind_handler.h>
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class Stream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class Handler>
|
||||||
|
class read_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
using parser_type =
|
||||||
|
parser<isRequest, Body, Headers>;
|
||||||
|
|
||||||
|
using message_type =
|
||||||
|
message<isRequest, Body, Headers>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
Stream& s;
|
||||||
|
Streambuf& sb;
|
||||||
|
message_type& m;
|
||||||
|
parser_type p;
|
||||||
|
Handler h;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, Stream& s_,
|
||||||
|
Streambuf& sb_, message_type& m_)
|
||||||
|
: s(s_)
|
||||||
|
, sb(sb_)
|
||||||
|
, m(m_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
read_op(read_op&&) = default;
|
||||||
|
read_op(read_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
read_op(DeducedHandler&& h, Stream&s, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), s,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(error_code ec,
|
||||||
|
std::size_t bytes_transferred, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(
|
||||||
|
std::size_t size, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(read_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Stream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class Handler>
|
||||||
|
void
|
||||||
|
read_op<Stream, Streambuf, isRequest, Body, Headers, Handler>::
|
||||||
|
operator()(error_code ec, std::size_t bytes_transferred, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
auto const used =
|
||||||
|
d.p.write(d.sb.data(), ec);
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.s.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this), ec, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.sb.consume(used);
|
||||||
|
if(d.p.complete())
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.m = d.p.release();
|
||||||
|
d.s.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this), ec, 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
// read
|
||||||
|
d.state = 2;
|
||||||
|
d.s.async_read_some(d.sb.prepare(
|
||||||
|
read_size_helper(d.sb, 65536)),
|
||||||
|
std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
// got data
|
||||||
|
case 2:
|
||||||
|
{
|
||||||
|
if(ec == boost::asio::error::eof)
|
||||||
|
{
|
||||||
|
if(! d.p.started())
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Caller will see eof on next read.
|
||||||
|
ec = {};
|
||||||
|
d.p.write_eof(ec);
|
||||||
|
if(! ec)
|
||||||
|
{
|
||||||
|
assert(d.p.complete());
|
||||||
|
d.m = d.p.release();
|
||||||
|
}
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.sb.commit(bytes_transferred);
|
||||||
|
d.sb.consume(d.p.write(d.sb.data(), ec));
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(d.p.complete())
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.m = d.p.release();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class SyncReadStream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
read(SyncReadStream& stream, Streambuf& streambuf,
|
||||||
|
message<isRequest, Body, Headers>& m,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_SyncReadStream<SyncReadStream>::value,
|
||||||
|
"SyncReadStream requirements not met");
|
||||||
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
|
"Streambuf requirements not met");
|
||||||
|
static_assert(is_ReadableBody<Body>::value,
|
||||||
|
"ReadableBody requirements not met");
|
||||||
|
parser<isRequest, Body, Headers> p;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
auto used =
|
||||||
|
p.write(streambuf.data(), ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
streambuf.consume(used);
|
||||||
|
if(p.complete())
|
||||||
|
{
|
||||||
|
m = p.release();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
streambuf.commit(stream.read_some(
|
||||||
|
streambuf.prepare(read_size_helper(
|
||||||
|
streambuf, 65536)), ec));
|
||||||
|
if(ec && ec != boost::asio::error::eof)
|
||||||
|
return;
|
||||||
|
if(ec == boost::asio::error::eof)
|
||||||
|
{
|
||||||
|
if(! p.started())
|
||||||
|
return;
|
||||||
|
// Caller will see eof on next read.
|
||||||
|
ec = {};
|
||||||
|
p.write_eof(ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
assert(p.complete());
|
||||||
|
m = p.release();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class AsyncReadStream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class CompletionToken>
|
||||||
|
auto
|
||||||
|
async_read(AsyncReadStream& stream, Streambuf& streambuf,
|
||||||
|
message<isRequest, Body, Headers>& m,
|
||||||
|
CompletionToken&& token)
|
||||||
|
{
|
||||||
|
static_assert(is_AsyncReadStream<AsyncReadStream>::value,
|
||||||
|
"AsyncReadStream requirements not met");
|
||||||
|
static_assert(is_Streambuf<Streambuf>::value,
|
||||||
|
"Streambuf requirements not met");
|
||||||
|
static_assert(is_ReadableBody<Body>::value,
|
||||||
|
"ReadableBody requirements not met");
|
||||||
|
beast::async_completion<CompletionToken,
|
||||||
|
void(error_code)> completion(token);
|
||||||
|
detail::read_op<AsyncReadStream, Streambuf,
|
||||||
|
isRequest, Body, Headers, decltype(
|
||||||
|
completion.handler)>{completion.handler,
|
||||||
|
stream, streambuf, m};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
396
beast/http/impl/write.ipp
Normal file
396
beast/http/impl/write.ipp
Normal file
@@ -0,0 +1,396 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_WRITE_IPP_INCLUDED
|
||||||
|
#define BEAST_HTTP_WRITE_IPP_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/chunk_encode.h>
|
||||||
|
#include <beast/http/resume_context.h>
|
||||||
|
#include <beast/http/type_check.h>
|
||||||
|
#include <beast/http/detail/writes.h>
|
||||||
|
#include <beast/http/detail/write_preparation.h>
|
||||||
|
#include <beast/asio/append_buffers.h>
|
||||||
|
#include <beast/asio/async_completion.h>
|
||||||
|
#include <beast/asio/bind_handler.h>
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <beast/asio/type_check.h>
|
||||||
|
#include <boost/asio/write.hpp>
|
||||||
|
#include <boost/logic/tribool.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
#include <beast/cxx17/type_traits.h> // <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<class Stream, class Handler,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
class write_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
Stream& s;
|
||||||
|
// VFALCO How do we use handler_alloc in write_preparation?
|
||||||
|
write_preparation<
|
||||||
|
isRequest, Body, Headers> wp;
|
||||||
|
Handler h;
|
||||||
|
resume_context resume;
|
||||||
|
resume_context copy;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, Stream& s_,
|
||||||
|
message<isRequest, Body, Headers> const& m_)
|
||||||
|
: s(s_)
|
||||||
|
, wp(m_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
write_op(write_op&&) = default;
|
||||||
|
write_op(write_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
write_op(DeducedHandler&& h, Stream& s, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), s,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.resume = {
|
||||||
|
[self = *this]() mutable
|
||||||
|
{
|
||||||
|
self.d_->cont = false;
|
||||||
|
auto& ios = self.d_->s.get_io_service();
|
||||||
|
ios.dispatch(bind_handler(std::move(self),
|
||||||
|
error_code{}, 0, false));
|
||||||
|
}};
|
||||||
|
d.copy = d.resume;
|
||||||
|
(*this)(error_code{}, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit
|
||||||
|
write_op(std::shared_ptr<data> d)
|
||||||
|
: d_(std::move(d))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()(error_code ec,
|
||||||
|
std::size_t bytes_transferred, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(
|
||||||
|
std::size_t size, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(write_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Stream, class Handler,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
write_op<Stream, Handler, isRequest, Body, Headers>::
|
||||||
|
operator()(error_code ec, std::size_t, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
{
|
||||||
|
d.wp.init(ec);
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.s.get_io_service().post(bind_handler(
|
||||||
|
std::move(*this), ec, 0, false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
d.state = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
{
|
||||||
|
auto const result = d.wp.w(std::move(d.copy), ec,
|
||||||
|
[&](auto const& buffers)
|
||||||
|
{
|
||||||
|
// write headers and body
|
||||||
|
if(d.wp.chunked)
|
||||||
|
boost::asio::async_write(d.s,
|
||||||
|
append_buffers(d.wp.sb.data(),
|
||||||
|
chunk_encode(buffers)),
|
||||||
|
std::move(*this));
|
||||||
|
else
|
||||||
|
boost::asio::async_write(d.s,
|
||||||
|
append_buffers(d.wp.sb.data(),
|
||||||
|
buffers), std::move(*this));
|
||||||
|
});
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
d.s.get_io_service().post(bind_handler(
|
||||||
|
std::move(*this), ec, false));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
// suspend
|
||||||
|
d.copy = d.resume;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(result)
|
||||||
|
d.state = d.wp.chunked ? 4 : 5;
|
||||||
|
else
|
||||||
|
d.state = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sent headers and body
|
||||||
|
case 2:
|
||||||
|
d.wp.sb.consume(d.wp.sb.size());
|
||||||
|
d.state = 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
{
|
||||||
|
auto const result = d.wp.w(std::move(d.copy), ec,
|
||||||
|
[&](auto const& buffers)
|
||||||
|
{
|
||||||
|
// write body
|
||||||
|
if(d.wp.chunked)
|
||||||
|
boost::asio::async_write(d.s,
|
||||||
|
chunk_encode(buffers),
|
||||||
|
std::move(*this));
|
||||||
|
else
|
||||||
|
boost::asio::async_write(d.s,
|
||||||
|
buffers, std::move(*this));
|
||||||
|
});
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
// call handler
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
// suspend
|
||||||
|
d.copy = d.resume;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(result)
|
||||||
|
d.state = d.wp.chunked ? 4 : 5;
|
||||||
|
else
|
||||||
|
d.state = 2;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
// VFALCO Unfortunately the current interface to the
|
||||||
|
// Writer concept prevents us from using coalescing the
|
||||||
|
// final body chunk with the final chunk delimiter.
|
||||||
|
//
|
||||||
|
// write final chunk
|
||||||
|
d.state = 5;
|
||||||
|
boost::asio::async_write(d.s,
|
||||||
|
chunk_encode_final(), std::move(*this));
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
if(d.wp.close)
|
||||||
|
{
|
||||||
|
// VFALCO TODO Decide on an error code
|
||||||
|
ec = boost::asio::error::eof;
|
||||||
|
}
|
||||||
|
d.state = 99;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
d.resume = {};
|
||||||
|
d.copy = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class SyncWriteStream,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
write(SyncWriteStream& stream,
|
||||||
|
message<isRequest, Body, Headers> const& msg,
|
||||||
|
boost::system::error_code& ec)
|
||||||
|
{
|
||||||
|
static_assert(is_WritableBody<Body>::value,
|
||||||
|
"WritableBody requirements not met");
|
||||||
|
detail::write_preparation<isRequest, Body, Headers> wp(msg);
|
||||||
|
wp.init(ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
std::mutex m;
|
||||||
|
std::condition_variable cv;
|
||||||
|
bool ready = false;
|
||||||
|
resume_context resume{
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
ready = true;
|
||||||
|
cv.notify_one();
|
||||||
|
}};
|
||||||
|
auto copy = resume;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto result = wp.w(std::move(copy), ec,
|
||||||
|
[&](auto const& buffers)
|
||||||
|
{
|
||||||
|
// write headers and body
|
||||||
|
if(wp.chunked)
|
||||||
|
boost::asio::write(stream, append_buffers(
|
||||||
|
wp.sb.data(), chunk_encode(buffers)), ec);
|
||||||
|
else
|
||||||
|
boost::asio::write(stream, append_buffers(
|
||||||
|
wp.sb.data(), buffers), ec);
|
||||||
|
});
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
if(result)
|
||||||
|
break;
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
boost::asio::write(stream, wp.sb.data(), ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
wp.sb.consume(wp.sb.size());
|
||||||
|
copy = resume;
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return ready; });
|
||||||
|
ready = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wp.sb.consume(wp.sb.size());
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
auto result = wp.w(std::move(copy), ec,
|
||||||
|
[&](auto const& buffers)
|
||||||
|
{
|
||||||
|
// write body
|
||||||
|
if(wp.chunked)
|
||||||
|
boost::asio::write(stream,
|
||||||
|
chunk_encode(buffers), ec);
|
||||||
|
else
|
||||||
|
boost::asio::write(stream, buffers, ec);
|
||||||
|
});
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
if(result)
|
||||||
|
break;
|
||||||
|
if(boost::indeterminate(result))
|
||||||
|
{
|
||||||
|
copy = resume;
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return ready; });
|
||||||
|
ready = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(wp.chunked)
|
||||||
|
{
|
||||||
|
// VFALCO Unfortunately the current interface to the
|
||||||
|
// Writer concept prevents us from using coalescing the
|
||||||
|
// final body chunk with the final chunk delimiter.
|
||||||
|
//
|
||||||
|
// write final chunk
|
||||||
|
boost::asio::write(stream, chunk_encode_final(), ec);
|
||||||
|
if(ec)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(wp.close)
|
||||||
|
{
|
||||||
|
// VFALCO TODO Decide on an error code
|
||||||
|
ec = boost::asio::error::eof;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class AsyncWriteStream,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class CompletionToken>
|
||||||
|
auto
|
||||||
|
async_write(AsyncWriteStream& stream,
|
||||||
|
message<isRequest, Body, Headers> const& msg,
|
||||||
|
CompletionToken&& token)
|
||||||
|
{
|
||||||
|
static_assert(
|
||||||
|
is_AsyncWriteStream<AsyncWriteStream>::value,
|
||||||
|
"AsyncWriteStream requirements not met");
|
||||||
|
static_assert(is_WritableBody<Body>::value,
|
||||||
|
"WritableBody requirements not met");
|
||||||
|
beast::async_completion<CompletionToken,
|
||||||
|
void(error_code)> completion(token);
|
||||||
|
detail::write_op<AsyncWriteStream, decltype(completion.handler),
|
||||||
|
isRequest, Body, Headers>{completion.handler, stream, msg};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -20,12 +20,171 @@
|
|||||||
#ifndef BEAST_HTTP_MESSAGE_H_INCLUDED
|
#ifndef BEAST_HTTP_MESSAGE_H_INCLUDED
|
||||||
#define BEAST_HTTP_MESSAGE_H_INCLUDED
|
#define BEAST_HTTP_MESSAGE_H_INCLUDED
|
||||||
|
|
||||||
#include <beast/http/basic_parser.h>
|
#include <beast/http/headers.h>
|
||||||
|
#include <beast/http/method.h>
|
||||||
|
#include <beast/asio/buffers_debug.h>
|
||||||
|
#include <beast/asio/type_check.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct request_fields
|
||||||
|
{
|
||||||
|
http::method_t method;
|
||||||
|
std::string url;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct response_fields
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
std::string reason;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
struct request_params
|
||||||
|
{
|
||||||
|
http::method_t method;
|
||||||
|
std::string url;
|
||||||
|
int version;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct response_params
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
std::string reason;
|
||||||
|
int version;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** A HTTP message.
|
||||||
|
|
||||||
|
A message can be a request or response, depending on the `isRequest`
|
||||||
|
template argument value. Requests and responses have different types,
|
||||||
|
so functions may be overloaded on them if desired.
|
||||||
|
|
||||||
|
The `Body` template argument type determines the model used
|
||||||
|
to read or write the content body of the message.
|
||||||
|
|
||||||
|
@tparam isRequest `true` if this is a request.
|
||||||
|
|
||||||
|
@tparam Body A type meeting the requirements of Body.
|
||||||
|
|
||||||
|
@tparam Headers A type meeting the requirements of Headers.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
struct message
|
||||||
|
: std::conditional_t<isRequest,
|
||||||
|
detail::request_fields, detail::response_fields>
|
||||||
|
{
|
||||||
|
/** The trait type characterizing the body.
|
||||||
|
|
||||||
|
The body member will be of type body_type::value_type.
|
||||||
|
*/
|
||||||
|
using body_type = Body;
|
||||||
|
using headers_type = Headers;
|
||||||
|
|
||||||
|
using is_request =
|
||||||
|
std::integral_constant<bool, isRequest>;
|
||||||
|
|
||||||
|
int version; // 10 or 11
|
||||||
|
headers_type headers;
|
||||||
|
typename Body::value_type body;
|
||||||
|
|
||||||
|
message();
|
||||||
|
message(message&&) = default;
|
||||||
|
message(message const&) = default;
|
||||||
|
message& operator=(message&&) = default;
|
||||||
|
message& operator=(message const&) = default;
|
||||||
|
|
||||||
|
/** Construct a HTTP request.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
message(request_params params);
|
||||||
|
|
||||||
|
/** Construct a HTTP response.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
message(response_params params);
|
||||||
|
|
||||||
|
/// Serialize the request or response line to a Streambuf.
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write_firstline(Streambuf& streambuf) const
|
||||||
|
{
|
||||||
|
write_firstline(streambuf,
|
||||||
|
std::integral_constant<bool, isRequest>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Diagnostics only
|
||||||
|
template<bool, class, class>
|
||||||
|
friend
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os,
|
||||||
|
message const& m);
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write_firstline(Streambuf& streambuf,
|
||||||
|
std::true_type) const;
|
||||||
|
|
||||||
|
template<class Streambuf>
|
||||||
|
void
|
||||||
|
write_firstline(Streambuf& streambuf,
|
||||||
|
std::false_type) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if ! GENERATING_DOCS
|
||||||
|
|
||||||
|
/// A typical HTTP request
|
||||||
|
template<class Body,
|
||||||
|
class Headers = basic_headers<std::allocator<char>>>
|
||||||
|
using request = message<true, Body, Headers>;
|
||||||
|
|
||||||
|
/// A typical HTTP response
|
||||||
|
template<class Body,
|
||||||
|
class Headers = basic_headers<std::allocator<char>>>
|
||||||
|
using response = message<false, Body, Headers>;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// For diagnostic output only
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
std::ostream&
|
||||||
|
operator<<(std::ostream& os,
|
||||||
|
message<isRequest, Body, Headers> const& m);
|
||||||
|
|
||||||
|
/// Write a FieldSequence to a Streambuf.
|
||||||
|
template<class Streambuf, class FieldSequence>
|
||||||
|
void
|
||||||
|
write_fields(Streambuf& streambuf, FieldSequence const& fields);
|
||||||
|
|
||||||
|
/// Returns `true` if a message indicates a keep alive
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
bool
|
||||||
|
is_keep_alive(message<isRequest, Body, Headers> const& msg);
|
||||||
|
|
||||||
|
/// Returns `true` if a message indicates a HTTP Upgrade request or response
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
bool
|
||||||
|
is_upgrade(message<isRequest, Body, Headers> const& msg);
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/message.ipp>
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
#include <beast/http/method.h>
|
#include <beast/http/method.h>
|
||||||
#include <beast/http/headers.h>
|
#include <beast/http/headers.h>
|
||||||
#include <beast/ci_char_traits.h>
|
#include <beast/ci_char_traits.h>
|
||||||
#include <boost/intrusive/list.hpp>
|
#include <beast/http/detail/writes.h>
|
||||||
#include <boost/intrusive/set.hpp>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
@@ -34,7 +193,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace deprecated_http {
|
||||||
|
|
||||||
inline
|
inline
|
||||||
std::pair<int, int>
|
std::pair<int, int>
|
||||||
@@ -70,17 +229,16 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
~message() = default;
|
~message() = default;
|
||||||
message (message const&) = delete;
|
message (message const&) = default;
|
||||||
message& operator= (message const&) = delete;
|
message (message&& other) = default;
|
||||||
|
message& operator= (message const&) = default;
|
||||||
|
message& operator= (message&& other) = default;
|
||||||
|
|
||||||
template <class = void>
|
template <class = void>
|
||||||
message();
|
message();
|
||||||
|
|
||||||
message (message&& other) = default;
|
|
||||||
message& operator= (message&& other) = default;
|
|
||||||
|
|
||||||
// Memberspace
|
// Memberspace
|
||||||
beast::http::headers headers;
|
beast::http::headers<std::allocator<char>> headers;
|
||||||
|
|
||||||
bool
|
bool
|
||||||
request() const
|
request() const
|
||||||
@@ -221,49 +379,31 @@ write (Streambuf& stream, message const& m)
|
|||||||
{
|
{
|
||||||
if (m.request())
|
if (m.request())
|
||||||
{
|
{
|
||||||
write (stream, to_string(m.method()));
|
http::detail::write (stream, to_string(m.method()));
|
||||||
write (stream, " ");
|
http::detail::write (stream, " ");
|
||||||
write (stream, m.url());
|
http::detail::write (stream, m.url());
|
||||||
write (stream, " HTTP/");
|
http::detail::write (stream, " HTTP/");
|
||||||
write (stream, std::to_string(m.version().first));
|
http::detail::write (stream, std::to_string(m.version().first));
|
||||||
write (stream, ".");
|
http::detail::write (stream, ".");
|
||||||
write (stream, std::to_string(m.version().second));
|
http::detail::write (stream, std::to_string(m.version().second));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
write (stream, "HTTP/");
|
http::detail::write (stream, "HTTP/");
|
||||||
write (stream, std::to_string(m.version().first));
|
http::detail::write (stream, std::to_string(m.version().first));
|
||||||
write (stream, ".");
|
http::detail::write (stream, ".");
|
||||||
write (stream, std::to_string(m.version().second));
|
http::detail::write (stream, std::to_string(m.version().second));
|
||||||
write (stream, " ");
|
http::detail::write (stream, " ");
|
||||||
write (stream, std::to_string(m.status()));
|
http::detail::write (stream, std::to_string(m.status()));
|
||||||
write (stream, " ");
|
http::detail::write (stream, " ");
|
||||||
write (stream, m.reason());
|
http::detail::write (stream, m.reason());
|
||||||
}
|
}
|
||||||
write (stream, "\r\n");
|
http::detail::write (stream, "\r\n");
|
||||||
write(stream, m.headers);
|
write_fields(stream, m.headers);
|
||||||
write (stream, "\r\n");
|
http::detail::write (stream, "\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class = void>
|
} // deprecated_http
|
||||||
std::string
|
|
||||||
to_string (message const& m)
|
|
||||||
{
|
|
||||||
std::stringstream ss;
|
|
||||||
if (m.request())
|
|
||||||
ss << to_string(m.method()) << " " << m.url() << " HTTP/" <<
|
|
||||||
std::to_string(m.version().first) << "." <<
|
|
||||||
std::to_string(m.version().second) << "\r\n";
|
|
||||||
else
|
|
||||||
ss << "HTTP/" << std::to_string(m.version().first) << "." <<
|
|
||||||
std::to_string(m.version().second) << " " <<
|
|
||||||
std::to_string(m.status()) << " " << m.reason() << "\r\n";
|
|
||||||
ss << to_string(m.headers);
|
|
||||||
ss << "\r\n";
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // http
|
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -78,8 +78,51 @@ enum class method_t
|
|||||||
http_unlink
|
http_unlink
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class = void>
|
||||||
std::string
|
std::string
|
||||||
to_string (method_t m);
|
to_string(method_t m)
|
||||||
|
{
|
||||||
|
switch(m)
|
||||||
|
{
|
||||||
|
case method_t::http_delete: return "DELETE";
|
||||||
|
case method_t::http_get: return "GET";
|
||||||
|
case method_t::http_head: return "HEAD";
|
||||||
|
case method_t::http_post: return "POST";
|
||||||
|
case method_t::http_put: return "PUT";
|
||||||
|
|
||||||
|
case method_t::http_connect: return "CONNECT";
|
||||||
|
case method_t::http_options: return "OPTIONS";
|
||||||
|
case method_t::http_trace: return "TRACE";
|
||||||
|
|
||||||
|
case method_t::http_copy: return "COPY";
|
||||||
|
case method_t::http_lock: return "LOCK";
|
||||||
|
case method_t::http_mkcol: return "MKCOL";
|
||||||
|
case method_t::http_move: return "MOVE";
|
||||||
|
case method_t::http_propfind: return "PROPFIND";
|
||||||
|
case method_t::http_proppatch: return "PROPPATCH";
|
||||||
|
case method_t::http_search: return "SEARCH";
|
||||||
|
case method_t::http_unlock: return "UNLOCK";
|
||||||
|
|
||||||
|
case method_t::http_report: return "REPORT";
|
||||||
|
case method_t::http_mkactivity: return "MKACTIVITY";
|
||||||
|
case method_t::http_checkout: return "CHECKOUT";
|
||||||
|
case method_t::http_merge: return "MERGE";
|
||||||
|
|
||||||
|
case method_t::http_msearch: return "MSEARCH";
|
||||||
|
case method_t::http_notify: return "NOTIFY";
|
||||||
|
case method_t::http_subscribe: return "SUBSCRIBE";
|
||||||
|
case method_t::http_unsubscribe: return "UNSUBSCRIBE";
|
||||||
|
|
||||||
|
case method_t::http_patch: return "PATCH";
|
||||||
|
case method_t::http_purge: return "PURGE";
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
return "GET";
|
||||||
|
}
|
||||||
|
|
||||||
template <class Stream>
|
template <class Stream>
|
||||||
Stream&
|
Stream&
|
||||||
@@ -89,10 +132,60 @@ operator<< (Stream& s, method_t m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the string corresponding to the numeric HTTP status code. */
|
/** Returns the string corresponding to the numeric HTTP status code. */
|
||||||
|
template<class = void>
|
||||||
std::string
|
std::string
|
||||||
status_text (int status);
|
status_text (int status)
|
||||||
|
{
|
||||||
|
switch(status)
|
||||||
|
{
|
||||||
|
case 100: return "Continue";
|
||||||
|
case 101: return "Switching Protocols";
|
||||||
|
case 200: return "OK";
|
||||||
|
case 201: return "Created";
|
||||||
|
case 202: return "Accepted";
|
||||||
|
case 203: return "Non-Authoritative Information";
|
||||||
|
case 204: return "No Content";
|
||||||
|
case 205: return "Reset Content";
|
||||||
|
case 206: return "Partial Content";
|
||||||
|
case 300: return "Multiple Choices";
|
||||||
|
case 301: return "Moved Permanently";
|
||||||
|
case 302: return "Found";
|
||||||
|
case 303: return "See Other";
|
||||||
|
case 304: return "Not Modified";
|
||||||
|
case 305: return "Use Proxy";
|
||||||
|
//case 306: return "<reserved>";
|
||||||
|
case 307: return "Temporary Redirect";
|
||||||
|
case 400: return "Bad Request";
|
||||||
|
case 401: return "Unauthorized";
|
||||||
|
case 402: return "Payment Required";
|
||||||
|
case 403: return "Forbidden";
|
||||||
|
case 404: return "Not Found";
|
||||||
|
case 405: return "Method Not Allowed";
|
||||||
|
case 406: return "Not Acceptable";
|
||||||
|
case 407: return "Proxy Authentication Required";
|
||||||
|
case 408: return "Request Timeout";
|
||||||
|
case 409: return "Conflict";
|
||||||
|
case 410: return "Gone";
|
||||||
|
case 411: return "Length Required";
|
||||||
|
case 412: return "Precondition Failed";
|
||||||
|
case 413: return "Request Entity Too Large";
|
||||||
|
case 414: return "Request-URI Too Long";
|
||||||
|
case 415: return "Unsupported Media Type";
|
||||||
|
case 416: return "Requested Range Not Satisfiable";
|
||||||
|
case 417: return "Expectation Failed";
|
||||||
|
case 500: return "Internal Server Error";
|
||||||
|
case 501: return "Not Implemented";
|
||||||
|
case 502: return "Bad Gateway";
|
||||||
|
case 503: return "Service Unavailable";
|
||||||
|
case 504: return "Gateway Timeout";
|
||||||
|
case 505: return "HTTP Version Not Supported";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "Unknown HTTP status";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
} // http
|
||||||
}
|
} // beast
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -20,6 +20,157 @@
|
|||||||
#ifndef BEAST_HTTP_PARSER_H_INCLUDED
|
#ifndef BEAST_HTTP_PARSER_H_INCLUDED
|
||||||
#define BEAST_HTTP_PARSER_H_INCLUDED
|
#define BEAST_HTTP_PARSER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/basic_parser.h>
|
||||||
|
#include <beast/http/error.h>
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <beast/cxx17/type_traits.h> // <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** A HTTP parser.
|
||||||
|
|
||||||
|
The parser may only be used once.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
class parser
|
||||||
|
: public basic_parser<parser<isRequest, Body, Headers>>
|
||||||
|
{
|
||||||
|
using message_type =
|
||||||
|
message<isRequest, Body, Headers>;
|
||||||
|
|
||||||
|
message_type m_;
|
||||||
|
typename message_type::body_type::reader r_;
|
||||||
|
bool started_ = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
parser(parser&&) = default;
|
||||||
|
|
||||||
|
parser()
|
||||||
|
: http::basic_parser<parser>(isRequest)
|
||||||
|
, r_(m_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if at least one byte has been processed
|
||||||
|
bool
|
||||||
|
started()
|
||||||
|
{
|
||||||
|
return started_;
|
||||||
|
}
|
||||||
|
|
||||||
|
message_type
|
||||||
|
release()
|
||||||
|
{
|
||||||
|
return std::move(m_);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class http::basic_parser<parser>;
|
||||||
|
|
||||||
|
void
|
||||||
|
on_start()
|
||||||
|
{
|
||||||
|
started_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_field(std::string const& field, std::string const& value)
|
||||||
|
{
|
||||||
|
m_.headers.insert(field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_headers_complete(error_code&)
|
||||||
|
{
|
||||||
|
// vFALCO TODO Decode the Content-Length and
|
||||||
|
// Transfer-Encoding, see if we can reserve the buffer.
|
||||||
|
//
|
||||||
|
// r_.reserve(content_length)
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
on_request(http::method_t method, std::string const& url,
|
||||||
|
int major, int minor, bool keep_alive, bool upgrade,
|
||||||
|
std::true_type)
|
||||||
|
{
|
||||||
|
m_.method = method;
|
||||||
|
m_.url = url;
|
||||||
|
m_.version = major * 10 + minor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
on_request(http::method_t, std::string const&,
|
||||||
|
int, int, bool, bool,
|
||||||
|
std::false_type)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
on_request(http::method_t method, std::string const& url,
|
||||||
|
int major, int minor, bool keep_alive, bool upgrade)
|
||||||
|
{
|
||||||
|
return on_request(method, url,
|
||||||
|
major, minor, keep_alive, upgrade,
|
||||||
|
typename message_type::is_request{});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
on_response(int status, std::string const& reason,
|
||||||
|
int major, int minor, bool keep_alive, bool upgrade,
|
||||||
|
std::true_type)
|
||||||
|
{
|
||||||
|
m_.status = status;
|
||||||
|
m_.reason = reason;
|
||||||
|
m_.version = major * 10 + minor;
|
||||||
|
// VFALCO TODO return expect_body_
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
on_response(int, std::string const&, int, int, bool, bool,
|
||||||
|
std::false_type)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
on_response(int status, std::string const& reason,
|
||||||
|
int major, int minor, bool keep_alive, bool upgrade)
|
||||||
|
{
|
||||||
|
return on_response(
|
||||||
|
status, reason, major, minor, keep_alive, upgrade,
|
||||||
|
std::integral_constant<bool, ! message_type::is_request::value>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_body(void const* data,
|
||||||
|
std::size_t size, error_code& ec)
|
||||||
|
{
|
||||||
|
r_.write(data, size, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_complete()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
//
|
||||||
|
// LEGACY
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <beast/http/basic_parser.h>
|
||||||
#include <beast/http/message.h>
|
#include <beast/http/message.h>
|
||||||
#include <beast/http/body.h>
|
#include <beast/http/body.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
@@ -27,167 +178,105 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace deprecated_http {
|
||||||
|
|
||||||
/** Parser for HTTP messages.
|
/** Parser for HTTP messages.
|
||||||
The result is stored in a message object.
|
The result is stored in a message object.
|
||||||
*/
|
*/
|
||||||
class parser : public beast::http::basic_parser
|
class parser
|
||||||
|
: public beast::http::basic_parser<parser>
|
||||||
{
|
{
|
||||||
private:
|
// friend class basic_parser<parser>;
|
||||||
std::reference_wrapper <message> message_;
|
|
||||||
|
message& m_;
|
||||||
std::function<void(void const*, std::size_t)> write_body_;
|
std::function<void(void const*, std::size_t)> write_body_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
parser(parser&&) = default;
|
||||||
|
parser(parser const&) = delete;
|
||||||
|
parser& operator=(parser&&) = delete;
|
||||||
|
parser& operator=(parser const&) = delete;
|
||||||
|
|
||||||
/** Construct a parser for HTTP request or response.
|
/** Construct a parser for HTTP request or response.
|
||||||
The headers plus request or status line are stored in message.
|
The headers plus request or status line are stored in message.
|
||||||
The content-body, if any, is passed as a series of calls to
|
The content-body, if any, is passed as a series of calls to
|
||||||
the write_body function. Transfer encodings are applied before
|
the write_body function. Transfer encodings are applied before
|
||||||
any data is passed to the write_body function.
|
any data is passed to the write_body function.
|
||||||
*/
|
*/
|
||||||
parser (std::function<void(void const*, std::size_t)> write_body,
|
parser(std::function<void(void const*, std::size_t)> write_body,
|
||||||
message& m, bool request)
|
message& m, bool request)
|
||||||
: beast::http::basic_parser (request)
|
: basic_parser(request)
|
||||||
, message_(m)
|
, m_(m)
|
||||||
, write_body_(std::move(write_body))
|
, write_body_(std::move(write_body))
|
||||||
{
|
{
|
||||||
message_.get().request(request);
|
m_.request(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
parser (message& m, body& b, bool request)
|
parser(message& m, body& b, bool request)
|
||||||
: beast::http::basic_parser (request)
|
: basic_parser(request)
|
||||||
, message_(m)
|
, m_(m)
|
||||||
{
|
{
|
||||||
write_body_ = [&b](void const* data, std::size_t size)
|
write_body_ = [&b](void const* data, std::size_t size)
|
||||||
{
|
{
|
||||||
b.write(data, size);
|
b.write(data, size);
|
||||||
};
|
};
|
||||||
|
m_.request(request);
|
||||||
message_.get().request(request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parser& operator= (parser&& other) = default;
|
//private:
|
||||||
|
|
||||||
private:
|
|
||||||
template <class = void>
|
|
||||||
void
|
void
|
||||||
do_start ();
|
on_start()
|
||||||
|
{
|
||||||
template <class = void>
|
}
|
||||||
bool
|
|
||||||
do_request (method_t method, std::string const& url,
|
void
|
||||||
int major, int minor, bool keep_alive, bool upgrade);
|
on_headers_complete(error_code&)
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
bool
|
|
||||||
do_response (int status, std::string const& text,
|
|
||||||
int major, int minor, bool keep_alive, bool upgrade);
|
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
void
|
|
||||||
do_field (std::string const& field, std::string const& value);
|
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
void
|
|
||||||
do_body (void const* data, std::size_t bytes);
|
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
void
|
|
||||||
do_complete();
|
|
||||||
|
|
||||||
void
|
|
||||||
on_start () override
|
|
||||||
{
|
{
|
||||||
do_start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
on_request (method_t method, std::string const& url,
|
on_request(http::method_t method, std::string const& url,
|
||||||
int major, int minor, bool keep_alive, bool upgrade) override
|
int major, int minor, bool keep_alive, bool upgrade)
|
||||||
{
|
{
|
||||||
return do_request (method, url, major, minor, keep_alive, upgrade);
|
m_.method(method);
|
||||||
|
m_.url(url);
|
||||||
|
m_.version(major, minor);
|
||||||
|
m_.keep_alive(keep_alive);
|
||||||
|
m_.upgrade(upgrade);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
on_response (int status, std::string const& text,
|
on_response(int status, std::string const& text,
|
||||||
int major, int minor, bool keep_alive, bool upgrade) override
|
int major, int minor, bool keep_alive, bool upgrade)
|
||||||
{
|
{
|
||||||
return do_response (status, text, major, minor, keep_alive, upgrade);
|
m_.status(status);
|
||||||
|
m_.reason(text);
|
||||||
|
m_.version(major, minor);
|
||||||
|
m_.keep_alive(keep_alive);
|
||||||
|
m_.upgrade(upgrade);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
on_field (std::string const& field, std::string const& value) override
|
on_field(std::string const& field, std::string const& value)
|
||||||
{
|
{
|
||||||
do_field (field, value);
|
m_.headers.insert(field, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
on_body (void const* data, std::size_t bytes) override
|
on_body(void const* data, std::size_t bytes, error_code&)
|
||||||
{
|
{
|
||||||
do_body (data, bytes);
|
write_body_(data, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
on_complete() override
|
on_complete()
|
||||||
{
|
{
|
||||||
do_complete();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
} // deprecated_http
|
||||||
template <class>
|
|
||||||
void
|
|
||||||
parser::do_start()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
bool
|
|
||||||
parser::do_request (method_t method, std::string const& url,
|
|
||||||
int major, int minor, bool keep_alive, bool upgrade)
|
|
||||||
{
|
|
||||||
message_.get().method (method);
|
|
||||||
message_.get().url (url);
|
|
||||||
message_.get().version (major, minor);
|
|
||||||
message_.get().keep_alive (keep_alive);
|
|
||||||
message_.get().upgrade (upgrade);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
bool
|
|
||||||
parser::do_response (int status, std::string const& text,
|
|
||||||
int major, int minor, bool keep_alive, bool upgrade)
|
|
||||||
{
|
|
||||||
message_.get().status (status);
|
|
||||||
message_.get().reason (text);
|
|
||||||
message_.get().version (major, minor);
|
|
||||||
message_.get().keep_alive (keep_alive);
|
|
||||||
message_.get().upgrade (upgrade);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
void
|
|
||||||
parser::do_field (std::string const& field, std::string const& value)
|
|
||||||
{
|
|
||||||
message_.get().headers.append (field, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
void
|
|
||||||
parser::do_body (void const* data, std::size_t bytes)
|
|
||||||
{
|
|
||||||
write_body_(data, bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class>
|
|
||||||
void
|
|
||||||
parser::do_complete()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
} // http
|
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
119
beast/http/read.h
Normal file
119
beast/http/read.h
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_READ_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_READ_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/error.h>
|
||||||
|
#include <beast/http/parser.h>
|
||||||
|
#include <beast/http/type_check.h>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** Read a HTTP message from a stream.
|
||||||
|
|
||||||
|
@param stream The stream to read the message from.
|
||||||
|
|
||||||
|
@param streambuf A Streambuf used to hold unread bytes. The
|
||||||
|
implementation may read past the end of the message. The extra
|
||||||
|
bytes are stored here, to be presented in a subsequent call to
|
||||||
|
read.
|
||||||
|
|
||||||
|
@param msg An object used to store the read message. Any
|
||||||
|
contents will be overwritten.
|
||||||
|
|
||||||
|
@throws boost::system::system_error on failure.
|
||||||
|
*/
|
||||||
|
template<class SyncReadStream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
read(SyncReadStream& stream, Streambuf& streambuf,
|
||||||
|
message<isRequest, Body, Headers>& msg)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
read(stream, streambuf, msg, ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read a HTTP message from a stream.
|
||||||
|
|
||||||
|
@param stream The stream to read the message from.
|
||||||
|
|
||||||
|
@param streambuf A Streambuf used to hold unread bytes. The
|
||||||
|
implementation may read past the end of the message. The extra
|
||||||
|
bytes are stored here, to be presented in a subsequent call to
|
||||||
|
read.
|
||||||
|
|
||||||
|
@param msg An object used to store the read message. Any
|
||||||
|
contents will be overwritten.
|
||||||
|
|
||||||
|
@param ec Set to the error, if any occurred.
|
||||||
|
*/
|
||||||
|
template<class SyncReadStream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
read(SyncReadStream& stream, Streambuf& streambuf,
|
||||||
|
message<isRequest, Body, Headers>& msg,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start reading a HTTP message from a stream asynchronously.
|
||||||
|
|
||||||
|
@param stream The stream to read the message from.
|
||||||
|
|
||||||
|
@param streambuf A Streambuf used to hold unread bytes. The
|
||||||
|
implementation may read past the end of the message. The extra
|
||||||
|
bytes are stored here, to be presented in a subsequent call to
|
||||||
|
async_read.
|
||||||
|
|
||||||
|
@param msg An object used to store the read message. Any
|
||||||
|
contents will be overwritten.
|
||||||
|
|
||||||
|
@param token The handler to be called when the request completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent
|
||||||
|
function signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
error_code const& error // result of operation
|
||||||
|
); @endcode
|
||||||
|
Regardless of whether the asynchronous operation completes
|
||||||
|
immediately or not, the handler will not be invoked from within
|
||||||
|
this function. Invocation of the handler will be performed in a
|
||||||
|
manner equivalent to using boost::asio::io_service::post().
|
||||||
|
*/
|
||||||
|
template<class AsyncReadStream, class Streambuf,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class CompletionToken>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
void_or_deduced
|
||||||
|
#else
|
||||||
|
auto
|
||||||
|
#endif
|
||||||
|
async_read(AsyncReadStream& stream, Streambuf& streambuf,
|
||||||
|
message<isRequest, Body, Headers>& msg,
|
||||||
|
CompletionToken&& token);
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/read.ipp>
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,75 +1,32 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/*
|
/*
|
||||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
Copyright 2013: Vinnie Falco <vinnie.falco@gmail.com>
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
Permission to use: copy: modify: and/or distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
purpose with or without fee is hereby granted: provided that the above
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
copyright notice and this permission notice appear in all copies.
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
ANY SPECIAL : DIRECT: INDIRECT: OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE: DATA OR PROFITS: WHETHER IN AN
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
ACTION OF CONTRACT: NEGLIGENCE OR OTHER TORTIOUS ACTION: ARISING OUT OF
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <beast/http/method.h>
|
#ifndef BEAST_HTTP_REASON_H_INCLUDED
|
||||||
#include <cassert>
|
#define BEAST_HTTP_REASON_H_INCLUDED
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
std::string
|
/** Returns the text for a known status code integer. */
|
||||||
to_string (method_t m)
|
template<class = void>
|
||||||
{
|
char const*
|
||||||
switch(m)
|
reason_string(int status)
|
||||||
{
|
|
||||||
case method_t::http_delete: return "DELETE";
|
|
||||||
case method_t::http_get: return "GET";
|
|
||||||
case method_t::http_head: return "HEAD";
|
|
||||||
case method_t::http_post: return "POST";
|
|
||||||
case method_t::http_put: return "PUT";
|
|
||||||
|
|
||||||
case method_t::http_connect: return "CONNECT";
|
|
||||||
case method_t::http_options: return "OPTIONS";
|
|
||||||
case method_t::http_trace: return "TRACE";
|
|
||||||
|
|
||||||
case method_t::http_copy: return "COPY";
|
|
||||||
case method_t::http_lock: return "LOCK";
|
|
||||||
case method_t::http_mkcol: return "MKCOL";
|
|
||||||
case method_t::http_move: return "MOVE";
|
|
||||||
case method_t::http_propfind: return "PROPFIND";
|
|
||||||
case method_t::http_proppatch: return "PROPPATCH";
|
|
||||||
case method_t::http_search: return "SEARCH";
|
|
||||||
case method_t::http_unlock: return "UNLOCK";
|
|
||||||
|
|
||||||
case method_t::http_report: return "REPORT";
|
|
||||||
case method_t::http_mkactivity: return "MKACTIVITY";
|
|
||||||
case method_t::http_checkout: return "CHECKOUT";
|
|
||||||
case method_t::http_merge: return "MERGE";
|
|
||||||
|
|
||||||
case method_t::http_msearch: return "MSEARCH";
|
|
||||||
case method_t::http_notify: return "NOTIFY";
|
|
||||||
case method_t::http_subscribe: return "SUBSCRIBE";
|
|
||||||
case method_t::http_unsubscribe: return "UNSUBSCRIBE";
|
|
||||||
|
|
||||||
case method_t::http_patch: return "PATCH";
|
|
||||||
case method_t::http_purge: return "PURGE";
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
return "GET";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
status_text (int status)
|
|
||||||
{
|
{
|
||||||
switch(status)
|
switch(status)
|
||||||
{
|
{
|
||||||
@@ -88,7 +45,6 @@ status_text (int status)
|
|||||||
case 303: return "See Other";
|
case 303: return "See Other";
|
||||||
case 304: return "Not Modified";
|
case 304: return "Not Modified";
|
||||||
case 305: return "Use Proxy";
|
case 305: return "Use Proxy";
|
||||||
//case 306: return "<reserved>";
|
|
||||||
case 307: return "Temporary Redirect";
|
case 307: return "Temporary Redirect";
|
||||||
case 400: return "Bad Request";
|
case 400: return "Bad Request";
|
||||||
case 401: return "Unauthorized";
|
case 401: return "Unauthorized";
|
||||||
@@ -114,11 +70,15 @@ status_text (int status)
|
|||||||
case 503: return "Service Unavailable";
|
case 503: return "Service Unavailable";
|
||||||
case 504: return "Gateway Timeout";
|
case 504: return "Gateway Timeout";
|
||||||
case 505: return "HTTP Version Not Supported";
|
case 505: return "HTTP Version Not Supported";
|
||||||
|
|
||||||
|
case 306: return "<reserved>";
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return "Unknown HTTP status";
|
return "<unknown-status>";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // http
|
||||||
}
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -17,47 +17,30 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_DETAIL_HEADER_TRAITS_H_INCLUDED
|
#ifndef BEAST_HTTP_RESUME_CONTEXT_H_INCLUDED
|
||||||
#define BEAST_HTTP_DETAIL_HEADER_TRAITS_H_INCLUDED
|
#define BEAST_HTTP_RESUME_CONTEXT_H_INCLUDED
|
||||||
|
|
||||||
#include <beast/utility/ci_char_traits.h>
|
#include <functional>
|
||||||
|
|
||||||
#include <boost/utility/string_ref.hpp>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
namespace detail {
|
|
||||||
|
|
||||||
// Utilities for dealing with HTTP headers
|
/** A functor that resumes a write operation.
|
||||||
|
|
||||||
template <class Allocator = std::allocator <char>>
|
An rvalue reference to an object of this type is provided by the
|
||||||
using basic_field_string =
|
write implementation to the `writer` associated with the body of
|
||||||
std::basic_string <char, ci_char_traits, Allocator>;
|
a message being sent.
|
||||||
|
|
||||||
using field_string = basic_field_string <>;
|
If it is desired that the `writer` suspend the write operation (for
|
||||||
|
example, to wait until data is ready), it can take ownership of
|
||||||
using field_string_ref = boost::basic_string_ref <char, ci_char_traits>;
|
the resume context using a move. Then, it returns `boost::indeterminate`
|
||||||
|
to indicate that the write operation should suspend. Later, the calling
|
||||||
/** Returns `true` if two header fields are the same.
|
code invokes the resume function and the write operation continues
|
||||||
The comparison is case-insensitive.
|
from where it left off.
|
||||||
*/
|
*/
|
||||||
template <class Alloc1, class Alloc2>
|
using resume_context = std::function<void(void)>;
|
||||||
inline
|
|
||||||
bool field_eq (
|
|
||||||
std::basic_string <char, std::char_traits <char>, Alloc1> const& s1,
|
|
||||||
std::basic_string <char, std::char_traits <char>, Alloc2> const& s2)
|
|
||||||
{
|
|
||||||
return field_string_ref (s1.c_str(), s1.size()) ==
|
|
||||||
field_string_ref (s2.c_str(), s2.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the string with leading and trailing LWS removed. */
|
} // http
|
||||||
|
} // beast
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -20,10 +20,14 @@
|
|||||||
#ifndef BEAST_HTTP_RFC2616_H_INCLUDED
|
#ifndef BEAST_HTTP_RFC2616_H_INCLUDED
|
||||||
#define BEAST_HTTP_RFC2616_H_INCLUDED
|
#define BEAST_HTTP_RFC2616_H_INCLUDED
|
||||||
|
|
||||||
#include <boost/regex.hpp>
|
#include <boost/range/algorithm/equal.hpp>
|
||||||
|
#include <boost/range/iterator_range.hpp>
|
||||||
|
#include <boost/utility/string_ref.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <tuple> // for std::tie, remove ASAP
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -36,20 +40,34 @@ namespace beast {
|
|||||||
*/
|
*/
|
||||||
namespace rfc2616 {
|
namespace rfc2616 {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
struct ci_equal_pred
|
||||||
|
{
|
||||||
|
bool operator()(char c1, char c2)
|
||||||
|
{
|
||||||
|
// VFALCO TODO Use a table lookup here
|
||||||
|
return std::tolower(c1) == std::tolower(c2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
/** Returns `true` if `c` is linear white space.
|
/** Returns `true` if `c` is linear white space.
|
||||||
|
|
||||||
This excludes the CRLF sequence allowed for line continuations.
|
This excludes the CRLF sequence allowed for line continuations.
|
||||||
*/
|
*/
|
||||||
template <class CharT>
|
inline
|
||||||
bool
|
bool
|
||||||
is_lws (CharT c)
|
is_lws(char c)
|
||||||
{
|
{
|
||||||
return c == ' ' || c == '\t';
|
return c == ' ' || c == '\t';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if `c` is any whitespace character. */
|
/** Returns `true` if `c` is any whitespace character. */
|
||||||
template <class CharT>
|
inline
|
||||||
bool
|
bool
|
||||||
is_white (CharT c)
|
is_white(char c)
|
||||||
{
|
{
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
@@ -61,18 +79,19 @@ is_white (CharT c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if `c` is a control character. */
|
/** Returns `true` if `c` is a control character. */
|
||||||
template <class CharT>
|
inline
|
||||||
bool
|
bool
|
||||||
is_ctl (CharT c)
|
is_control(char c)
|
||||||
{
|
{
|
||||||
return c <= 31 || c >= 127;
|
return c <= 31 || c >= 127;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns `true` if `c` is a separator. */
|
/** Returns `true` if `c` is a separator. */
|
||||||
template <class CharT>
|
inline
|
||||||
bool
|
bool
|
||||||
is_sep (CharT c)
|
is_separator(char c)
|
||||||
{
|
{
|
||||||
|
// VFALCO Could use a static table
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case '(': case ')': case '<': case '>': case '@':
|
case '(': case ')': case '<': case '>': case '@':
|
||||||
@@ -83,12 +102,20 @@ is_sep (CharT c)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if `c` is a character. */
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
is_char(char c)
|
||||||
|
{
|
||||||
|
return c >= 0 && c <= 127;
|
||||||
|
}
|
||||||
|
|
||||||
template <class FwdIter>
|
template <class FwdIter>
|
||||||
FwdIter
|
FwdIter
|
||||||
trim_left (FwdIter first, FwdIter last)
|
trim_left (FwdIter first, FwdIter last)
|
||||||
{
|
{
|
||||||
return std::find_if_not (first, last,
|
return std::find_if_not (first, last,
|
||||||
&is_white <typename FwdIter::value_type>);
|
is_white);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class FwdIter>
|
template <class FwdIter>
|
||||||
@@ -166,7 +193,8 @@ trim (std::string const& s)
|
|||||||
*/
|
*/
|
||||||
template <class FwdIt,
|
template <class FwdIt,
|
||||||
class Result = std::vector<
|
class Result = std::vector<
|
||||||
std::basic_string<typename FwdIt::value_type>>,
|
std::basic_string<typename
|
||||||
|
std::iterator_traits<FwdIt>::value_type>>,
|
||||||
class Char>
|
class Char>
|
||||||
Result
|
Result
|
||||||
split(FwdIt first, FwdIt last, Char delim)
|
split(FwdIt first, FwdIt last, Char delim)
|
||||||
@@ -239,7 +267,8 @@ split(FwdIt first, FwdIt last, Char delim)
|
|||||||
|
|
||||||
template <class FwdIt,
|
template <class FwdIt,
|
||||||
class Result = std::vector<
|
class Result = std::vector<
|
||||||
std::basic_string<typename FwdIt::value_type>>>
|
std::basic_string<typename std::iterator_traits<
|
||||||
|
FwdIt>::value_type>>>
|
||||||
Result
|
Result
|
||||||
split_commas(FwdIt first, FwdIt last)
|
split_commas(FwdIt first, FwdIt last)
|
||||||
{
|
{
|
||||||
@@ -248,11 +277,194 @@ split_commas(FwdIt first, FwdIt last)
|
|||||||
|
|
||||||
template <class Result = std::vector<std::string>>
|
template <class Result = std::vector<std::string>>
|
||||||
Result
|
Result
|
||||||
split_commas(std::string const& s)
|
split_commas(boost::string_ref const& s)
|
||||||
{
|
{
|
||||||
return split_commas(s.begin(), s.end());
|
return split_commas(s.begin(), s.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Iterates through a comma separated list.
|
||||||
|
|
||||||
|
Meets the requirements of ForwardIterator.
|
||||||
|
|
||||||
|
List defined in rfc2616 2.1.
|
||||||
|
|
||||||
|
@note Values returned may contain backslash escapes.
|
||||||
|
*/
|
||||||
|
class list_iterator
|
||||||
|
{
|
||||||
|
using iter_type = boost::string_ref::const_iterator;
|
||||||
|
|
||||||
|
iter_type it_;
|
||||||
|
iter_type end_;
|
||||||
|
boost::string_ref value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = boost::string_ref;
|
||||||
|
using pointer = value_type const*;
|
||||||
|
using reference = value_type const&;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category =
|
||||||
|
std::forward_iterator_tag;
|
||||||
|
|
||||||
|
list_iterator(iter_type begin, iter_type end)
|
||||||
|
: it_(begin)
|
||||||
|
, end_(end)
|
||||||
|
{
|
||||||
|
if(it_ != end_)
|
||||||
|
increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator==(list_iterator const& other) const
|
||||||
|
{
|
||||||
|
return other.it_ == it_ && other.end_ == end_
|
||||||
|
&& other.value_.size() == value_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
operator!=(list_iterator const& other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
reference
|
||||||
|
operator*() const
|
||||||
|
{
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer
|
||||||
|
operator->() const
|
||||||
|
{
|
||||||
|
return &*(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_iterator&
|
||||||
|
operator++()
|
||||||
|
{
|
||||||
|
increment();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_iterator
|
||||||
|
operator++(int)
|
||||||
|
{
|
||||||
|
auto temp = *this;
|
||||||
|
++(*this);
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class = void>
|
||||||
|
void
|
||||||
|
increment();
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class>
|
||||||
|
void
|
||||||
|
list_iterator::increment()
|
||||||
|
{
|
||||||
|
value_.clear();
|
||||||
|
while(it_ != end_)
|
||||||
|
{
|
||||||
|
if(*it_ == '"')
|
||||||
|
{
|
||||||
|
// quoted-string
|
||||||
|
++it_;
|
||||||
|
if(it_ == end_)
|
||||||
|
return;
|
||||||
|
if(*it_ != '"')
|
||||||
|
{
|
||||||
|
auto start = it_;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
++it_;
|
||||||
|
if(it_ == end_)
|
||||||
|
{
|
||||||
|
value_ = boost::string_ref(
|
||||||
|
&*start, std::distance(start, it_));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(*it_ == '"')
|
||||||
|
{
|
||||||
|
value_ = boost::string_ref(
|
||||||
|
&*start, std::distance(start, it_));
|
||||||
|
++it_;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++it_;
|
||||||
|
}
|
||||||
|
else if(*it_ == ',')
|
||||||
|
{
|
||||||
|
it_++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if(is_lws(*it_))
|
||||||
|
{
|
||||||
|
++it_;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto start = it_;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
++it_;
|
||||||
|
if(it_ == end_ ||
|
||||||
|
*it_ == ',' ||
|
||||||
|
is_lws(*it_))
|
||||||
|
{
|
||||||
|
value_ = boost::string_ref(
|
||||||
|
&*start, std::distance(start, it_));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if two strings are equal.
|
||||||
|
|
||||||
|
A case-insensitive comparison is used.
|
||||||
|
*/
|
||||||
|
inline
|
||||||
|
bool
|
||||||
|
ci_equal(boost::string_ref s1, boost::string_ref s2)
|
||||||
|
{
|
||||||
|
return boost::range::equal(s1, s2,
|
||||||
|
detail::ci_equal_pred{});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a range representing the list. */
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
make_list(boost::string_ref const& field)
|
||||||
|
{
|
||||||
|
return boost::iterator_range<list_iterator>{
|
||||||
|
list_iterator{field.begin(), field.end()},
|
||||||
|
list_iterator{field.end(), field.end()}};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns true if the specified token exists in the list.
|
||||||
|
|
||||||
|
A case-insensitive comparison is used.
|
||||||
|
*/
|
||||||
|
template<class = void>
|
||||||
|
bool
|
||||||
|
token_in_list(boost::string_ref const& value,
|
||||||
|
boost::string_ref const& token)
|
||||||
|
{
|
||||||
|
for(auto const& item : make_list(value))
|
||||||
|
if(ci_equal(item, token))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
} // rfc2616
|
} // rfc2616
|
||||||
|
|
||||||
} // beast
|
} // beast
|
||||||
|
|||||||
@@ -17,13 +17,10 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <beast/http/impl/joyent_parser.h>
|
#include <beast/http/src/nodejs_parser.h>
|
||||||
#include <beast/http/method.h>
|
#include <beast/http/method.h>
|
||||||
#include <boost/system/error_code.hpp>
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace joyent {
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning (push)
|
# pragma warning (push)
|
||||||
# pragma warning (disable: 4127) // conditional expression is constant
|
# pragma warning (disable: 4127) // conditional expression is constant
|
||||||
@@ -34,33 +31,10 @@ namespace joyent {
|
|||||||
# pragma warning (pop)
|
# pragma warning (pop)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
beast::http::method_t
|
||||||
}
|
convert_http_method(http_method m)
|
||||||
|
|
||||||
namespace boost {
|
|
||||||
namespace system {
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct is_error_code_enum <beast::joyent::http_errno>
|
|
||||||
: std::true_type
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct is_error_condition_enum <beast::joyent::http_errno>
|
|
||||||
: std::true_type
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace joyent {
|
|
||||||
|
|
||||||
http::method_t
|
|
||||||
convert_http_method (joyent::http_method m)
|
|
||||||
{
|
{
|
||||||
|
using namespace beast;
|
||||||
switch (m)
|
switch (m)
|
||||||
{
|
{
|
||||||
case HTTP_DELETE: return http::method_t::http_delete;
|
case HTTP_DELETE: return http::method_t::http_delete;
|
||||||
@@ -114,58 +88,3 @@ convert_http_method (joyent::http_method m)
|
|||||||
|
|
||||||
return http::method_t::http_get;
|
return http::method_t::http_get;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::system::error_code
|
|
||||||
convert_http_errno (joyent::http_errno err)
|
|
||||||
{
|
|
||||||
class http_error_category_t
|
|
||||||
: public boost::system::error_category
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
using error_code = boost::system::error_code;
|
|
||||||
using error_condition = boost::system::error_condition;
|
|
||||||
|
|
||||||
public:
|
|
||||||
char const*
|
|
||||||
name() const noexcept override
|
|
||||||
{
|
|
||||||
return "http_errno";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
message (int ev) const override
|
|
||||||
{
|
|
||||||
return joyent::http_errno_name (
|
|
||||||
joyent::http_errno (ev));
|
|
||||||
}
|
|
||||||
|
|
||||||
error_condition
|
|
||||||
default_error_condition (int ev) const noexcept override
|
|
||||||
{
|
|
||||||
return error_condition (ev, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
equivalent (int code, error_condition const& condition
|
|
||||||
) const noexcept override
|
|
||||||
{
|
|
||||||
return default_error_condition (code) == condition;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
equivalent (error_code const& code, int condition
|
|
||||||
) const noexcept override
|
|
||||||
{
|
|
||||||
return *this == code.category() &&
|
|
||||||
code.value() == condition;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static http_error_category_t http_error_category;
|
|
||||||
|
|
||||||
return boost::system::error_code (
|
|
||||||
err, http_error_category);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,28 +17,29 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_JOYENT_PARSER_H_INCLUDED
|
#ifndef BEAST_HTTP_NODEJS_PARSER_H_INCLUDED
|
||||||
#define BEAST_HTTP_JOYENT_PARSER_H_INCLUDED
|
#define BEAST_HTTP_NODEJS_PARSER_H_INCLUDED
|
||||||
|
|
||||||
#include <beast/http/method.h>
|
#include <beast/http/method.h>
|
||||||
|
#include <beast/http/impl/http-parser/http_parser.h>
|
||||||
// TODO Use <system_error>
|
|
||||||
#include <boost/system/error_code.hpp>
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
// Wraps the C-language joyent http parser header in a namespace
|
beast::http::method_t
|
||||||
|
convert_http_method(http_method m);
|
||||||
|
|
||||||
namespace beast {
|
namespace boost {
|
||||||
namespace joyent {
|
namespace system {
|
||||||
|
template<>
|
||||||
#include <beast/http/impl/http-parser/http_parser.h>
|
struct is_error_code_enum<http_errno>
|
||||||
|
: std::true_type
|
||||||
http::method_t
|
{
|
||||||
convert_http_method (joyent::http_method m);
|
};
|
||||||
|
template<>
|
||||||
boost::system::error_code
|
struct is_error_condition_enum<http_errno>
|
||||||
convert_http_errno (joyent::http_errno err);
|
: std::true_type
|
||||||
|
{
|
||||||
}
|
};
|
||||||
}
|
} // system
|
||||||
|
} // boost
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
186
beast/http/src/test/beast_http_message_test.cpp
Normal file
186
beast/http/src/test/beast_http_message_test.cpp
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <BeastConfig.h>
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
#include <beast/unit_test/thread.h>
|
||||||
|
#include <beast/asio/buffers_debug.h>
|
||||||
|
#include <beast/asio/placeholders.h>
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <beast/http.h>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class sync_echo_http_server
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unit_test::suite& suite_;
|
||||||
|
boost::asio::io_service ios_;
|
||||||
|
socket_type sock_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
unit_test::thread thread_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
sync_echo_http_server(
|
||||||
|
endpoint_type ep, unit_test::suite& suite)
|
||||||
|
: suite_(suite)
|
||||||
|
, sock_(ios_)
|
||||||
|
, acceptor_(ios_)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
acceptor_.open(ep.protocol(), ec);
|
||||||
|
maybe_throw(ec, "open");
|
||||||
|
acceptor_.bind(ep, ec);
|
||||||
|
maybe_throw(ec, "bind");
|
||||||
|
acceptor_.listen(
|
||||||
|
boost::asio::socket_base::max_connections, ec);
|
||||||
|
maybe_throw(ec, "listen");
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&sync_echo_http_server::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
thread_ = unit_test::thread(suite_,
|
||||||
|
[&]
|
||||||
|
{
|
||||||
|
ios_.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
~sync_echo_http_server()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
ios_.dispatch(
|
||||||
|
[&]{ acceptor_.close(ec); });
|
||||||
|
thread_.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
suite_.log <<
|
||||||
|
what << ": " << ec.message();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
maybe_throw(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec &&
|
||||||
|
ec != boost::asio::error::operation_aborted)
|
||||||
|
{
|
||||||
|
fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_accept(error_code ec)
|
||||||
|
{
|
||||||
|
if(ec == boost::asio::error::operation_aborted)
|
||||||
|
return;
|
||||||
|
maybe_throw(ec, "accept");
|
||||||
|
std::thread{&sync_echo_http_server::do_client, this,
|
||||||
|
std::move(sock_), boost::asio::io_service::work{
|
||||||
|
sock_.get_io_service()}}.detach();
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&sync_echo_http_server::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
do_client(socket_type sock, boost::asio::io_service::work)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
streambuf rb;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
request<string_body> req;
|
||||||
|
read(sock, rb, req, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
response<string_body> resp(
|
||||||
|
{100, "OK", req.version});
|
||||||
|
resp.body = "Completed successfully.";
|
||||||
|
write(sock, resp, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class http_message_test : public unit_test::suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using error_code = boost::system::error_code;
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
|
||||||
|
void
|
||||||
|
syncEcho(endpoint_type ep)
|
||||||
|
{
|
||||||
|
boost::asio::io_service ios;
|
||||||
|
socket_type sock(ios);
|
||||||
|
sock.connect(ep);
|
||||||
|
|
||||||
|
streambuf rb;
|
||||||
|
{
|
||||||
|
request<string_body> req(
|
||||||
|
{beast::http::method_t::http_get, "/", 11});
|
||||||
|
req.body = "Beast.HTTP";
|
||||||
|
req.headers.replace("Host",
|
||||||
|
ep.address().to_string() + ":" +
|
||||||
|
std::to_string(ep.port()));
|
||||||
|
write(sock, req);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
response<string_body> m;
|
||||||
|
read(sock, rb, m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testAsio()
|
||||||
|
{
|
||||||
|
endpoint_type ep{
|
||||||
|
address_type::from_string("127.0.0.1"), 6000};
|
||||||
|
sync_echo_http_server s(ep, *this);
|
||||||
|
syncEcho(ep);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
testAsio();
|
||||||
|
pass();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE_MANUAL(http_message,http,beast);
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
@@ -23,46 +23,30 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace deprecated_http {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
class message_test : public beast::unit_test::suite
|
class parser_test : public beast::unit_test::suite
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::pair <message, bool>
|
message
|
||||||
request (std::string const& text)
|
request(std::string const& text)
|
||||||
{
|
{
|
||||||
message m;
|
|
||||||
body b;
|
body b;
|
||||||
parser p (m, b, true);
|
message m;
|
||||||
auto result (p.write (boost::asio::buffer(text)));
|
parser p(m, b, true);
|
||||||
|
auto const used =
|
||||||
|
p.write(boost::asio::buffer(text));
|
||||||
|
expect(used == text.size());
|
||||||
p.write_eof();
|
p.write_eof();
|
||||||
return std::make_pair (std::move(m), result.first);
|
return m;
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
dump()
|
|
||||||
{
|
|
||||||
auto const result = request (
|
|
||||||
"GET / HTTP/1.1\r\n"
|
|
||||||
//"Connection: Upgrade\r\n"
|
|
||||||
//"Upgrade: Ripple\r\n"
|
|
||||||
"Field: \t Value \t \r\n"
|
|
||||||
"Blib: Continu\r\n"
|
|
||||||
" ation\r\n"
|
|
||||||
"Field: Hey\r\n"
|
|
||||||
"Content-Length: 1\r\n"
|
|
||||||
"\r\n"
|
|
||||||
"x"
|
|
||||||
);
|
|
||||||
log << result.first.headers;
|
|
||||||
log << "|" << result.first.headers["Field"] << "|";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
test_headers()
|
test_headers()
|
||||||
{
|
{
|
||||||
headers h;
|
beast::http::headers<std::allocator<char>> h;
|
||||||
h.append("Field", "Value");
|
h.insert("Field", "Value");
|
||||||
expect (h.erase("Field") == 1);
|
expect (h.erase("Field") == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,14 +60,14 @@ public:
|
|||||||
"GET / HTTP/1.1\r\n"
|
"GET / HTTP/1.1\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
;
|
;
|
||||||
message m;
|
|
||||||
body b;
|
body b;
|
||||||
|
message m;
|
||||||
parser p (m, b, true);
|
parser p (m, b, true);
|
||||||
auto result (p.write (boost::asio::buffer(text)));
|
auto const used = p.write(
|
||||||
expect (! result.first);
|
boost::asio::buffer(text));
|
||||||
auto result2 (p.write_eof());
|
expect(used == text.size());
|
||||||
expect (! result2);
|
p.write_eof();
|
||||||
expect (p.complete());
|
expect(p.complete());
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -92,17 +76,19 @@ public:
|
|||||||
"GET\r\n"
|
"GET\r\n"
|
||||||
"\r\n"
|
"\r\n"
|
||||||
;
|
;
|
||||||
message m;
|
|
||||||
body b;
|
body b;
|
||||||
parser p (m, b, true);
|
message m;
|
||||||
auto result = p.write (boost::asio::buffer(text));
|
parser p(m, b, true);
|
||||||
if (expect (result.first))
|
boost::system::error_code ec;
|
||||||
expect (result.first.message() == "invalid HTTP method");
|
p.write(boost::asio::buffer(text), ec);
|
||||||
|
if(expect(ec))
|
||||||
|
expect(ec.message() == "invalid HTTP method");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BEAST_DEFINE_TESTSUITE(message,http,beast);
|
BEAST_DEFINE_TESTSUITE(parser,http,beast);
|
||||||
|
|
||||||
} // http
|
} // test
|
||||||
|
} // deprecated_http
|
||||||
} // beast
|
} // beast
|
||||||
126
beast/http/src/test/beast_http_rfc2616_test.cpp
Normal file
126
beast/http/src/test/beast_http_rfc2616_test.cpp
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <beast/http/rfc2616.h>
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace rfc2616 {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class rfc2616_test : public beast::unit_test::suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
checkSplit(std::string const& s,
|
||||||
|
std::vector <std::string> const& expected)
|
||||||
|
{
|
||||||
|
auto const parsed = split_commas(s.begin(), s.end());
|
||||||
|
expect (parsed == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testSplit()
|
||||||
|
{
|
||||||
|
checkSplit("", {});
|
||||||
|
checkSplit(" ", {});
|
||||||
|
checkSplit(" ", {});
|
||||||
|
checkSplit("\t", {});
|
||||||
|
checkSplit(" \t ", {});
|
||||||
|
checkSplit(",", {});
|
||||||
|
checkSplit(",,", {});
|
||||||
|
checkSplit(" ,", {});
|
||||||
|
checkSplit(" , ,", {});
|
||||||
|
checkSplit("x", {"x"});
|
||||||
|
checkSplit(" x", {"x"});
|
||||||
|
checkSplit(" \t x", {"x"});
|
||||||
|
checkSplit("x ", {"x"});
|
||||||
|
checkSplit("x \t", {"x"});
|
||||||
|
checkSplit(" \t x \t ", {"x"});
|
||||||
|
checkSplit("\"\"", {});
|
||||||
|
checkSplit(" \"\"", {});
|
||||||
|
checkSplit("\"\" ", {});
|
||||||
|
checkSplit("\"x\"", {"x"});
|
||||||
|
checkSplit("\" \"", {" "});
|
||||||
|
checkSplit("\" x\"", {" x"});
|
||||||
|
checkSplit("\"x \"", {"x "});
|
||||||
|
checkSplit("\" x \"", {" x "});
|
||||||
|
checkSplit("\"\tx \"", {"\tx "});
|
||||||
|
checkSplit("x,y", {"x", "y"});
|
||||||
|
checkSplit("x ,\ty ", {"x", "y"});
|
||||||
|
checkSplit("x, y, z", {"x","y","z"});
|
||||||
|
checkSplit("x, \"y\", z", {"x","y","z"});
|
||||||
|
checkSplit(",,x,,\"y\",,", {"x","y"});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
checkIter(std::string const& s,
|
||||||
|
std::vector<std::string> const& expected)
|
||||||
|
{
|
||||||
|
std::vector<std::string> got;
|
||||||
|
for(auto const& v : make_list(s))
|
||||||
|
got.emplace_back(v);
|
||||||
|
expect(got == expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testIter()
|
||||||
|
{
|
||||||
|
checkIter("x", {"x"});
|
||||||
|
checkIter(" x", {"x"});
|
||||||
|
checkIter("x\t", {"x"});
|
||||||
|
checkIter("\tx ", {"x"});
|
||||||
|
checkIter(",x", {"x"});
|
||||||
|
checkIter("x,", {"x"});
|
||||||
|
checkIter(",x,", {"x"});
|
||||||
|
checkIter(" , x\t,\t", {"x"});
|
||||||
|
checkIter("x,y", {"x", "y"});
|
||||||
|
checkIter("x, ,y ", {"x", "y"});
|
||||||
|
checkIter("\"x\"", {"x"});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
testList()
|
||||||
|
{
|
||||||
|
expect(token_in_list("x", "x"));
|
||||||
|
expect(token_in_list("x,y", "x"));
|
||||||
|
expect(token_in_list("x,y", "y"));
|
||||||
|
expect(token_in_list("x, y ", "y"));
|
||||||
|
expect(token_in_list("x", "X"));
|
||||||
|
expect(token_in_list("Y", "y"));
|
||||||
|
expect(token_in_list("close, keepalive", "close"));
|
||||||
|
expect(token_in_list("close, keepalive", "keepalive"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
testSplit();
|
||||||
|
testIter();
|
||||||
|
testList();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(rfc2616,http,beast);
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // rfc2616
|
||||||
|
} // beast
|
||||||
109
beast/http/streambuf_body.h
Normal file
109
beast/http/streambuf_body.h
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_STREAMBUF_BODY_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_STREAMBUF_BODY_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/error.h>
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <beast/asio/append_buffers.h>
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** A Body represented by a Streambuf
|
||||||
|
*/
|
||||||
|
template<class Streambuf>
|
||||||
|
struct basic_streambuf_body
|
||||||
|
{
|
||||||
|
using value_type = Streambuf;
|
||||||
|
|
||||||
|
class reader
|
||||||
|
{
|
||||||
|
value_type& sb_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<bool isRequest, class Allocator>
|
||||||
|
explicit
|
||||||
|
reader(message<isRequest,
|
||||||
|
basic_streambuf_body, Allocator>& m) noexcept
|
||||||
|
: sb_(m.body)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write(void const* data,
|
||||||
|
std::size_t size, error_code&) noexcept
|
||||||
|
{
|
||||||
|
using boost::asio::buffer;
|
||||||
|
using boost::asio::buffer_copy;
|
||||||
|
sb_.commit(buffer_copy(
|
||||||
|
sb_.prepare(size), buffer(data, size)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class writer
|
||||||
|
{
|
||||||
|
Streambuf const& body_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<bool isRequest, class Allocator>
|
||||||
|
explicit
|
||||||
|
writer(message<isRequest, basic_streambuf_body,
|
||||||
|
Allocator> const& m)
|
||||||
|
: body_(m.body)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(error_code& ec)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
content_length() const
|
||||||
|
{
|
||||||
|
return body_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Write>
|
||||||
|
boost::tribool
|
||||||
|
operator()(resume_context&&, error_code&, Write&& write)
|
||||||
|
{
|
||||||
|
write(body_.data());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto
|
||||||
|
data() const noexcept
|
||||||
|
{
|
||||||
|
return body_.data();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
using streambuf_body = basic_streambuf_body<streambuf>;
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
99
beast/http/string_body.h
Normal file
99
beast/http/string_body.h
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_STRING_BODY_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_STRING_BODY_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/error.h>
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <beast/http/resume_context.h>
|
||||||
|
#include <beast/asio/append_buffers.h>
|
||||||
|
#include <beast/asio/streambuf.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** A Body represented by a std::string.
|
||||||
|
*/
|
||||||
|
struct string_body
|
||||||
|
{
|
||||||
|
using value_type = std::string;
|
||||||
|
|
||||||
|
class reader
|
||||||
|
{
|
||||||
|
value_type& s_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<bool isRequest, class Allocator>
|
||||||
|
explicit
|
||||||
|
reader(message<isRequest,
|
||||||
|
string_body, Allocator>& m) noexcept
|
||||||
|
: s_(m.body)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
write(void const* data,
|
||||||
|
std::size_t size, error_code&) noexcept
|
||||||
|
{
|
||||||
|
auto const n = s_.size();
|
||||||
|
s_.resize(n + size);
|
||||||
|
std::memcpy(&s_[n], data, size);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class writer
|
||||||
|
{
|
||||||
|
value_type const& body_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<bool isRequest, class Allocator>
|
||||||
|
explicit
|
||||||
|
writer(message<isRequest, string_body, Allocator> const& msg)
|
||||||
|
: body_(msg.body)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(error_code& ec)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
content_length() const
|
||||||
|
{
|
||||||
|
return body_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Write>
|
||||||
|
boost::tribool
|
||||||
|
operator()(resume_context&&, error_code&, Write&& write)
|
||||||
|
{
|
||||||
|
write(boost::asio::buffer(body_));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
|
||||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
//==============================================================================
|
|
||||||
|
|
||||||
#include <beast/http/URL.h>
|
|
||||||
|
|
||||||
#include <beast/unit_test/suite.h>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
|
|
||||||
class URL_test : public unit_test::suite
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void check_url_parsing (std::string const& url, bool expected)
|
|
||||||
{
|
|
||||||
auto result = parse_URL (url);
|
|
||||||
|
|
||||||
expect (result.first == expected,
|
|
||||||
(expected ? "Failed to parse " : "Succeeded in parsing ") + url);
|
|
||||||
expect (to_string (result.second) == url);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_url_parsing ()
|
|
||||||
{
|
|
||||||
char const* const urls[] =
|
|
||||||
{
|
|
||||||
"http://en.wikipedia.org/wiki/URI#Examples_of_URI_references",
|
|
||||||
"ftp://ftp.funet.fi/pub/standards/RFC/rfc959.txt"
|
|
||||||
"ftp://test:test@example.com:21/path/specifier/is/here"
|
|
||||||
"http://www.boost.org/doc/libs/1_54_0/doc/html/boost_asio/reference.html",
|
|
||||||
"foo://username:password@example.com:8042/over/there/index.dtb?type=animal&name=narwhal#nose",
|
|
||||||
};
|
|
||||||
|
|
||||||
testcase ("URL parsing");
|
|
||||||
|
|
||||||
for (auto url : urls)
|
|
||||||
check_url_parsing (url, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
run ()
|
|
||||||
{
|
|
||||||
test_url_parsing ();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BEAST_DEFINE_TESTSUITE(URL,http,beast);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
|
||||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
|
||||||
purpose with or without fee is hereby granted, provided that the above
|
|
||||||
copyright notice and this permission notice appear in all copies.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
||||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
||||||
*/
|
|
||||||
//==============================================================================
|
|
||||||
|
|
||||||
#if BEAST_INCLUDE_BEASTCONFIG
|
|
||||||
#include "../../BeastConfig.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <beast/http/rfc2616.h>
|
|
||||||
#include <beast/unit_test/suite.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace rfc2616 {
|
|
||||||
|
|
||||||
class rfc2616_test : public beast::unit_test::suite
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void
|
|
||||||
check (std::string const& s,
|
|
||||||
std::vector <std::string> const& expected)
|
|
||||||
{
|
|
||||||
auto const parsed = split_commas(s.begin(), s.end());
|
|
||||||
expect (parsed == expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_split_commas()
|
|
||||||
{
|
|
||||||
testcase("split_commas");
|
|
||||||
check ("", {});
|
|
||||||
check (" ", {});
|
|
||||||
check (" ", {});
|
|
||||||
check ("\t", {});
|
|
||||||
check (" \t ", {});
|
|
||||||
check (",", {});
|
|
||||||
check (",,", {});
|
|
||||||
check (" ,", {});
|
|
||||||
check (" , ,", {});
|
|
||||||
check ("x", {"x"});
|
|
||||||
check (" x", {"x"});
|
|
||||||
check (" \t x", {"x"});
|
|
||||||
check ("x ", {"x"});
|
|
||||||
check ("x \t", {"x"});
|
|
||||||
check (" \t x \t ", {"x"});
|
|
||||||
check ("\"\"", {});
|
|
||||||
check (" \"\"", {});
|
|
||||||
check ("\"\" ", {});
|
|
||||||
check ("\"x\"", {"x"});
|
|
||||||
check ("\" \"", {" "});
|
|
||||||
check ("\" x\"", {" x"});
|
|
||||||
check ("\"x \"", {"x "});
|
|
||||||
check ("\" x \"", {" x "});
|
|
||||||
check ("\"\tx \"", {"\tx "});
|
|
||||||
check ("x,y", { "x", "y" });
|
|
||||||
check ("x ,\ty ", { "x", "y" });
|
|
||||||
check ("x, y, z", {"x","y","z"});
|
|
||||||
check ("x, \"y\", z", {"x","y","z"});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
run()
|
|
||||||
{
|
|
||||||
test_split_commas();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BEAST_DEFINE_TESTSUITE(rfc2616,http,beast);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
84
beast/http/type_check.h
Normal file
84
beast/http/type_check.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_TYPE_CHECK_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_TYPE_CHECK_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/error.h>
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <beast/http/resume_context.h>
|
||||||
|
#include <beast/asio/type_check.h>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/logic/tribool.hpp>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
#include <functional>
|
||||||
|
#include <beast/cxx17/type_traits.h> // <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
namespace detail {
|
||||||
|
#else
|
||||||
|
namespace concept {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Reader
|
||||||
|
{
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
Reader(message<isRequest, Body, Headers>&) noexcept;
|
||||||
|
void write(void const*, std::size_t, error_code&) noexcept;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // concept
|
||||||
|
|
||||||
|
/// Evaluates to std::true_type if `T` models Body
|
||||||
|
template<class T>
|
||||||
|
struct is_Body : std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Evalulates to std::true_type if Body has a reader
|
||||||
|
template<class T>
|
||||||
|
struct is_ReadableBody : std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Evalulates to std::true_type if Body has a writer
|
||||||
|
template<class T>
|
||||||
|
struct is_WritableBody : std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Evaluates to std::true_type if `T` models HTTPMessage
|
||||||
|
template<class T>
|
||||||
|
struct is_HTTPMessage : std::false_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Evaluates to std::true_type if `HTTPMessage` is a request
|
||||||
|
template<class HTTPMessage>
|
||||||
|
struct is_HTTPRequest : std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
103
beast/http/write.h
Normal file
103
beast/http/write.h
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_WRITE_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_WRITE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/error.h>
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** Write a HTTP message to a stream.
|
||||||
|
|
||||||
|
@param stream The stream to send the message on.
|
||||||
|
|
||||||
|
@param msg The message to send.
|
||||||
|
|
||||||
|
@throws boost::system::error code on failure.
|
||||||
|
*/
|
||||||
|
template<class SyncWriteStream,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
write(SyncWriteStream& stream,
|
||||||
|
message<isRequest, Body, Headers> const& msg)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
write(stream, msg, ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write a HTTP message to a stream.
|
||||||
|
|
||||||
|
@param stream The stream to send the message on.
|
||||||
|
|
||||||
|
@param msg The message to send.
|
||||||
|
|
||||||
|
@param ec Set to the error, if any occurred.
|
||||||
|
*/
|
||||||
|
template<class SyncWriteStream,
|
||||||
|
bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
write(SyncWriteStream& stream,
|
||||||
|
message<isRequest, Body, Headers> const& msg,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start writing a HTTP message to a stream asynchronously.
|
||||||
|
|
||||||
|
@param stream The stream to send the message on.
|
||||||
|
|
||||||
|
@param msg The message to send.
|
||||||
|
|
||||||
|
@param token The handler to be called when the request completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent
|
||||||
|
function signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
error_code const& error // result of operation
|
||||||
|
); @endcode
|
||||||
|
Regardless of whether the asynchronous operation completes
|
||||||
|
immediately or not, the handler will not be invoked from within
|
||||||
|
this function. Invocation of the handler will be performed in a
|
||||||
|
manner equivalent to using boost::asio::io_service::post().
|
||||||
|
|
||||||
|
@note The message must remain valid at least until the
|
||||||
|
completion handler is called, no copies are made.
|
||||||
|
*/
|
||||||
|
template<class AsyncWriteStream,
|
||||||
|
bool isRequest, class Body, class Headers,
|
||||||
|
class CompletionToken>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
void_or_deduced
|
||||||
|
#else
|
||||||
|
auto
|
||||||
|
#endif
|
||||||
|
async_write(AsyncWriteStream& stream,
|
||||||
|
message<isRequest, Body, Headers> const& msg,
|
||||||
|
CompletionToken&& token);
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include <beast/http/impl/write.ipp>
|
||||||
|
|
||||||
|
#endif
|
||||||
27
beast/unity/beast_http_unity.cpp
Normal file
27
beast/unity/beast_http_unity.cpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include <beast/http/src/test/beast_http_chunked_encoder_test.cpp>
|
||||||
|
#include <beast/http/src/test/beast_http_message_test.cpp>
|
||||||
|
#include <beast/http/src/test/beast_http_parser_test.cpp>
|
||||||
|
#include <beast/http/src/test/beast_http_rfc2616_test.cpp>
|
||||||
|
|
||||||
|
// VFALCO Must come last otherwise Windows 10 SDK
|
||||||
|
// gets a compile error in winnt.h
|
||||||
|
#include <beast/http/src/beast_http_nodejs_parser.cpp>
|
||||||
19
examples/Jamfile
Normal file
19
examples/Jamfile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
#
|
||||||
|
# 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)
|
||||||
|
#
|
||||||
|
|
||||||
|
import os ;
|
||||||
|
|
||||||
|
exe http_crawl :
|
||||||
|
../beast/http/src/beast_http_nodejs_parser.cpp
|
||||||
|
http_crawl.cpp
|
||||||
|
urls_large_data.cpp
|
||||||
|
;
|
||||||
|
|
||||||
|
exe http_server :
|
||||||
|
../beast/http/src/beast_http_nodejs_parser.cpp
|
||||||
|
http_server.cpp
|
||||||
|
;
|
||||||
94
examples/file_body.h
Normal file
94
examples/file_body.h
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
|
||||||
|
#define BEAST_EXAMPLE_FILE_BODY_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/message.h>
|
||||||
|
#include <beast/http/resume_context.h>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
struct file_body
|
||||||
|
{
|
||||||
|
using value_type = std::string;
|
||||||
|
|
||||||
|
class writer
|
||||||
|
{
|
||||||
|
std::size_t size_;
|
||||||
|
std::size_t offset_ = 0;
|
||||||
|
std::string const& path_;
|
||||||
|
FILE* file_ = nullptr;
|
||||||
|
char buf_[4096];
|
||||||
|
std::size_t buf_len_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static bool constexpr is_single_pass = false;
|
||||||
|
|
||||||
|
template<bool isRequest, class Headers>
|
||||||
|
writer(message<isRequest, file_body, Headers> const& m) noexcept
|
||||||
|
: path_(m.body)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~writer()
|
||||||
|
{
|
||||||
|
if(file_)
|
||||||
|
fclose(file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
init(error_code& ec) noexcept
|
||||||
|
{
|
||||||
|
file_ = fopen(path_.c_str(), "rb");
|
||||||
|
if(! file_)
|
||||||
|
ec = boost::system::errc::make_error_code(
|
||||||
|
static_cast<boost::system::errc::errc_t>(errno));
|
||||||
|
else
|
||||||
|
size_ = boost::filesystem::file_size(path_);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto
|
||||||
|
content_length() const
|
||||||
|
{
|
||||||
|
return size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Write>
|
||||||
|
boost::tribool
|
||||||
|
operator()(resume_context&&, error_code&, Write&& write)
|
||||||
|
{
|
||||||
|
buf_len_ = std::min(size_ - offset_, sizeof(buf_));
|
||||||
|
fread(buf_, 1, sizeof(buf_), file_);
|
||||||
|
offset_ += buf_len_;
|
||||||
|
write(boost::asio::buffer(buf_, buf_len_));
|
||||||
|
return offset_ >= size_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
205
examples/http_async_server.h
Normal file
205
examples/http_async_server.h
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
|
||||||
|
#define BEAST_EXAMPLE_HTTP_ASYNC_SERVER_H_INCLUDED
|
||||||
|
|
||||||
|
#include "file_body.h"
|
||||||
|
#include "http_stream.h"
|
||||||
|
|
||||||
|
#include <beast/asio/placeholders.h>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
class http_async_server
|
||||||
|
{
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
|
||||||
|
using req_type = request<string_body>;
|
||||||
|
using resp_type = response<file_body>;
|
||||||
|
|
||||||
|
boost::asio::io_service ios_;
|
||||||
|
socket_type sock_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
std::string root_;
|
||||||
|
std::vector<std::thread> thread_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
http_async_server(endpoint_type const& ep,
|
||||||
|
int threads, std::string const& root)
|
||||||
|
: sock_(ios_)
|
||||||
|
, acceptor_(ios_)
|
||||||
|
, root_(root)
|
||||||
|
{
|
||||||
|
acceptor_.open(ep.protocol());
|
||||||
|
acceptor_.bind(ep);
|
||||||
|
acceptor_.listen(
|
||||||
|
boost::asio::socket_base::max_connections);
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&http_async_server::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
thread_.reserve(threads);
|
||||||
|
for(int i = 0; i < threads; ++i)
|
||||||
|
thread_.emplace_back(
|
||||||
|
[&] { ios_.run(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
~http_async_server()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
ios_.dispatch(
|
||||||
|
[&]{ acceptor_.close(ec); });
|
||||||
|
for(auto& t : thread_)
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
class peer : public std::enable_shared_from_this<peer>
|
||||||
|
{
|
||||||
|
int id_;
|
||||||
|
stream<socket_type> stream_;
|
||||||
|
boost::asio::io_service::strand strand_;
|
||||||
|
std::string root_;
|
||||||
|
req_type req_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
peer(peer&&) = default;
|
||||||
|
peer(peer const&) = default;
|
||||||
|
peer& operator=(peer&&) = delete;
|
||||||
|
peer& operator=(peer const&) = delete;
|
||||||
|
|
||||||
|
explicit
|
||||||
|
peer(socket_type&& sock, std::string const& root)
|
||||||
|
: id_([]
|
||||||
|
{
|
||||||
|
static int n = 0;
|
||||||
|
return ++n;
|
||||||
|
}())
|
||||||
|
, stream_(std::move(sock))
|
||||||
|
, strand_(stream_.get_io_service())
|
||||||
|
, root_(root)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void run()
|
||||||
|
{
|
||||||
|
do_read();
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_read()
|
||||||
|
{
|
||||||
|
stream_.async_read(req_, strand_.wrap(
|
||||||
|
std::bind(&peer::on_read, shared_from_this(),
|
||||||
|
asio::placeholders::error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_read(error_code ec)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
return fail(ec, "read");
|
||||||
|
do_read();
|
||||||
|
auto path = req_.url;
|
||||||
|
if(path == "/")
|
||||||
|
path = "/index.html";
|
||||||
|
path = root_ + path;
|
||||||
|
if(! boost::filesystem::exists(path))
|
||||||
|
{
|
||||||
|
response<string_body> resp(
|
||||||
|
{404, "Not Found", req_.version});
|
||||||
|
resp.headers.replace("Server", "http_async_server");
|
||||||
|
resp.body = "The file '" + path + "' was not found";
|
||||||
|
stream_.async_write(std::move(resp),
|
||||||
|
std::bind(&peer::on_write, shared_from_this(),
|
||||||
|
asio::placeholders::error));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
response<file_body> resp(
|
||||||
|
{200, "OK", req_.version});
|
||||||
|
resp.headers.replace("Server", "http_async_server");
|
||||||
|
resp.headers.replace("Content-Type", "text/html");
|
||||||
|
resp.body = path;
|
||||||
|
stream_.async_write(std::move(resp),
|
||||||
|
std::bind(&peer::on_write, shared_from_this(),
|
||||||
|
asio::placeholders::error));
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_write(error_code ec)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
fail(ec, "write");
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec != boost::asio::error::operation_aborted)
|
||||||
|
{
|
||||||
|
std::cerr <<
|
||||||
|
"#" << std::to_string(id_) << " " <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
std::cerr <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
maybe_throw(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_accept(error_code ec)
|
||||||
|
{
|
||||||
|
if(! acceptor_.is_open())
|
||||||
|
return;
|
||||||
|
maybe_throw(ec, "accept");
|
||||||
|
socket_type sock(std::move(sock_));
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&http_async_server::on_accept, this,
|
||||||
|
asio::placeholders::error));
|
||||||
|
std::make_shared<peer>(std::move(sock), root_)->run();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
67
examples/http_crawl.cpp
Normal file
67
examples/http_crawl.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include "http_stream.h"
|
||||||
|
#include "urls_large_data.h"
|
||||||
|
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace beast::http;
|
||||||
|
using namespace boost::asio;
|
||||||
|
|
||||||
|
template<class String>
|
||||||
|
void
|
||||||
|
err(error_code const& ec, String const& what)
|
||||||
|
{
|
||||||
|
std::cerr << what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int, char const*[])
|
||||||
|
{
|
||||||
|
io_service ios;
|
||||||
|
for(auto const& host : urls_large_data())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ip::tcp::resolver r(ios);
|
||||||
|
auto it = r.resolve(
|
||||||
|
ip::tcp::resolver::query{host, "http"});
|
||||||
|
stream<ip::tcp::socket> hs(ios);
|
||||||
|
connect(hs.lowest_layer(), it);
|
||||||
|
auto ep = hs.lowest_layer().remote_endpoint();
|
||||||
|
request<empty_body> req({method_t::http_get, "/", 11});
|
||||||
|
req.headers.insert("Host", host +
|
||||||
|
std::string(":") + std::to_string(ep.port()));
|
||||||
|
req.headers.insert("User-Agent", "beast/http");
|
||||||
|
hs.write(req);
|
||||||
|
response<string_body> resp;
|
||||||
|
hs.read(resp);
|
||||||
|
std::cout << resp;
|
||||||
|
}
|
||||||
|
catch(boost::system::system_error const& ec)
|
||||||
|
{
|
||||||
|
std::cerr << host << ": " << ec.what();
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
std::cerr << host << ": unknown exception" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
81
examples/http_server.cpp
Normal file
81
examples/http_server.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#include "http_async_server.h"
|
||||||
|
#include "http_sync_server.h"
|
||||||
|
#include "sig_wait.h"
|
||||||
|
|
||||||
|
#include <boost/program_options.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main(int ac, char const* av[])
|
||||||
|
{
|
||||||
|
using namespace beast::http;
|
||||||
|
namespace po = boost::program_options;
|
||||||
|
po::options_description desc("Options");
|
||||||
|
|
||||||
|
desc.add_options()
|
||||||
|
("root,r", po::value<std::string>()->implicit_value("."),
|
||||||
|
"Set the root directory for serving files")
|
||||||
|
("port,p", po::value<std::uint16_t>()->implicit_value(8080),
|
||||||
|
"Set the port number for the server")
|
||||||
|
("ip", po::value<std::string>()->implicit_value("0.0.0.0"),
|
||||||
|
"Set the IP address to bind to, \"0.0.0.0\" for all")
|
||||||
|
("threads,n", po::value<std::size_t>()->implicit_value(4),
|
||||||
|
"Set the number of threads to use")
|
||||||
|
("sync,s", "Launch a synchronous server")
|
||||||
|
;
|
||||||
|
po::variables_map vm;
|
||||||
|
po::store(po::parse_command_line(ac, av, desc), vm);
|
||||||
|
|
||||||
|
std::string root = ".";
|
||||||
|
if(vm.count("root"))
|
||||||
|
root = vm["root"].as<std::string>();
|
||||||
|
|
||||||
|
std::uint16_t port = 8080;
|
||||||
|
if(vm.count("port"))
|
||||||
|
port = vm["port"].as<std::uint16_t>();
|
||||||
|
|
||||||
|
std::string ip = "0.0.0.0";
|
||||||
|
if(vm.count("ip"))
|
||||||
|
ip = vm["ip"].as<std::string>();
|
||||||
|
|
||||||
|
std::size_t threads = 4;
|
||||||
|
if(vm.count("threads"))
|
||||||
|
threads = vm["threads"].as<std::size_t>();
|
||||||
|
|
||||||
|
bool sync = vm.count("sync") > 0;
|
||||||
|
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
|
||||||
|
endpoint_type ep{address_type::from_string(ip), port};
|
||||||
|
|
||||||
|
if(sync)
|
||||||
|
{
|
||||||
|
http_sync_server server(ep, root);
|
||||||
|
sig_wait();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
http_async_server server(ep, threads, root);
|
||||||
|
sig_wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
31
examples/http_server_response.h
Normal file
31
examples/http_server_response.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_EXAMPLE_SERVER_RESPONSE_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_EXAMPLE_SERVER_RESPONSE_H_INCLUDED
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
namespace example {
|
||||||
|
|
||||||
|
} // example
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
488
examples/http_stream.h
Normal file
488
examples/http_stream.h
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_STREAM_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_STREAM_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http.h>
|
||||||
|
#include <beast/asio/basic_streambuf.h>
|
||||||
|
#include <boost/asio/io_service.hpp>
|
||||||
|
#include <boost/intrusive/list.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
class stream_base
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
struct op
|
||||||
|
: boost::intrusive::list_base_hook<
|
||||||
|
boost::intrusive::link_mode<
|
||||||
|
boost::intrusive::normal_link>>
|
||||||
|
{
|
||||||
|
virtual ~op() = default;
|
||||||
|
virtual void operator()() = 0;
|
||||||
|
virtual void cancel() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
using op_list = typename boost::intrusive::make_list<
|
||||||
|
op, boost::intrusive::constant_time_size<false>>::type;
|
||||||
|
|
||||||
|
op_list wr_q_;
|
||||||
|
bool wr_active_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/** Provides message-oriented functionality using HTTP.
|
||||||
|
|
||||||
|
The stream class template provides asynchronous and blocking
|
||||||
|
message-oriented functionality necessary for clients and servers
|
||||||
|
to utilize the HTTP protocol.
|
||||||
|
|
||||||
|
@par Thread Safety
|
||||||
|
@e Distinct @e objects: Safe.@n
|
||||||
|
@e Shared @e objects: Unsafe. The application must ensure that
|
||||||
|
all asynchronous operations are performed within the same
|
||||||
|
implicit or explicit strand.
|
||||||
|
|
||||||
|
@par Example
|
||||||
|
|
||||||
|
To use the class template with an `ip::tcp::socket`, you would write:
|
||||||
|
|
||||||
|
@code
|
||||||
|
http::stream<ip::tcp::socket> hs(io_service);
|
||||||
|
@endcode
|
||||||
|
Alternatively, you can write:
|
||||||
|
@code
|
||||||
|
ip::tcp::socket sock(io_service);
|
||||||
|
http::stream<ip::tcp::socket&> hs(sock);
|
||||||
|
@endcode
|
||||||
|
|
||||||
|
@note A stream object must not be destroyed while there are
|
||||||
|
pending asynchronous operations associated with it.
|
||||||
|
|
||||||
|
@par Concepts
|
||||||
|
AsyncReadStream, AsyncWriteStream, Stream, SyncReadStream, SyncWriteStream.
|
||||||
|
*/
|
||||||
|
template<class NextLayer,
|
||||||
|
class Allocator = std::allocator<char>>
|
||||||
|
class stream : public detail::stream_base
|
||||||
|
{
|
||||||
|
NextLayer next_layer_;
|
||||||
|
basic_streambuf<Allocator> rd_buf_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// The type of the next layer.
|
||||||
|
using next_layer_type =
|
||||||
|
std::remove_reference_t<NextLayer>;
|
||||||
|
|
||||||
|
/// The type of the lowest layer.
|
||||||
|
using lowest_layer_type =
|
||||||
|
typename next_layer_type::lowest_layer_type;
|
||||||
|
|
||||||
|
/// The type of endpoint of the lowest layer.
|
||||||
|
using endpoint_type =
|
||||||
|
typename lowest_layer_type::endpoint_type;
|
||||||
|
|
||||||
|
/// The protocol of the next layer.
|
||||||
|
using protocol_type =
|
||||||
|
typename lowest_layer_type::protocol_type;
|
||||||
|
|
||||||
|
/// The type of resolver of the next layer.
|
||||||
|
using resolver_type =
|
||||||
|
typename protocol_type::resolver;
|
||||||
|
|
||||||
|
/** Destructor.
|
||||||
|
|
||||||
|
@note A stream object must not be destroyed while there
|
||||||
|
are pending asynchronous operations associated with it.
|
||||||
|
*/
|
||||||
|
~stream();
|
||||||
|
|
||||||
|
/** Move constructor.
|
||||||
|
|
||||||
|
Undefined behavior if operations are active or pending.
|
||||||
|
*/
|
||||||
|
stream(stream&&) = default;
|
||||||
|
|
||||||
|
/** Move assignment.
|
||||||
|
|
||||||
|
Undefined behavior if operations are active or pending.
|
||||||
|
*/
|
||||||
|
stream& operator=(stream&&) = default;
|
||||||
|
|
||||||
|
/** Construct a HTTP stream.
|
||||||
|
|
||||||
|
This constructor creates a HTTP stream and initialises
|
||||||
|
the next layer.
|
||||||
|
|
||||||
|
@throws Any exceptions thrown by the Stream constructor.
|
||||||
|
|
||||||
|
@param args The arguments to be passed to initialise the
|
||||||
|
next layer. The arguments are forwarded to the next layer's
|
||||||
|
constructor.
|
||||||
|
*/
|
||||||
|
template<class... Args>
|
||||||
|
explicit
|
||||||
|
stream(Args&&... args);
|
||||||
|
|
||||||
|
/** Get the io_service associated with the stream.
|
||||||
|
|
||||||
|
This function may be used to obtain the io_service object
|
||||||
|
that the stream uses to dispatch handlers for asynchronous
|
||||||
|
operations.
|
||||||
|
|
||||||
|
@return A reference to the io_service object that the stream
|
||||||
|
will use to dispatch handlers. Ownership is not transferred
|
||||||
|
to the caller.
|
||||||
|
*/
|
||||||
|
boost::asio::io_service&
|
||||||
|
get_io_service()
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer().get_io_service();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the next layer.
|
||||||
|
|
||||||
|
This function returns a reference to the next layer
|
||||||
|
in a stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the next layer in the stack of
|
||||||
|
stream layers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
next_layer_type&
|
||||||
|
next_layer()
|
||||||
|
{
|
||||||
|
return next_layer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the next layer.
|
||||||
|
|
||||||
|
This function returns a reference to the next layer in a
|
||||||
|
stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the next layer in the stack of
|
||||||
|
stream layers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
next_layer_type const&
|
||||||
|
next_layer() const
|
||||||
|
{
|
||||||
|
return next_layer_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the lowest layer.
|
||||||
|
|
||||||
|
This function returns a reference to the lowest layer
|
||||||
|
in a stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the lowest layer in the stack of
|
||||||
|
stream layers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
lowest_layer_type&
|
||||||
|
lowest_layer()
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get a reference to the lowest layer.
|
||||||
|
|
||||||
|
This function returns a reference to the lowest layer
|
||||||
|
in a stack of stream layers.
|
||||||
|
|
||||||
|
@return A reference to the lowest layer in the stack of
|
||||||
|
stream layers. Ownership is not transferred to the caller.
|
||||||
|
*/
|
||||||
|
lowest_layer_type const&
|
||||||
|
lowest_layer() const
|
||||||
|
{
|
||||||
|
return next_layer_.lowest_layer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancel pending operations.
|
||||||
|
|
||||||
|
This will cancel all of the asynchronous operations pending,
|
||||||
|
including pipelined writes that have not been started. Handlers for
|
||||||
|
canceled writes will be called with
|
||||||
|
`boost::asio::error::operation_aborted`.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
cancel()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
cancel(ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancel pending operations.
|
||||||
|
|
||||||
|
This will cancel all of the asynchronous operations pending,
|
||||||
|
including pipelined writes that have not been started. Handlers for
|
||||||
|
canceled writes will be called with
|
||||||
|
`boost::asio::error::operation_aborted`.
|
||||||
|
|
||||||
|
@param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
cancel(error_code& ec);
|
||||||
|
|
||||||
|
/** Read a HTTP message from the stream.
|
||||||
|
|
||||||
|
This function is used to read a single HTTP message from the stream.
|
||||||
|
The call will block until one of the followign conditions is true:
|
||||||
|
|
||||||
|
@li A message has been read.
|
||||||
|
|
||||||
|
@li An error occurred.
|
||||||
|
|
||||||
|
The operation is implemented in terms of zero or more calls to the
|
||||||
|
next layer's `read_some` function.
|
||||||
|
|
||||||
|
@param msg An object used to store the message. The previous
|
||||||
|
contents of the object will be overwritten.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
read(message<isRequest, Body, Headers>& msg)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
read(msg, ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read a HTTP message from the stream.
|
||||||
|
|
||||||
|
This function is used to read a single HTTP message from the stream.
|
||||||
|
The call will block until one of the followign conditions is true:
|
||||||
|
|
||||||
|
@li A message has been read.
|
||||||
|
|
||||||
|
@li An error occurred.
|
||||||
|
|
||||||
|
The operation is implemented in terms of zero or more calls to the
|
||||||
|
next layer's `read_some` function.
|
||||||
|
|
||||||
|
@param msg An object used to store the message. The previous
|
||||||
|
contents of the object will be overwritten.
|
||||||
|
|
||||||
|
@param ec Set to indicate what error occurred, if any.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
read(message<isRequest, Body, Headers>& msg,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start reading a HTTP message from the stream asynchronously.
|
||||||
|
|
||||||
|
This function is used to asynchronously read a single HTTP message
|
||||||
|
from the stream. The function call always returns immediately. The
|
||||||
|
asynchronous operation will continue until one of the following
|
||||||
|
conditions is true:
|
||||||
|
|
||||||
|
@li The message has been written.
|
||||||
|
|
||||||
|
@li An error occurred.
|
||||||
|
|
||||||
|
This operation is implemented in terms of zero or more calls to the
|
||||||
|
next layer's async_read_some function, and is known as a composed
|
||||||
|
operation. The program must ensure that the stream performs no other
|
||||||
|
read operations or any other composed operations that perform reads
|
||||||
|
until this operation completes.
|
||||||
|
|
||||||
|
@param msg An object used to store the message. The previous
|
||||||
|
contents of the object will be overwritten. Ownership of the message
|
||||||
|
is not transferred; the caller must guarantee that the object remains
|
||||||
|
valid until the handler is called.
|
||||||
|
|
||||||
|
@param handler The handler to be called when the request completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent
|
||||||
|
function signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
error_code const& error // result of operation
|
||||||
|
); @endcode
|
||||||
|
Regardless of whether the asynchronous operation completes
|
||||||
|
immediately or not, the handler will not be invoked from within
|
||||||
|
this function. Invocation of the handler will be performed in a
|
||||||
|
manner equivalent to using boost::asio::io_service::post().
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class ReadHandler>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
void_or_deduced
|
||||||
|
#else
|
||||||
|
auto
|
||||||
|
#endif
|
||||||
|
async_read(message<isRequest, Body, Headers>& msg,
|
||||||
|
ReadHandler&& handler);
|
||||||
|
|
||||||
|
/** Write a HTTP message to the stream.
|
||||||
|
|
||||||
|
This function is used to write a single HTTP message to the
|
||||||
|
stream. The call will block until one of the following conditions
|
||||||
|
is true:
|
||||||
|
|
||||||
|
@li The entire message is sent.
|
||||||
|
|
||||||
|
@li An error occurred.
|
||||||
|
|
||||||
|
If the semantics of the message require that the connection is
|
||||||
|
closed to indicate the end of the content body,
|
||||||
|
`boost::asio::error::eof` is thrown after the message is sent.
|
||||||
|
successfuly. The caller is responsible for actually closing the
|
||||||
|
connection. For regular TCP/IP streams this means shutting down the
|
||||||
|
send side, while SSL streams may call the SSL shutdown function.
|
||||||
|
|
||||||
|
@param msg The message to send.
|
||||||
|
|
||||||
|
@throws boost::system::system_error Thrown on failure.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
write(message<isRequest, Body, Headers> const& msg)
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
write(msg, ec);
|
||||||
|
if(ec)
|
||||||
|
throw boost::system::system_error{ec};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write a HTTP message to the stream.
|
||||||
|
|
||||||
|
This function is used to write a single HTTP message to the
|
||||||
|
stream. The call will block until one of the following conditions
|
||||||
|
is true:
|
||||||
|
|
||||||
|
@li The entire message is sent.
|
||||||
|
|
||||||
|
@li An error occurred.
|
||||||
|
|
||||||
|
If the semantics of the message require that the connection is
|
||||||
|
closed to indicate the end of the content body,
|
||||||
|
`boost::asio::error::eof` is returned after the message is sent.
|
||||||
|
successfuly. The caller is responsible for actually closing the
|
||||||
|
connection. For regular TCP/IP streams this means shutting down the
|
||||||
|
send side, while SSL streams may call the SSL shutdown function.
|
||||||
|
|
||||||
|
@param msg The message to send.
|
||||||
|
|
||||||
|
@param ec Set to the error, if any occurred.
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
write(message<isRequest, Body, Headers> const& msg,
|
||||||
|
error_code& ec);
|
||||||
|
|
||||||
|
/** Start pipelining a HTTP message to the stream asynchronously.
|
||||||
|
|
||||||
|
This function is used to queue a message to be sent on the stream.
|
||||||
|
Unlike the free function, this version will place the message on an
|
||||||
|
outgoing message queue if there is already a write pending.
|
||||||
|
|
||||||
|
If the semantics of the message require that the connection is
|
||||||
|
closed to indicate the end of the content body, the handler
|
||||||
|
is called with the error `boost::asio::error::eof` after the message
|
||||||
|
has been sent successfully. The caller is responsible for actually
|
||||||
|
closing the connection. For regular TCP/IP streams this means
|
||||||
|
shutting down the send side, while SSL streams may call the SSL
|
||||||
|
`async_shutdown` function.
|
||||||
|
|
||||||
|
@param msg The message to send. A copy of the message will be made.
|
||||||
|
|
||||||
|
@param handler The handler to be called when the request completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent
|
||||||
|
function signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
error_code const& error // result of operation
|
||||||
|
); @endcode
|
||||||
|
Regardless of whether the asynchronous operation completes
|
||||||
|
immediately or not, the handler will not be invoked from within
|
||||||
|
this function. Invocation of the handler will be performed in a
|
||||||
|
manner equivalent to using boost::asio::io_service::post().
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class WriteHandler>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
void_or_deduced
|
||||||
|
#else
|
||||||
|
auto
|
||||||
|
#endif
|
||||||
|
async_write(message<isRequest, Body, Headers> const& msg,
|
||||||
|
WriteHandler&& handler);
|
||||||
|
|
||||||
|
/** Start pipelining a HTTP message to the stream asynchronously.
|
||||||
|
|
||||||
|
This function is used to queue a message to be sent on the stream.
|
||||||
|
Unlike the free function, this version will place the message on an
|
||||||
|
outgoing message queue if there is already a write pending.
|
||||||
|
|
||||||
|
If the semantics of the message require that the connection is
|
||||||
|
closed to indicate the end of the content body, the handler
|
||||||
|
is called with the error boost::asio::error::eof. The caller is
|
||||||
|
responsible for actually closing the connection. For regular
|
||||||
|
TCP/IP streams this means shutting down the send side, while SSL
|
||||||
|
streams may call the SSL async_shutdown function.
|
||||||
|
|
||||||
|
@param msg The message to send. Ownership of the message, which
|
||||||
|
must be movable, is transferred to the implementation. The message
|
||||||
|
will not be destroyed until the asynchronous operation completes.
|
||||||
|
|
||||||
|
@param handler The handler to be called when the request completes.
|
||||||
|
Copies will be made of the handler as required. The equivalent
|
||||||
|
function signature of the handler must be:
|
||||||
|
@code void handler(
|
||||||
|
error_code const& error // result of operation
|
||||||
|
); @endcode
|
||||||
|
Regardless of whether the asynchronous operation completes
|
||||||
|
immediately or not, the handler will not be invoked from within
|
||||||
|
this function. Invocation of the handler will be performed in a
|
||||||
|
manner equivalent to using boost::asio::io_service::post().
|
||||||
|
*/
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class WriteHandler>
|
||||||
|
#if GENERATING_DOCS
|
||||||
|
void_or_deduced
|
||||||
|
#else
|
||||||
|
auto
|
||||||
|
#endif
|
||||||
|
async_write(message<isRequest, Body, Headers>&& msg,
|
||||||
|
WriteHandler&& handler);
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<bool, class, class, class> class read_op;
|
||||||
|
template<bool, class, class, class> class write_op;
|
||||||
|
|
||||||
|
void
|
||||||
|
cancel_all();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#include "http_stream.ipp"
|
||||||
|
|
||||||
|
#endif
|
||||||
419
examples/http_stream.ipp
Normal file
419
examples/http_stream.ipp
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_HTTP_STREAM_IPP_INCLUDED
|
||||||
|
#define BEAST_HTTP_STREAM_IPP_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/asio/async_completion.h>
|
||||||
|
#include <beast/asio/bind_handler.h>
|
||||||
|
#include <beast/asio/handler_alloc.h>
|
||||||
|
#include <beast/http/read.h>
|
||||||
|
#include <beast/http/type_check.h>
|
||||||
|
#include <beast/http/write.h>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class Handler>
|
||||||
|
class stream<NextLayer, Allocator>::read_op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
stream<NextLayer>& s;
|
||||||
|
message<isRequest, Body, Headers>& m;
|
||||||
|
Handler h;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, stream<NextLayer>& s_,
|
||||||
|
message<isRequest, Body, Headers>& m_)
|
||||||
|
: s(s_)
|
||||||
|
, m(m_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(boost_asio_handler_cont_helpers::
|
||||||
|
is_continuation(h))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
read_op(read_op&&) = default;
|
||||||
|
read_op(read_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
read_op(DeducedHandler&& h,
|
||||||
|
stream<NextLayer>& s, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), s,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(error_code const& ec, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(
|
||||||
|
std::size_t size, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(read_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f, read_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
read_op<isRequest, Body, Headers, Handler>::
|
||||||
|
operator()(error_code const& ec, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
d.state = 99;
|
||||||
|
beast::http::async_read(d.s.next_layer_,
|
||||||
|
d.s.rd_buf_, d.m, std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class Handler>
|
||||||
|
class stream<NextLayer, Allocator>::write_op : public op
|
||||||
|
{
|
||||||
|
using alloc_type =
|
||||||
|
handler_alloc<char, Handler>;
|
||||||
|
|
||||||
|
struct data
|
||||||
|
{
|
||||||
|
stream<NextLayer>& s;
|
||||||
|
message<isRequest, Body, Headers> m;
|
||||||
|
Handler h;
|
||||||
|
bool cont;
|
||||||
|
int state = 0;
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, stream<NextLayer>& s_,
|
||||||
|
message<isRequest, Body, Headers> const& m_,
|
||||||
|
bool cont_)
|
||||||
|
: s(s_)
|
||||||
|
, m(m_)
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(cont_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class DeducedHandler>
|
||||||
|
data(DeducedHandler&& h_, stream<NextLayer>& s_,
|
||||||
|
message<isRequest, Body, Headers>&& m_,
|
||||||
|
bool cont_)
|
||||||
|
: s(s_)
|
||||||
|
, m(std::move(m_))
|
||||||
|
, h(std::forward<DeducedHandler>(h_))
|
||||||
|
, cont(cont_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<data> d_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
write_op(write_op&&) = default;
|
||||||
|
write_op(write_op const&) = default;
|
||||||
|
|
||||||
|
template<class DeducedHandler, class... Args>
|
||||||
|
write_op(DeducedHandler&& h,
|
||||||
|
stream<NextLayer>& s, Args&&... args)
|
||||||
|
: d_(std::allocate_shared<data>(alloc_type{h},
|
||||||
|
std::forward<DeducedHandler>(h), s,
|
||||||
|
std::forward<Args>(args)...))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
operator()() override
|
||||||
|
{
|
||||||
|
(*this)(error_code{}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cancel() override;
|
||||||
|
|
||||||
|
void operator()(error_code const& ec, bool again = true);
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_allocate(
|
||||||
|
std::size_t size, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
allocate(size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_deallocate(
|
||||||
|
void* p, std::size_t size, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_alloc_helpers::
|
||||||
|
deallocate(p, size, op->d_->h);
|
||||||
|
}
|
||||||
|
|
||||||
|
friend
|
||||||
|
auto asio_handler_is_continuation(write_op* op)
|
||||||
|
{
|
||||||
|
return op->d_->cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Function>
|
||||||
|
friend
|
||||||
|
auto asio_handler_invoke(Function&& f, write_op* op)
|
||||||
|
{
|
||||||
|
return boost_asio_handler_invoke_helpers::
|
||||||
|
invoke(f, op->d_->h);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
write_op<isRequest, Body, Headers, Handler>::
|
||||||
|
cancel()
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.s.get_io_service().post(
|
||||||
|
bind_handler(std::move(*this),
|
||||||
|
boost::asio::error::operation_aborted));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers, class Handler>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
write_op<isRequest, Body, Headers, Handler>::
|
||||||
|
operator()(error_code const& ec, bool again)
|
||||||
|
{
|
||||||
|
auto& d = *d_;
|
||||||
|
d.cont = d.cont || again;
|
||||||
|
while(! ec && d.state != 99)
|
||||||
|
{
|
||||||
|
switch(d.state)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
d.state = 99;
|
||||||
|
beast::http::async_write(d.s.next_layer_,
|
||||||
|
d.m, std::move(*this));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.h(ec);
|
||||||
|
if(! d.s.wr_q_.empty())
|
||||||
|
{
|
||||||
|
auto& op = d.s.wr_q_.front();
|
||||||
|
op();
|
||||||
|
// VFALCO Use allocator
|
||||||
|
delete &op;
|
||||||
|
d.s.wr_q_.pop_front();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
d.s.wr_active_ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
~stream()
|
||||||
|
{
|
||||||
|
// Can't destroy with pending operations!
|
||||||
|
assert(wr_q_.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<class... Args>
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
stream(Args&&... args)
|
||||||
|
: next_layer_(std::forward<Args>(args)...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
cancel(error_code& ec)
|
||||||
|
{
|
||||||
|
cancel_all();
|
||||||
|
lowest_layer().cancel(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
read(message<isRequest, Body, Headers>& msg,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
beast::http::read(next_layer_, rd_buf_, msg, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class ReadHandler>
|
||||||
|
auto
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
async_read(message<isRequest, Body, Headers>& msg,
|
||||||
|
ReadHandler&& handler)
|
||||||
|
{
|
||||||
|
async_completion<
|
||||||
|
ReadHandler, void(error_code)
|
||||||
|
> completion(handler);
|
||||||
|
read_op<isRequest, Body, Headers,
|
||||||
|
decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, msg};
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
write(message<isRequest, Body, Headers> const& msg,
|
||||||
|
error_code& ec)
|
||||||
|
{
|
||||||
|
beast::http::write(next_layer_, msg, ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class WriteHandler>
|
||||||
|
auto
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
async_write(message<isRequest, Body, Headers> const& msg,
|
||||||
|
WriteHandler&& handler)
|
||||||
|
{
|
||||||
|
async_completion<
|
||||||
|
WriteHandler, void(error_code)> completion(handler);
|
||||||
|
auto const cont = wr_active_ ||
|
||||||
|
boost_asio_handler_cont_helpers::is_continuation(handler);
|
||||||
|
if(! wr_active_)
|
||||||
|
{
|
||||||
|
wr_active_ = true;
|
||||||
|
write_op<isRequest, Body, Headers,
|
||||||
|
decltype(completion.handler)>{
|
||||||
|
completion.handler, *this, msg, cont }();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// VFALCO Use allocator
|
||||||
|
wr_q_.push_back(*new write_op<isRequest, Body, Headers,
|
||||||
|
decltype(completion.handler)>(
|
||||||
|
completion.handler, *this, msg, cont));
|
||||||
|
}
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
template<bool isRequest, class Body, class Headers,
|
||||||
|
class WriteHandler>
|
||||||
|
auto
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
async_write(message<isRequest, Body, Headers>&& msg,
|
||||||
|
WriteHandler&& handler)
|
||||||
|
{
|
||||||
|
async_completion<
|
||||||
|
WriteHandler, void(error_code)> completion(handler);
|
||||||
|
auto const cont = wr_active_ ||
|
||||||
|
boost_asio_handler_cont_helpers::is_continuation(handler);
|
||||||
|
if(! wr_active_)
|
||||||
|
{
|
||||||
|
wr_active_ = true;
|
||||||
|
write_op<isRequest, Body, Headers,
|
||||||
|
decltype(completion.handler)>{completion.handler,
|
||||||
|
*this, std::move(msg), cont}();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// VFALCO Use allocator
|
||||||
|
wr_q_.push_back(*new write_op<isRequest, Body, Headers,
|
||||||
|
decltype(completion.handler)>(completion.handler,
|
||||||
|
*this, std::move(msg), cont));
|
||||||
|
}
|
||||||
|
return completion.result.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class NextLayer, class Allocator>
|
||||||
|
void
|
||||||
|
stream<NextLayer, Allocator>::
|
||||||
|
cancel_all()
|
||||||
|
{
|
||||||
|
for(auto it = wr_q_.begin(); it != wr_q_.end();)
|
||||||
|
{
|
||||||
|
auto& op = *it++;
|
||||||
|
op.cancel();
|
||||||
|
// VFALCO Use allocator
|
||||||
|
delete &op;
|
||||||
|
}
|
||||||
|
wr_q_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
170
examples/http_sync_server.h
Normal file
170
examples/http_sync_server.h
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
|
||||||
|
#define BEAST_EXAMPLE_HTTP_SYNC_SERVER_H_INCLUDED
|
||||||
|
|
||||||
|
#include "file_body.h"
|
||||||
|
#include "http_stream.h"
|
||||||
|
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
class http_sync_server
|
||||||
|
{
|
||||||
|
using endpoint_type = boost::asio::ip::tcp::endpoint;
|
||||||
|
using address_type = boost::asio::ip::address;
|
||||||
|
using socket_type = boost::asio::ip::tcp::socket;
|
||||||
|
|
||||||
|
using req_type = request<string_body>;
|
||||||
|
using resp_type = response<file_body>;
|
||||||
|
|
||||||
|
boost::asio::io_service ios_;
|
||||||
|
socket_type sock_;
|
||||||
|
boost::asio::ip::tcp::acceptor acceptor_;
|
||||||
|
std::string root_;
|
||||||
|
std::thread thread_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
http_sync_server(endpoint_type const& ep,
|
||||||
|
std::string const& root)
|
||||||
|
: sock_(ios_)
|
||||||
|
, acceptor_(ios_)
|
||||||
|
, root_(root)
|
||||||
|
{
|
||||||
|
acceptor_.open(ep.protocol());
|
||||||
|
acceptor_.bind(ep);
|
||||||
|
acceptor_.listen(
|
||||||
|
boost::asio::socket_base::max_connections);
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&http_sync_server::on_accept, this,
|
||||||
|
beast::asio::placeholders::error));
|
||||||
|
thread_ = std::thread{[&]{ ios_.run(); }};
|
||||||
|
}
|
||||||
|
|
||||||
|
~http_sync_server()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
ios_.dispatch(
|
||||||
|
[&]{ acceptor_.close(ec); });
|
||||||
|
thread_.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fail(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
std::cerr <<
|
||||||
|
what << ": " << ec.message() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
maybe_throw(error_code ec, std::string what)
|
||||||
|
{
|
||||||
|
if(ec)
|
||||||
|
{
|
||||||
|
fail(ec, what);
|
||||||
|
throw ec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
on_accept(error_code ec)
|
||||||
|
{
|
||||||
|
if(! acceptor_.is_open())
|
||||||
|
return;
|
||||||
|
maybe_throw(ec, "accept");
|
||||||
|
static int id_ = 0;
|
||||||
|
std::thread{
|
||||||
|
[
|
||||||
|
id = ++id_,
|
||||||
|
this,
|
||||||
|
sock = std::move(sock_),
|
||||||
|
work = boost::asio::io_service::work{ios_}
|
||||||
|
]() mutable
|
||||||
|
{
|
||||||
|
do_peer(id, std::move(sock));
|
||||||
|
}}.detach();
|
||||||
|
acceptor_.async_accept(sock_,
|
||||||
|
std::bind(&http_sync_server::on_accept, this,
|
||||||
|
asio::placeholders::error));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
fail(int id, error_code const& ec)
|
||||||
|
{
|
||||||
|
if(ec != boost::asio::error::operation_aborted &&
|
||||||
|
ec != boost::asio::error::eof)
|
||||||
|
std::cerr <<
|
||||||
|
"#" << std::to_string(id) << " " << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
do_peer(int id, socket_type&& sock)
|
||||||
|
{
|
||||||
|
http::stream<socket_type> hs(std::move(sock));
|
||||||
|
error_code ec;
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
req_type req;
|
||||||
|
hs.read(req, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
auto path = req.url;
|
||||||
|
if(path == "/")
|
||||||
|
path = "/index.html";
|
||||||
|
path = root_ + path;
|
||||||
|
if(! boost::filesystem::exists(path))
|
||||||
|
{
|
||||||
|
response<string_body> resp(
|
||||||
|
{404, "Not Found", req.version});
|
||||||
|
resp.headers.replace("Server", "http_sync_server");
|
||||||
|
resp.body = "The file '" + path + "' was not found";
|
||||||
|
hs.write(resp, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
response<file_body> resp(
|
||||||
|
{200, "OK", req.version});
|
||||||
|
resp.headers.replace("Server", "http_sync_server");
|
||||||
|
resp.headers.replace("Content-Type", "text/html");
|
||||||
|
resp.body = path;
|
||||||
|
hs.write(resp, ec);
|
||||||
|
if(ec)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fail(id, ec);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
49
examples/sig_wait.h
Normal file
49
examples/sig_wait.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
|
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||||
|
|
||||||
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
purpose with or without fee is hereby granted, provided that the above
|
||||||
|
copyright notice and this permission notice appear in all copies.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*/
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
#ifndef BEAST_EXAMPLE_SIG_WAIT_H_INCLUDED
|
||||||
|
#define BEAST_EXAMPLE_SIG_WAIT_H_INCLUDED
|
||||||
|
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
// Block until SIGINT or SIGTERM
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
sig_wait()
|
||||||
|
{
|
||||||
|
boost::asio::io_service ios;
|
||||||
|
boost::asio::signal_set signals(
|
||||||
|
ios, SIGINT, SIGTERM);
|
||||||
|
std::mutex m;
|
||||||
|
bool stop = false;
|
||||||
|
std::condition_variable cv;
|
||||||
|
signals.async_wait(
|
||||||
|
[&](boost::system::error_code const&, int)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
stop = true;
|
||||||
|
cv.notify_one();
|
||||||
|
});
|
||||||
|
std::unique_lock<std::mutex> lock(m);
|
||||||
|
cv.wait(lock, [&]{ return stop; });
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -17,15 +17,12 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <beast/http/tests/urls_large_data.h>
|
#include "urls_large_data.h"
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace http {
|
|
||||||
|
|
||||||
// Data from Alexa top 1 million sites
|
// Data from Alexa top 1 million sites
|
||||||
// http://s3.amazonaws.com/alexa-static/top-1m.csv.zip
|
// http://s3.amazonaws.com/alexa-static/top-1m.csv.zip
|
||||||
//
|
//
|
||||||
std::vector <char const*> const&
|
std::vector<char const*> const&
|
||||||
urls_large_data()
|
urls_large_data()
|
||||||
{
|
{
|
||||||
static std::vector <char const*> const urls ({
|
static std::vector <char const*> const urls ({
|
||||||
@@ -10032,7 +10029,3 @@ urls_large_data()
|
|||||||
|
|
||||||
return urls;
|
return urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -17,18 +17,12 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_URLS_LARGE_DATA_H_INCLUDED
|
#ifndef URLS_LARGE_DATA_H_INCLUDED
|
||||||
#define BEAST_HTTP_URLS_LARGE_DATA_H_INCLUDED
|
#define URLS_LARGE_DATA_H_INCLUDED
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace beast {
|
std::vector<char const*> const&
|
||||||
namespace http {
|
|
||||||
|
|
||||||
std::vector <char const*> const&
|
|
||||||
urls_large_data();
|
urls_large_data();
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -14,6 +14,7 @@ unit-test all :
|
|||||||
asio.cpp
|
asio.cpp
|
||||||
async_completion.cpp
|
async_completion.cpp
|
||||||
basic_streambuf.cpp
|
basic_streambuf.cpp
|
||||||
|
../basic_headers.cpp
|
||||||
bind_handler.cpp
|
bind_handler.cpp
|
||||||
buffers_adapter.cpp
|
buffers_adapter.cpp
|
||||||
buffers_debug.cpp
|
buffers_debug.cpp
|
||||||
|
|||||||
63
test/basic_headers.cpp
Normal file
63
test/basic_headers.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot com)
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
//
|
||||||
|
|
||||||
|
// Test that header file is self-contained.
|
||||||
|
#include <beast/http/basic_headers.h>
|
||||||
|
|
||||||
|
#include <beast/unit_test/suite.h>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
class basic_headers_test : public unit_test::suite
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<class Allocator>
|
||||||
|
using bha = basic_headers<Allocator>;
|
||||||
|
|
||||||
|
using bh = basic_headers<std::allocator<char>>;
|
||||||
|
|
||||||
|
template<class Allocator>
|
||||||
|
static
|
||||||
|
void
|
||||||
|
fill(std::size_t n, basic_headers<Allocator>& h)
|
||||||
|
{
|
||||||
|
for(std::size_t i = 1; i<= n; ++i)
|
||||||
|
h.insert(std::to_string(i), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void testHeaders()
|
||||||
|
{
|
||||||
|
bh h1;
|
||||||
|
expect(h1.empty());
|
||||||
|
fill(1, h1);
|
||||||
|
expect(h1.size() == 1);
|
||||||
|
bh h2;
|
||||||
|
h2 = h1;
|
||||||
|
expect(h2.size() == 1);
|
||||||
|
h2.insert("2", "2");
|
||||||
|
expect(std::distance(h2.begin(), h2.end()) == 2);
|
||||||
|
h1 = std::move(h2);
|
||||||
|
expect(h1.size() == 2);
|
||||||
|
expect(h2.size() == 0);
|
||||||
|
bh h3(std::move(h1));
|
||||||
|
expect(h3.size() == 2);
|
||||||
|
expect(h1.size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() override
|
||||||
|
{
|
||||||
|
testHeaders();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BEAST_DEFINE_TESTSUITE(basic_headers,http,beast);
|
||||||
|
|
||||||
|
} // test
|
||||||
|
} // asio
|
||||||
|
} // beast
|
||||||
Reference in New Issue
Block a user