mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-25 13:35:54 +00:00
HTTP message and parser improvements:
* streambuf wrapper supports rvalue move * message class holds a complete HTTP message * body class holds the HTTP content body * headers class holds RFC-compliant HTTP headers * basic_parser provides class interface to joyent's http-parser * parser class parses into a message object * Remove unused http get client free function * unit test for parsing malformed messages
This commit is contained in:
committed by
Tom Ritchford
parent
d81154bf6c
commit
b968821cc1
@@ -17,22 +17,33 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#ifndef BEAST_HTTP_GET_H_INCLUDED
|
#ifndef BEAST_ASIO_BASIC_STREAMBUF_H_INCLUDED
|
||||||
#define BEAST_HTTP_GET_H_INCLUDED
|
#define BEAST_ASIO_BASIC_STREAMBUF_H_INCLUDED
|
||||||
|
|
||||||
#include <boost/system/error_code.hpp>
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <vector>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace asio {
|
||||||
|
|
||||||
/** Perform simple HTTP GET to retrieve a resource as a string. */
|
template <class Alloc = std::allocator <char>>
|
||||||
std::pair <std::string, boost::system::error_code>
|
class basic_streambuf : private Alloc
|
||||||
get (std::string const& url_string);
|
{
|
||||||
|
private:
|
||||||
|
typedef std::allocator_traits <Alloc> alloc_traits;
|
||||||
|
std::vector <boost::asio::mutable_buffer> bufs_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~basic_streambuf()
|
||||||
|
{
|
||||||
|
for (auto const& buf : bufs_)
|
||||||
|
alloc_traits::deallocate (
|
||||||
|
boost::asio::buffer_cast<char const*>(buf));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
} // asio
|
||||||
|
} // beast
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -21,18 +21,17 @@
|
|||||||
#include <BeastConfig.h>
|
#include <BeastConfig.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <beast/http/impl/basic_parser.cpp>
|
||||||
#include <beast/http/impl/basic_url.cpp>
|
#include <beast/http/impl/basic_url.cpp>
|
||||||
#include <beast/http/impl/get.cpp>
|
|
||||||
#include <beast/http/impl/joyent_parser.cpp>
|
#include <beast/http/impl/joyent_parser.cpp>
|
||||||
#include <beast/http/impl/method.cpp>
|
#include <beast/http/impl/method.cpp>
|
||||||
#include <beast/http/impl/ParsedURL.cpp>
|
#include <beast/http/impl/ParsedURL.cpp>
|
||||||
#include <beast/http/impl/parser.cpp>
|
|
||||||
#include <beast/http/impl/raw_parser.cpp>
|
#include <beast/http/impl/raw_parser.cpp>
|
||||||
#include <beast/http/impl/URL.cpp>
|
#include <beast/http/impl/URL.cpp>
|
||||||
|
|
||||||
#include <beast/http/tests/basic_message.test.cpp>
|
|
||||||
#include <beast/http/tests/basic_url.test.cpp>
|
#include <beast/http/tests/basic_url.test.cpp>
|
||||||
#include <beast/http/tests/client_session.test.cpp>
|
#include <beast/http/tests/client_session.test.cpp>
|
||||||
|
#include <beast/http/tests/parser.test.cpp>
|
||||||
#include <beast/http/tests/ParsedURL.cpp>
|
#include <beast/http/tests/ParsedURL.cpp>
|
||||||
#include <beast/http/tests/rfc2616.test.cpp>
|
#include <beast/http/tests/rfc2616.test.cpp>
|
||||||
#include <beast/http/tests/urls_large_data.cpp>
|
#include <beast/http/tests/urls_large_data.cpp>
|
||||||
|
|||||||
@@ -1,511 +0,0 @@
|
|||||||
//------------------------------------------------------------------------------
|
|
||||||
/*
|
|
||||||
This file is part of rippled: https://github.com/ripple/rippled
|
|
||||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
|
||||||
|
|
||||||
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_H_INCLUDED
|
|
||||||
#define BEAST_HTTP_MESSAGE_H_INCLUDED
|
|
||||||
|
|
||||||
#include <beast/http/method.h>
|
|
||||||
#include <beast/http/parser.h>
|
|
||||||
#include <beast/utility/ci_char_traits.h>
|
|
||||||
#include <boost/intrusive/list.hpp>
|
|
||||||
#include <boost/intrusive/set.hpp>
|
|
||||||
#include <boost/system/error_code.hpp>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cctype>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace http {
|
|
||||||
|
|
||||||
class basic_message
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
class parser;
|
|
||||||
|
|
||||||
typedef boost::system::error_code error_code;
|
|
||||||
|
|
||||||
private:
|
|
||||||
class element
|
|
||||||
: public boost::intrusive::set_base_hook <
|
|
||||||
boost::intrusive::link_mode <
|
|
||||||
boost::intrusive::normal_link>
|
|
||||||
>
|
|
||||||
, public boost::intrusive::list_base_hook <
|
|
||||||
boost::intrusive::link_mode <
|
|
||||||
boost::intrusive::normal_link>
|
|
||||||
>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
element (std::string const& f, std::string const& v)
|
|
||||||
: field (f)
|
|
||||||
, value (v)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string field;
|
|
||||||
std::string value;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct less : private beast::ci_less
|
|
||||||
{
|
|
||||||
template <class String>
|
|
||||||
bool
|
|
||||||
operator() (String const& lhs, element const& rhs) const
|
|
||||||
{
|
|
||||||
return beast::ci_less::operator() (lhs, rhs.field);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class String>
|
|
||||||
bool
|
|
||||||
operator() (element const& lhs, String const& rhs) const
|
|
||||||
{
|
|
||||||
return beast::ci_less::operator() (lhs.field, rhs);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class headers_t : private less
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
typedef boost::intrusive::make_list <element,
|
|
||||||
boost::intrusive::constant_time_size <false>
|
|
||||||
>::type list_t;
|
|
||||||
|
|
||||||
typedef boost::intrusive::make_set <element,
|
|
||||||
boost::intrusive::constant_time_size <true>
|
|
||||||
>::type set_t;
|
|
||||||
|
|
||||||
list_t list_;
|
|
||||||
set_t set_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
typedef list_t::const_iterator iterator;
|
|
||||||
|
|
||||||
~headers_t()
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
headers_t() = default;
|
|
||||||
|
|
||||||
headers_t (headers_t&& other)
|
|
||||||
: list_ (std::move(other.list_))
|
|
||||||
, set_ (std::move(other.set_))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
headers_t (headers_t const& other)
|
|
||||||
{
|
|
||||||
for (auto const& e : other.list_)
|
|
||||||
append (e.field, e.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
headers_t&
|
|
||||||
operator= (headers_t&& other)
|
|
||||||
{
|
|
||||||
list_ = std::move(other.list_);
|
|
||||||
set_ = std::move(other.set_);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
headers_t&
|
|
||||||
operator= (headers_t const& other)
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
for (auto const& e : other.list_)
|
|
||||||
append (e.field, e.value);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
clear()
|
|
||||||
{
|
|
||||||
for (auto iter (list_.begin()); iter != list_.end();)
|
|
||||||
{
|
|
||||||
element* const p (&*iter);
|
|
||||||
++iter;
|
|
||||||
delete p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator
|
|
||||||
begin() const
|
|
||||||
{
|
|
||||||
return list_.cbegin();
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator
|
|
||||||
end() const
|
|
||||||
{
|
|
||||||
return list_.cend();
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator
|
|
||||||
cbegin() const
|
|
||||||
{
|
|
||||||
return list_.cbegin();
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator
|
|
||||||
cend() const
|
|
||||||
{
|
|
||||||
return list_.cend();
|
|
||||||
}
|
|
||||||
|
|
||||||
iterator
|
|
||||||
find (std::string const& field) const
|
|
||||||
{
|
|
||||||
auto const iter (set_.find (field,
|
|
||||||
std::cref(static_cast<less const&>(*this))));
|
|
||||||
if (iter == set_.end())
|
|
||||||
return list_.end();
|
|
||||||
return list_.iterator_to (*iter);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string const&
|
|
||||||
operator[] (std::string const& field) const
|
|
||||||
{
|
|
||||||
static std::string none;
|
|
||||||
auto const found (find (field));
|
|
||||||
if (found == end())
|
|
||||||
return none;
|
|
||||||
return found->value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
append (std::string const& field, std::string const& value)
|
|
||||||
{
|
|
||||||
set_t::insert_commit_data d;
|
|
||||||
auto const result (set_.insert_check (field,
|
|
||||||
std::cref(static_cast<less const&>(*this)), 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->value);
|
|
||||||
cur.reserve (cur.size() + 1 + value.size());
|
|
||||||
cur.append (1, ',');
|
|
||||||
cur.append (value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
bool request_;
|
|
||||||
|
|
||||||
// request
|
|
||||||
beast::http::method_t method_;
|
|
||||||
std::string url_;
|
|
||||||
|
|
||||||
// response
|
|
||||||
int status_;
|
|
||||||
std::string reason_;
|
|
||||||
|
|
||||||
// message
|
|
||||||
std::pair<int, int> version_;
|
|
||||||
bool keep_alive_;
|
|
||||||
bool upgrade_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
~basic_message() = default;
|
|
||||||
|
|
||||||
basic_message()
|
|
||||||
: request_ (true)
|
|
||||||
, method_ (beast::http::method_t::http_get)
|
|
||||||
, url_ ("/")
|
|
||||||
, status_ (200)
|
|
||||||
, version_ (1, 1)
|
|
||||||
, keep_alive_ (false)
|
|
||||||
, upgrade_ (false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_message (basic_message&& other)
|
|
||||||
: request_ (true)
|
|
||||||
, method_ (std::move(other.method_))
|
|
||||||
, url_ (std::move(other.url_))
|
|
||||||
, status_ (other.status_)
|
|
||||||
, reason_ (std::move(other.reason_))
|
|
||||||
, version_ (other.version_)
|
|
||||||
, keep_alive_ (other.keep_alive_)
|
|
||||||
, upgrade_ (other.upgrade_)
|
|
||||||
, headers (std::move(other.headers))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
request() const
|
|
||||||
{
|
|
||||||
return request_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
request (bool value)
|
|
||||||
{
|
|
||||||
request_ = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request
|
|
||||||
|
|
||||||
void
|
|
||||||
method (beast::http::method_t http_method)
|
|
||||||
{
|
|
||||||
method_ = http_method;
|
|
||||||
}
|
|
||||||
|
|
||||||
beast::http::method_t
|
|
||||||
method() const
|
|
||||||
{
|
|
||||||
return method_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
url (std::string const& s)
|
|
||||||
{
|
|
||||||
url_ = s;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string const&
|
|
||||||
url() const
|
|
||||||
{
|
|
||||||
return url_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns `false` if this is not the last message.
|
|
||||||
When keep_alive returns `false`:
|
|
||||||
* Server roles respond with a "Connection: close" header.
|
|
||||||
* Client roles close the connection.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
keep_alive() const
|
|
||||||
{
|
|
||||||
return keep_alive_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Set the keep_alive setting. */
|
|
||||||
void
|
|
||||||
keep_alive (bool value)
|
|
||||||
{
|
|
||||||
keep_alive_ = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns `true` if this is an HTTP Upgrade message.
|
|
||||||
@note Upgrade messages have no content body.
|
|
||||||
*/
|
|
||||||
bool
|
|
||||||
upgrade() const
|
|
||||||
{
|
|
||||||
return upgrade_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Set the upgrade setting. */
|
|
||||||
void
|
|
||||||
upgrade (bool value)
|
|
||||||
{
|
|
||||||
upgrade_ = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
status() const
|
|
||||||
{
|
|
||||||
return status_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
status (int code)
|
|
||||||
{
|
|
||||||
status_ = code;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string const&
|
|
||||||
reason() const
|
|
||||||
{
|
|
||||||
return reason_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
reason (std::string const& text)
|
|
||||||
{
|
|
||||||
reason_ = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message
|
|
||||||
|
|
||||||
void
|
|
||||||
version (int major, int minor)
|
|
||||||
{
|
|
||||||
version_ = std::make_pair (major, minor);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<int, int>
|
|
||||||
version() const
|
|
||||||
{
|
|
||||||
return version_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Memberspace
|
|
||||||
headers_t headers;
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class basic_message::parser : public beast::http::parser
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
basic_message& message_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
parser (basic_message& message, bool request)
|
|
||||||
: beast::http::parser (request)
|
|
||||||
, message_ (message)
|
|
||||||
{
|
|
||||||
message_.request(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void
|
|
||||||
on_start () override
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
on_request (method_t method, std::string const& url,
|
|
||||||
int major, int minor, bool keep_alive, bool upgrade) override
|
|
||||||
{
|
|
||||||
message_.method (method);
|
|
||||||
message_.url (url);
|
|
||||||
message_.version (major, minor);
|
|
||||||
message_.keep_alive(keep_alive);
|
|
||||||
message_.upgrade(upgrade);
|
|
||||||
return upgrade ? false : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
on_response (int status, std::string const& text,
|
|
||||||
int major, int minor, bool keep_alive, bool upgrade) override
|
|
||||||
{
|
|
||||||
message_.status (status);
|
|
||||||
message_.reason (text);
|
|
||||||
message_.version (major, minor);
|
|
||||||
message_.keep_alive(keep_alive);
|
|
||||||
message_.upgrade(upgrade);
|
|
||||||
return upgrade ? false : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
on_field (std::string const& field, std::string const& value) override
|
|
||||||
{
|
|
||||||
message_.headers.append (field, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
on_body (void const* data, std::size_t bytes) override
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
on_complete() override
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template <class AsioStreamBuf>
|
|
||||||
void
|
|
||||||
xwrite (AsioStreamBuf& stream, std::string const& s)
|
|
||||||
{
|
|
||||||
stream.commit (boost::asio::buffer_copy (
|
|
||||||
stream.prepare (s.size()), boost::asio::buffer(s)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class AsioStreamBuf>
|
|
||||||
void
|
|
||||||
xwrite (AsioStreamBuf& 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 AsioStreamBuf>
|
|
||||||
void
|
|
||||||
xwrite (AsioStreamBuf& stream, basic_message const& m)
|
|
||||||
{
|
|
||||||
if (m.request())
|
|
||||||
{
|
|
||||||
xwrite (stream, to_string(m.method()));
|
|
||||||
xwrite (stream, " ");
|
|
||||||
xwrite (stream, m.url());
|
|
||||||
xwrite (stream, " HTTP/");
|
|
||||||
xwrite (stream, std::to_string(m.version().first));
|
|
||||||
xwrite (stream, ".");
|
|
||||||
xwrite (stream, std::to_string(m.version().second));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xwrite (stream, "HTTP/");
|
|
||||||
xwrite (stream, std::to_string(m.version().first));
|
|
||||||
xwrite (stream, ".");
|
|
||||||
xwrite (stream, std::to_string(m.version().second));
|
|
||||||
xwrite (stream, " ");
|
|
||||||
xwrite (stream, std::to_string(m.status()));
|
|
||||||
xwrite (stream, " ");
|
|
||||||
xwrite (stream, m.reason());
|
|
||||||
}
|
|
||||||
xwrite (stream, "\r\n");
|
|
||||||
for (auto const& header : m.headers)
|
|
||||||
{
|
|
||||||
xwrite (stream, header.field);
|
|
||||||
xwrite (stream, ": ");
|
|
||||||
xwrite (stream, header.value);
|
|
||||||
xwrite (stream, "\r\n");
|
|
||||||
}
|
|
||||||
xwrite (stream, "\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <class = void>
|
|
||||||
std::string
|
|
||||||
to_string (basic_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";
|
|
||||||
for (auto const& header : m.headers)
|
|
||||||
ss << header.field << ": " << header.value << "\r\n";
|
|
||||||
ss << "\r\n";
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // http
|
|
||||||
} // beast
|
|
||||||
|
|
||||||
#endif
|
|
||||||
259
beast/http/basic_parser.h
Normal file
259
beast/http/basic_parser.h
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_PARSER_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_BASIC_PARSER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/method.h>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <beast/utility/noexcept.h>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
|
||||||
|
namespace joyent {
|
||||||
|
struct http_parser;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
class basic_parser
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// These structures must exactly match the
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef int (*data_cb_t) (
|
||||||
|
state_t*, const char *at, size_t length);
|
||||||
|
typedef int (*cb_t) (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;
|
||||||
|
};
|
||||||
|
|
||||||
|
char state_ [sizeof(state_t)];
|
||||||
|
char hooks_ [sizeof(hooks_t)];
|
||||||
|
|
||||||
|
bool complete_ = false;
|
||||||
|
std::string url_;
|
||||||
|
std::string status_;
|
||||||
|
std::string field_;
|
||||||
|
std::string value_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef boost::system::error_code error_code;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
~basic_parser() = default;
|
||||||
|
|
||||||
|
/** Construct the parser.
|
||||||
|
If `request` is `true` this sets up the parser to
|
||||||
|
process an HTTP request.
|
||||||
|
*/
|
||||||
|
explicit
|
||||||
|
basic_parser (bool request) noexcept;
|
||||||
|
|
||||||
|
basic_parser&
|
||||||
|
operator= (basic_parser&& other);
|
||||||
|
|
||||||
|
/** Returns `true` if parsing is complete.
|
||||||
|
This is only defined when no errors have been returned.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
complete() const noexcept
|
||||||
|
{
|
||||||
|
return complete_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the error if write or write_eof returned false. */
|
||||||
|
error_code
|
||||||
|
error() const noexcept;
|
||||||
|
|
||||||
|
/** 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 <bool, std::size_t>
|
||||||
|
write (void const* data, std::size_t bytes);
|
||||||
|
|
||||||
|
/** Write a set of buffer data to the parser.
|
||||||
|
The return value includes the error code if any,
|
||||||
|
and the number of bytes consumed in the input sequence.
|
||||||
|
@param buffers The buffers to write. These must meet the
|
||||||
|
requirements of ConstBufferSequence.
|
||||||
|
@return A pair with bool success, and the number of bytes consumed.
|
||||||
|
*/
|
||||||
|
template <class ConstBufferSequence>
|
||||||
|
std::pair <bool, std::size_t>
|
||||||
|
write (ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
std::pair <bool, std::size_t> result (true, 0);
|
||||||
|
for (auto const& buffer : buffers)
|
||||||
|
{
|
||||||
|
std::size_t bytes_consumed;
|
||||||
|
std::tie (result.first, bytes_consumed) =
|
||||||
|
write (boost::asio::buffer_cast <void const*> (buffer),
|
||||||
|
boost::asio::buffer_size (buffer));
|
||||||
|
if (! result.first)
|
||||||
|
break;
|
||||||
|
result.second += bytes_consumed;
|
||||||
|
if (complete())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called to indicate the end of file.
|
||||||
|
HTTP needs to know where the end of the stream is. For example,
|
||||||
|
sometimes servers send responses without Content-Length and
|
||||||
|
expect the client to consume input (for the body) until EOF.
|
||||||
|
Callbacks and errors will still be processed as usual.
|
||||||
|
@note This is typically called when a socket read returns eof.
|
||||||
|
@return `true` if the message is complete.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
write_eof();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/** Called once when a new message begins. */
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
on_start() = 0;
|
||||||
|
|
||||||
|
/** Called for each header field. */
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
on_field (std::string const& field, std::string const& value) = 0;
|
||||||
|
|
||||||
|
/** 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.
|
||||||
|
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
|
||||||
|
@param major The HTTP major version number
|
||||||
|
@param minor The HTTP minor version number
|
||||||
|
@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
|
||||||
|
on_body (void const* data, std::size_t bytes) = 0;
|
||||||
|
|
||||||
|
/** Called once when the message is complete. */
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
on_complete() = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void check_header();
|
||||||
|
|
||||||
|
int do_message_start ();
|
||||||
|
int do_url (char const* in, std::size_t bytes);
|
||||||
|
int do_status (char const* in, std::size_t bytes);
|
||||||
|
int do_header_field (char const* in, std::size_t bytes);
|
||||||
|
int do_header_value (char const* in, std::size_t bytes);
|
||||||
|
int do_headers_complete ();
|
||||||
|
int do_body (char const* in, std::size_t bytes);
|
||||||
|
int do_message_complete ();
|
||||||
|
|
||||||
|
static int cb_message_start (joyent::http_parser*);
|
||||||
|
static int cb_url (joyent::http_parser*, char const*, std::size_t);
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
static int cb_message_complete (joyent::http_parser*);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
133
beast/http/body.h
Normal file
133
beast/http/body.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_BODY_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_BODY_H_INCLUDED
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
|
#include <boost/asio/streambuf.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
#include <beast/cxx14/memory.h> // <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
/** Container for the HTTP content-body. */
|
||||||
|
class body
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
typedef boost::asio::streambuf buffer_type;
|
||||||
|
|
||||||
|
// Hack: use unique_ptr because streambuf cant be moved
|
||||||
|
std::unique_ptr <buffer_type> buf_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef buffer_type::const_buffers_type const_buffers_type;
|
||||||
|
|
||||||
|
body();
|
||||||
|
body (body&& other);
|
||||||
|
body& operator= (body&& other);
|
||||||
|
|
||||||
|
body (body const&) = delete;
|
||||||
|
body& operator= (body const&) = delete;
|
||||||
|
|
||||||
|
void
|
||||||
|
write (void const* data, std::size_t bytes);
|
||||||
|
|
||||||
|
template <class ConstBufferSequence>
|
||||||
|
void
|
||||||
|
write (ConstBufferSequence const& buffers)
|
||||||
|
{
|
||||||
|
for (auto const& buffer : buffers)
|
||||||
|
write (boost::asio::buffer_cast <void const*> (buffer),
|
||||||
|
boost::asio::buffer_size (buffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t
|
||||||
|
size() const;
|
||||||
|
|
||||||
|
const_buffers_type
|
||||||
|
data() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
std::string
|
||||||
|
to_string (body const& b)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
auto const& data (b.data());
|
||||||
|
auto const n (boost::asio::buffer_size (data));
|
||||||
|
s.resize (n);
|
||||||
|
boost::asio::buffer_copy (
|
||||||
|
boost::asio::buffer (&s[0], n), data);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
inline
|
||||||
|
body::body()
|
||||||
|
: buf_ (std::make_unique <buffer_type>())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
body::body (body&& other)
|
||||||
|
{
|
||||||
|
buf_ = std::move(other.buf_);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
body&
|
||||||
|
body::operator= (body&& other)
|
||||||
|
{
|
||||||
|
buf_ = std::move(other.buf_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
body::write (void const* data, std::size_t bytes)
|
||||||
|
{
|
||||||
|
buf_->commit (boost::asio::buffer_copy (buf_->prepare (bytes),
|
||||||
|
boost::asio::const_buffers_1 (data, bytes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
std::size_t
|
||||||
|
body::size() const
|
||||||
|
{
|
||||||
|
return buf_->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
auto
|
||||||
|
body::data() const
|
||||||
|
-> const_buffers_type
|
||||||
|
{
|
||||||
|
return buf_->data();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
350
beast/http/headers.h
Normal file
350
beast/http/headers.h
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_HEADERS_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_HEADERS_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/utility/ci_char_traits.h>
|
||||||
|
#include <boost/intrusive/list.hpp>
|
||||||
|
#include <boost/intrusive/set.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <map>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
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>>
|
||||||
|
{
|
||||||
|
template <class = void>
|
||||||
|
element (std::string const& f, std::string const& v);
|
||||||
|
|
||||||
|
std::string field;
|
||||||
|
std::string value;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
/** Holds a collection of HTTP headers. */
|
||||||
|
class headers : private detail::less
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
typedef boost::intrusive::make_list <detail::element,
|
||||||
|
boost::intrusive::constant_time_size <false>
|
||||||
|
>::type list_t;
|
||||||
|
|
||||||
|
typedef boost::intrusive::make_set <detail::element,
|
||||||
|
boost::intrusive::constant_time_size <true>
|
||||||
|
>::type set_t;
|
||||||
|
|
||||||
|
list_t list_;
|
||||||
|
set_t set_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef list_t::const_iterator iterator;
|
||||||
|
typedef iterator const_iterator;
|
||||||
|
|
||||||
|
~headers()
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
headers() = default;
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
headers (headers&& other);
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
headers (headers const& other);
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
headers&
|
||||||
|
operator= (headers&& other);
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/** 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);
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
element::element (
|
||||||
|
std::string const& f, std::string const& v)
|
||||||
|
: field (f)
|
||||||
|
, value (v)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class String>
|
||||||
|
bool
|
||||||
|
less::operator() (
|
||||||
|
String const& lhs, element const& rhs) const
|
||||||
|
{
|
||||||
|
return beast::ci_less::operator() (lhs, rhs.field);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class String>
|
||||||
|
bool
|
||||||
|
less::operator() (
|
||||||
|
element const& lhs, String const& rhs) const
|
||||||
|
{
|
||||||
|
return beast::ci_less::operator() (lhs.field, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // detail
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
headers::headers (headers&& other)
|
||||||
|
: list_ (std::move(other.list_))
|
||||||
|
, set_ (std::move(other.set_))
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
headers::headers (headers const& other)
|
||||||
|
{
|
||||||
|
for (auto const& e : other.list_)
|
||||||
|
append (e.field, e.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
headers&
|
||||||
|
headers::operator= (headers&& other)
|
||||||
|
{
|
||||||
|
list_ = std::move(other.list_);
|
||||||
|
set_ = std::move(other.set_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
headers&
|
||||||
|
headers::operator= (headers const& other)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
for (auto const& e : other.list_)
|
||||||
|
append (e.field, e.value);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
headers::iterator
|
||||||
|
headers::begin() const
|
||||||
|
{
|
||||||
|
return list_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
headers::iterator
|
||||||
|
headers::end() const
|
||||||
|
{
|
||||||
|
return list_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
headers::iterator
|
||||||
|
headers::cbegin() const
|
||||||
|
{
|
||||||
|
return list_.cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
headers::iterator
|
||||||
|
headers::cend() const
|
||||||
|
{
|
||||||
|
return list_.cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
headers::iterator
|
||||||
|
headers::find (std::string const& field) const
|
||||||
|
{
|
||||||
|
auto const iter (set_.find (field,
|
||||||
|
std::cref(static_cast<less const&>(*this))));
|
||||||
|
if (iter == set_.end())
|
||||||
|
return list_.end();
|
||||||
|
return list_.iterator_to (*iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
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->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
void
|
||||||
|
headers::clear() noexcept
|
||||||
|
{
|
||||||
|
for (auto iter (list_.begin()); iter != list_.end();)
|
||||||
|
delete &(*iter++);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
std::cref(static_cast<less const&>(*this)), d));
|
||||||
|
if (result.second)
|
||||||
|
{
|
||||||
|
detail::element* const p =
|
||||||
|
new detail::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->value);
|
||||||
|
cur.reserve (cur.size() + 1 + value.size());
|
||||||
|
cur.append (1, ',');
|
||||||
|
cur.append (value);
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
std::string
|
||||||
|
to_string (headers const& h)
|
||||||
|
{
|
||||||
|
std::string s;
|
||||||
|
std::size_t n (0);
|
||||||
|
for (auto const& e : h)
|
||||||
|
n += e.field.size() + 2 + e.value.size() + 2;
|
||||||
|
s.reserve (n);
|
||||||
|
for (auto const& e : h)
|
||||||
|
{
|
||||||
|
s.append (e.field);
|
||||||
|
s.append (": ");
|
||||||
|
s.append (e.value);
|
||||||
|
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.field);
|
||||||
|
// TODO Replace with safe C++14 version
|
||||||
|
std::transform (key.begin(), key.end(), key.begin(), ::tolower);
|
||||||
|
c [key] = e.value;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // http
|
||||||
|
} // beast
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -17,14 +17,64 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <beast/http/parser.h>
|
#include <beast/http/basic_parser.h>
|
||||||
#include <beast/http/impl/joyent_parser.h>
|
#include <beast/http/impl/joyent_parser.h>
|
||||||
#include <beast/http/rfc2616.h>
|
#include <beast/http/rfc2616.h>
|
||||||
|
#include <beast/utility/noexcept.h>
|
||||||
|
#include <boost/system/error_code.hpp>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
parser::parser (bool request)
|
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),
|
static_assert (sizeof(joyent::http_parser) == sizeof(state_t),
|
||||||
"state_t size must match http_parser size");
|
"state_t size must match http_parser size");
|
||||||
@@ -36,45 +86,66 @@ parser::parser (bool request)
|
|||||||
s->data = this;
|
s->data = this;
|
||||||
|
|
||||||
auto h (reinterpret_cast <joyent::http_parser_settings*> (&hooks_));
|
auto h (reinterpret_cast <joyent::http_parser_settings*> (&hooks_));
|
||||||
h->on_message_begin = &parser::cb_message_start;
|
h->on_message_begin = &basic_parser::cb_message_start;
|
||||||
h->on_url = &parser::cb_url;
|
h->on_url = &basic_parser::cb_url;
|
||||||
h->on_status = &parser::cb_status;
|
h->on_status = &basic_parser::cb_status;
|
||||||
h->on_header_field = &parser::cb_header_field;
|
h->on_header_field = &basic_parser::cb_header_field;
|
||||||
h->on_header_value = &parser::cb_header_value;
|
h->on_header_value = &basic_parser::cb_header_value;
|
||||||
h->on_headers_complete = &parser::cb_headers_complete;
|
h->on_headers_complete = &basic_parser::cb_headers_complete;
|
||||||
h->on_body = &parser::cb_body;
|
h->on_body = &basic_parser::cb_body;
|
||||||
h->on_message_complete = &parser::cb_message_complete;
|
h->on_message_complete = &basic_parser::cb_message_complete;
|
||||||
|
|
||||||
joyent::http_parser_init (s, request
|
joyent::http_parser_init (s, request
|
||||||
? joyent::http_parser_type::HTTP_REQUEST
|
? joyent::http_parser_type::HTTP_REQUEST
|
||||||
: joyent::http_parser_type::HTTP_RESPONSE);
|
: joyent::http_parser_type::HTTP_RESPONSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair <parser::error_code, std::size_t>
|
basic_parser&
|
||||||
parser::write (void const* data, std::size_t bytes)
|
basic_parser::operator= (basic_parser&& other)
|
||||||
{
|
{
|
||||||
std::pair <error_code, std::size_t> result (error_code(), 0);
|
*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;
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_parser::error_code
|
||||||
|
basic_parser::error() const noexcept
|
||||||
|
{
|
||||||
|
auto s (reinterpret_cast <joyent::http_parser const*> (&state_));
|
||||||
|
return error_code{static_cast<int>(s->http_errno), message_category()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair <bool, std::size_t>
|
||||||
|
basic_parser::write (void const* data, std::size_t bytes)
|
||||||
|
{
|
||||||
|
std::pair <bool, std::size_t> result (false, 0);
|
||||||
auto s (reinterpret_cast <joyent::http_parser*> (&state_));
|
auto s (reinterpret_cast <joyent::http_parser*> (&state_));
|
||||||
auto h (reinterpret_cast <joyent::http_parser_settings const*> (&hooks_));
|
auto h (reinterpret_cast <joyent::http_parser_settings const*> (&hooks_));
|
||||||
result.second = joyent::http_parser_execute (s, h,
|
result.second = joyent::http_parser_execute (s, h,
|
||||||
static_cast <const char*> (data), bytes);
|
static_cast <const char*> (data), bytes);
|
||||||
result.first = ec_;
|
result.first = s->http_errno == 0;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
parser::error_code
|
bool
|
||||||
parser::eof()
|
basic_parser::write_eof()
|
||||||
{
|
{
|
||||||
auto s (reinterpret_cast <joyent::http_parser*> (&state_));
|
auto s (reinterpret_cast <joyent::http_parser*> (&state_));
|
||||||
auto h (reinterpret_cast <joyent::http_parser_settings const*> (&hooks_));
|
auto h (reinterpret_cast <joyent::http_parser_settings const*> (&hooks_));
|
||||||
joyent::http_parser_execute (s, h, nullptr, 0);
|
joyent::http_parser_execute (s, h, nullptr, 0);
|
||||||
return ec_;
|
return s->http_errno == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
void
|
void
|
||||||
parser::check_header()
|
basic_parser::check_header()
|
||||||
{
|
{
|
||||||
if (! value_.empty())
|
if (! value_.empty())
|
||||||
{
|
{
|
||||||
@@ -86,7 +157,7 @@ parser::check_header()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::do_message_start ()
|
basic_parser::do_message_start ()
|
||||||
{
|
{
|
||||||
complete_ = false;
|
complete_ = false;
|
||||||
url_.clear();
|
url_.clear();
|
||||||
@@ -98,21 +169,21 @@ parser::do_message_start ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::do_url (char const* in, std::size_t bytes)
|
basic_parser::do_url (char const* in, std::size_t bytes)
|
||||||
{
|
{
|
||||||
url_.append (static_cast <char const*> (in), bytes);
|
url_.append (static_cast <char const*> (in), bytes);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::do_status (char const* in, std::size_t bytes)
|
basic_parser::do_status (char const* in, std::size_t bytes)
|
||||||
{
|
{
|
||||||
status_.append (static_cast <char const*> (in), bytes);
|
status_.append (static_cast <char const*> (in), bytes);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::do_header_field (char const* in, std::size_t bytes)
|
basic_parser::do_header_field (char const* in, std::size_t bytes)
|
||||||
{
|
{
|
||||||
check_header();
|
check_header();
|
||||||
field_.append (static_cast <char const*> (in), bytes);
|
field_.append (static_cast <char const*> (in), bytes);
|
||||||
@@ -120,7 +191,7 @@ parser::do_header_field (char const* in, std::size_t bytes)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::do_header_value (char const* in, std::size_t bytes)
|
basic_parser::do_header_value (char const* in, std::size_t bytes)
|
||||||
{
|
{
|
||||||
value_.append (static_cast <char const*> (in), bytes);
|
value_.append (static_cast <char const*> (in), bytes);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -132,7 +203,7 @@ parser::do_header_value (char const* in, std::size_t bytes)
|
|||||||
that the message has no body (e.g. a HEAD request).
|
that the message has no body (e.g. a HEAD request).
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
parser::do_headers_complete()
|
basic_parser::do_headers_complete()
|
||||||
{
|
{
|
||||||
check_header();
|
check_header();
|
||||||
auto const p (reinterpret_cast <joyent::http_parser const*> (&state_));
|
auto const p (reinterpret_cast <joyent::http_parser const*> (&state_));
|
||||||
@@ -149,7 +220,7 @@ parser::do_headers_complete()
|
|||||||
has already had the transfer-encoding removed.
|
has already had the transfer-encoding removed.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
parser::do_body (char const* in, std::size_t bytes)
|
basic_parser::do_body (char const* in, std::size_t bytes)
|
||||||
{
|
{
|
||||||
on_body (in, bytes);
|
on_body (in, bytes);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -157,7 +228,7 @@ parser::do_body (char const* in, std::size_t bytes)
|
|||||||
|
|
||||||
/* Called when the both the headers and content body (if any) are complete. */
|
/* Called when the both the headers and content body (if any) are complete. */
|
||||||
int
|
int
|
||||||
parser::do_message_complete ()
|
basic_parser::do_message_complete ()
|
||||||
{
|
{
|
||||||
complete_ = true;
|
complete_ = true;
|
||||||
on_complete();
|
on_complete();
|
||||||
@@ -167,64 +238,64 @@ parser::do_message_complete ()
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::cb_message_start (joyent::http_parser* p)
|
basic_parser::cb_message_start (joyent::http_parser* p)
|
||||||
{
|
{
|
||||||
return reinterpret_cast <parser*> (
|
return reinterpret_cast <basic_parser*> (
|
||||||
p->data)->do_message_start();
|
p->data)->do_message_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::cb_url (joyent::http_parser* p,
|
basic_parser::cb_url (joyent::http_parser* p,
|
||||||
char const* in, std::size_t bytes)
|
char const* in, std::size_t bytes)
|
||||||
{
|
{
|
||||||
return reinterpret_cast <parser*> (
|
return reinterpret_cast <basic_parser*> (
|
||||||
p->data)->do_url (in, bytes);
|
p->data)->do_url (in, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::cb_status (joyent::http_parser* p,
|
basic_parser::cb_status (joyent::http_parser* p,
|
||||||
char const* in, std::size_t bytes)
|
char const* in, std::size_t bytes)
|
||||||
{
|
{
|
||||||
return reinterpret_cast <parser*> (
|
return reinterpret_cast <basic_parser*> (
|
||||||
p->data)->do_status (in, bytes);
|
p->data)->do_status (in, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::cb_header_field (joyent::http_parser* p,
|
basic_parser::cb_header_field (joyent::http_parser* p,
|
||||||
char const* in, std::size_t bytes)
|
char const* in, std::size_t bytes)
|
||||||
{
|
{
|
||||||
return reinterpret_cast <parser*> (
|
return reinterpret_cast <basic_parser*> (
|
||||||
p->data)->do_header_field (in, bytes);
|
p->data)->do_header_field (in, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::cb_header_value (joyent::http_parser* p,
|
basic_parser::cb_header_value (joyent::http_parser* p,
|
||||||
char const* in, std::size_t bytes)
|
char const* in, std::size_t bytes)
|
||||||
{
|
{
|
||||||
return reinterpret_cast <parser*> (
|
return reinterpret_cast <basic_parser*> (
|
||||||
p->data)->do_header_value (in, bytes);
|
p->data)->do_header_value (in, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::cb_headers_complete (joyent::http_parser* p)
|
basic_parser::cb_headers_complete (joyent::http_parser* p)
|
||||||
{
|
{
|
||||||
return reinterpret_cast <parser*> (
|
return reinterpret_cast <basic_parser*> (
|
||||||
p->data)->do_headers_complete();
|
p->data)->do_headers_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::cb_body (joyent::http_parser* p,
|
basic_parser::cb_body (joyent::http_parser* p,
|
||||||
char const* in, std::size_t bytes)
|
char const* in, std::size_t bytes)
|
||||||
{
|
{
|
||||||
return reinterpret_cast <parser*> (
|
return reinterpret_cast <basic_parser*> (
|
||||||
p->data)->do_body (
|
p->data)->do_body (
|
||||||
in, bytes);
|
in, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
parser::cb_message_complete (joyent::http_parser* p)
|
basic_parser::cb_message_complete (joyent::http_parser* p)
|
||||||
{
|
{
|
||||||
return reinterpret_cast <parser*> (
|
return reinterpret_cast <basic_parser*> (
|
||||||
p->data)->do_message_complete();
|
p->data)->do_message_complete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,42 +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/get.h>
|
|
||||||
|
|
||||||
#include <beast/http/basic_url.h>
|
|
||||||
|
|
||||||
namespace beast {
|
|
||||||
namespace http {
|
|
||||||
|
|
||||||
std::pair <std::string, boost::system::error_code>
|
|
||||||
get (std::string const& url_string)
|
|
||||||
{
|
|
||||||
std::pair <std::string, boost::system::error_code> result;
|
|
||||||
|
|
||||||
url u;
|
|
||||||
u.parse (url_string, result.second);
|
|
||||||
if (result.second)
|
|
||||||
return result;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
309
beast/http/message.h
Normal file
309
beast/http/message.h
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
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_H_INCLUDED
|
||||||
|
#define BEAST_HTTP_MESSAGE_H_INCLUDED
|
||||||
|
|
||||||
|
#include <beast/http/basic_parser.h>
|
||||||
|
#include <beast/http/body.h>
|
||||||
|
#include <beast/http/method.h>
|
||||||
|
#include <beast/http/headers.h>
|
||||||
|
#include <beast/utility/ci_char_traits.h>
|
||||||
|
#include <boost/intrusive/list.hpp>
|
||||||
|
#include <boost/intrusive/set.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cctype>
|
||||||
|
#include <ostream>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace beast {
|
||||||
|
namespace http {
|
||||||
|
|
||||||
|
class message
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
bool request_;
|
||||||
|
|
||||||
|
// request
|
||||||
|
beast::http::method_t method_;
|
||||||
|
std::string url_;
|
||||||
|
|
||||||
|
// response
|
||||||
|
int status_;
|
||||||
|
std::string reason_;
|
||||||
|
|
||||||
|
// message
|
||||||
|
std::pair<int, int> version_;
|
||||||
|
bool keep_alive_;
|
||||||
|
bool upgrade_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~message() = default;
|
||||||
|
message (message const&) = delete;
|
||||||
|
message& operator= (message const&) = delete;
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
message();
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
message (message&& other);
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
message& operator= (message&& other);
|
||||||
|
|
||||||
|
// Memberspace
|
||||||
|
beast::http::headers headers;
|
||||||
|
|
||||||
|
beast::http::body body;
|
||||||
|
|
||||||
|
bool
|
||||||
|
request() const
|
||||||
|
{
|
||||||
|
return request_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
request (bool value)
|
||||||
|
{
|
||||||
|
request_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request
|
||||||
|
|
||||||
|
void
|
||||||
|
method (beast::http::method_t http_method)
|
||||||
|
{
|
||||||
|
method_ = http_method;
|
||||||
|
}
|
||||||
|
|
||||||
|
beast::http::method_t
|
||||||
|
method() const
|
||||||
|
{
|
||||||
|
return method_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
url (std::string const& s)
|
||||||
|
{
|
||||||
|
url_ = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
url() const
|
||||||
|
{
|
||||||
|
return url_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `false` if this is not the last message.
|
||||||
|
When keep_alive returns `false`:
|
||||||
|
* Server roles respond with a "Connection: close" header.
|
||||||
|
* Client roles close the connection.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
keep_alive() const
|
||||||
|
{
|
||||||
|
return keep_alive_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the keep_alive setting. */
|
||||||
|
void
|
||||||
|
keep_alive (bool value)
|
||||||
|
{
|
||||||
|
keep_alive_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns `true` if this is an HTTP Upgrade message.
|
||||||
|
@note Upgrade messages have no content body.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
upgrade() const
|
||||||
|
{
|
||||||
|
return upgrade_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set the upgrade setting. */
|
||||||
|
void
|
||||||
|
upgrade (bool value)
|
||||||
|
{
|
||||||
|
upgrade_ = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
status() const
|
||||||
|
{
|
||||||
|
return status_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
status (int code)
|
||||||
|
{
|
||||||
|
status_ = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string const&
|
||||||
|
reason() const
|
||||||
|
{
|
||||||
|
return reason_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
reason (std::string const& text)
|
||||||
|
{
|
||||||
|
reason_ = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message
|
||||||
|
|
||||||
|
void
|
||||||
|
version (int major, int minor)
|
||||||
|
{
|
||||||
|
version_ = std::make_pair (major, minor);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<int, int>
|
||||||
|
version() const
|
||||||
|
{
|
||||||
|
return version_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
message::message()
|
||||||
|
: request_ (true)
|
||||||
|
, method_ (beast::http::method_t::http_get)
|
||||||
|
, url_ ("/")
|
||||||
|
, status_ (200)
|
||||||
|
, version_ (1, 1)
|
||||||
|
, keep_alive_ (false)
|
||||||
|
, upgrade_ (false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
message::message (message&& other)
|
||||||
|
: request_ (other.request_)
|
||||||
|
, method_ (std::move(other.method_))
|
||||||
|
, url_ (std::move(other.url_))
|
||||||
|
, status_ (other.status_)
|
||||||
|
, reason_ (std::move(other.reason_))
|
||||||
|
, version_ (other.version_)
|
||||||
|
, keep_alive_ (other.keep_alive_)
|
||||||
|
, upgrade_ (other.upgrade_)
|
||||||
|
, headers (std::move(other.headers))
|
||||||
|
, body (std::move(other.body))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
message&
|
||||||
|
message::operator= (message&& other)
|
||||||
|
{
|
||||||
|
request_ = other.request_;
|
||||||
|
method_ = std::move(other.method_);
|
||||||
|
url_ = std::move(other.url_);
|
||||||
|
status_ = other.status_;
|
||||||
|
reason_ = std::move(other.reason_);
|
||||||
|
version_ = other.version_;
|
||||||
|
keep_alive_ = other.keep_alive_;
|
||||||
|
upgrade_ = other.upgrade_;
|
||||||
|
headers = std::move(other.headers);
|
||||||
|
body = std::move(other.body);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class AsioStreamBuf>
|
||||||
|
void
|
||||||
|
write (AsioStreamBuf& stream, std::string const& s)
|
||||||
|
{
|
||||||
|
stream.commit (boost::asio::buffer_copy (
|
||||||
|
stream.prepare (s.size()), boost::asio::buffer(s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class AsioStreamBuf>
|
||||||
|
void
|
||||||
|
write (AsioStreamBuf& 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 AsioStreamBuf>
|
||||||
|
void
|
||||||
|
write (AsioStreamBuf& stream, message const& m)
|
||||||
|
{
|
||||||
|
if (m.request())
|
||||||
|
{
|
||||||
|
write (stream, to_string(m.method()));
|
||||||
|
write (stream, " ");
|
||||||
|
write (stream, m.url());
|
||||||
|
write (stream, " HTTP/");
|
||||||
|
write (stream, std::to_string(m.version().first));
|
||||||
|
write (stream, ".");
|
||||||
|
write (stream, std::to_string(m.version().second));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write (stream, "HTTP/");
|
||||||
|
write (stream, std::to_string(m.version().first));
|
||||||
|
write (stream, ".");
|
||||||
|
write (stream, std::to_string(m.version().second));
|
||||||
|
write (stream, " ");
|
||||||
|
write (stream, std::to_string(m.status()));
|
||||||
|
write (stream, " ");
|
||||||
|
write (stream, m.reason());
|
||||||
|
}
|
||||||
|
write (stream, "\r\n");
|
||||||
|
for (auto const& header : m.headers)
|
||||||
|
{
|
||||||
|
write (stream, header.field);
|
||||||
|
write (stream, ": ");
|
||||||
|
write (stream, header.value);
|
||||||
|
write (stream, "\r\n");
|
||||||
|
}
|
||||||
|
write (stream, "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
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
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -20,229 +20,165 @@
|
|||||||
#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/method.h>
|
#include <beast/http/message.h>
|
||||||
#include <boost/asio/buffer.hpp>
|
|
||||||
#include <boost/system/error_code.hpp>
|
|
||||||
#include <array>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
|
|
||||||
namespace joyent {
|
|
||||||
struct http_parser;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
class parser
|
/** Parser for HTTP messages.
|
||||||
|
The result is stored in a message object.
|
||||||
|
*/
|
||||||
|
class parser : public beast::http::basic_parser
|
||||||
{
|
{
|
||||||
|
private:
|
||||||
|
std::reference_wrapper <message> message_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef boost::system::error_code error_code;
|
/** Construct a parser for HTTP request or response.
|
||||||
|
The result is stored in the passed message.
|
||||||
|
*/
|
||||||
|
parser (message& m, bool request)
|
||||||
|
: beast::http::basic_parser (request)
|
||||||
|
, message_(m)
|
||||||
|
{
|
||||||
|
message_.get().request(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class = void>
|
||||||
|
parser&
|
||||||
|
operator= (parser&& other);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// These structures must exactly match the
|
template <class = void>
|
||||||
// declarations in joyent http_parser.h include
|
void
|
||||||
//
|
do_start ();
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef int (*data_cb_t) (
|
template <class = void>
|
||||||
state_t*, const char *at, size_t length);
|
|
||||||
typedef int (*cb_t) (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;
|
|
||||||
};
|
|
||||||
|
|
||||||
error_code ec_;
|
|
||||||
char state_ [sizeof(state_t)];
|
|
||||||
char hooks_ [sizeof(hooks_t)];
|
|
||||||
|
|
||||||
bool complete_ = false;
|
|
||||||
std::string url_;
|
|
||||||
std::string status_;
|
|
||||||
std::string field_;
|
|
||||||
std::string value_;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/** Construct the parser.
|
|
||||||
If `request` is `true` this sets up the parser to
|
|
||||||
process an HTTP request.
|
|
||||||
*/
|
|
||||||
explicit
|
|
||||||
parser (bool request);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/** Returns `true` if parsing is complete.
|
|
||||||
This is only defined when no errors have been returned.
|
|
||||||
*/
|
|
||||||
bool
|
bool
|
||||||
complete() const
|
do_request (method_t method, std::string const& url,
|
||||||
|
int major, int minor, bool keep_alive, bool upgrade);
|
||||||
|
|
||||||
|
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
|
||||||
{
|
{
|
||||||
return complete_;
|
do_start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Write data to the parser.
|
|
||||||
The return value includes the error code if any,
|
|
||||||
and the number of bytes consumed in the input sequence.
|
|
||||||
@param data A buffer containing the data to write
|
|
||||||
@param bytes The size of the buffer pointed to by data.
|
|
||||||
*/
|
|
||||||
std::pair <error_code, std::size_t>
|
|
||||||
write (void const* data, std::size_t bytes);
|
|
||||||
|
|
||||||
/** Write a set of buffer data to the parser.
|
|
||||||
The return value includes the error code if any,
|
|
||||||
and the number of bytes consumed in the input sequence.
|
|
||||||
@param buffers The buffers to write. These must meet the
|
|
||||||
requirements of ConstBufferSequence.
|
|
||||||
*/
|
|
||||||
template <class ConstBufferSequence>
|
|
||||||
std::pair <error_code, std::size_t>
|
|
||||||
write (ConstBufferSequence const& buffers)
|
|
||||||
{
|
|
||||||
std::pair <error_code, std::size_t> result (error_code(), 0);
|
|
||||||
for (auto const& buffer : buffers)
|
|
||||||
{
|
|
||||||
std::size_t bytes_consumed;
|
|
||||||
std::tie (result.first, bytes_consumed) =
|
|
||||||
write (boost::asio::buffer_cast <void const*> (buffer),
|
|
||||||
boost::asio::buffer_size (buffer));
|
|
||||||
if (result.first)
|
|
||||||
break;
|
|
||||||
result.second += bytes_consumed;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called to indicate the end of file.
|
|
||||||
HTTP needs to know where the end of the stream is. For example,
|
|
||||||
sometimes servers send responses without Content-Length and
|
|
||||||
expect the client to consume input (for the body) until EOF.
|
|
||||||
Callbacks and errors will still be processed as usual.
|
|
||||||
@note Typically this should be called when the
|
|
||||||
socket indicates a closure.
|
|
||||||
*/
|
|
||||||
error_code
|
|
||||||
eof();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/** Called once when a new message begins. */
|
|
||||||
virtual
|
|
||||||
void
|
|
||||||
on_start() = 0;
|
|
||||||
|
|
||||||
/** Called for each header field. */
|
|
||||||
virtual
|
|
||||||
void
|
|
||||||
on_field (std::string const& field, std::string const& value) = 0;
|
|
||||||
|
|
||||||
/** 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.
|
|
||||||
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
|
|
||||||
@param major The HTTP major version number
|
|
||||||
@param minor The HTTP minor version number
|
|
||||||
@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
|
bool
|
||||||
on_request (method_t method, std::string const& url,
|
on_request (method_t method, std::string const& url,
|
||||||
int major, int minor, bool keep_alive, bool upgrade) = 0;
|
int major, int minor, bool keep_alive, bool upgrade) override
|
||||||
|
{
|
||||||
|
return do_request (method, url, major, minor, keep_alive, 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.
|
|
||||||
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
|
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) = 0;
|
int major, int minor, bool keep_alive, bool upgrade) override
|
||||||
|
{
|
||||||
|
return do_response (status, text, major, minor, keep_alive, upgrade);
|
||||||
|
}
|
||||||
|
|
||||||
/** 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;
|
on_field (std::string const& field, std::string const& value) override
|
||||||
|
{
|
||||||
|
do_field (field, value);
|
||||||
|
}
|
||||||
|
|
||||||
/** Called once when the message is complete. */
|
|
||||||
virtual
|
|
||||||
void
|
void
|
||||||
on_complete() = 0;
|
on_body (void const* data, std::size_t bytes) override
|
||||||
|
{
|
||||||
|
do_body (data, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
void
|
||||||
void check_header();
|
on_complete() override
|
||||||
|
{
|
||||||
int do_message_start ();
|
do_complete();
|
||||||
int do_url (char const* in, std::size_t bytes);
|
}
|
||||||
int do_status (char const* in, std::size_t bytes);
|
|
||||||
int do_header_field (char const* in, std::size_t bytes);
|
|
||||||
int do_header_value (char const* in, std::size_t bytes);
|
|
||||||
int do_headers_complete ();
|
|
||||||
int do_body (char const* in, std::size_t bytes);
|
|
||||||
int do_message_complete ();
|
|
||||||
|
|
||||||
static int cb_message_start (joyent::http_parser*);
|
|
||||||
static int cb_url (joyent::http_parser*, char const*, std::size_t);
|
|
||||||
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);
|
|
||||||
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);
|
|
||||||
static int cb_message_complete (joyent::http_parser*);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
parser&
|
||||||
|
parser::operator= (parser&& other)
|
||||||
|
{
|
||||||
|
basic_parser::operator= (std::move(other));
|
||||||
|
message_ = std::move (other.message_);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
message_.get().body.write (data, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class>
|
||||||
|
void
|
||||||
|
parser::do_complete()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
|
|
||||||
#include <beast/http/tests/urls_large_data.h>
|
#include <beast/http/tests/urls_large_data.h>
|
||||||
#include <beast/http/client_session.h>
|
#include <beast/http/client_session.h>
|
||||||
#include <beast/http/get.h>
|
|
||||||
#include <beast/asio/bind_handler.h>
|
#include <beast/asio/bind_handler.h>
|
||||||
#include <beast/asio/memory_buffer.h>
|
#include <beast/asio/memory_buffer.h>
|
||||||
#include <beast/utility/ci_char_traits.h>
|
#include <beast/utility/ci_char_traits.h>
|
||||||
@@ -365,14 +364,8 @@ std::advance (last, 3000);
|
|||||||
test_concurrent_get (std::begin (sequence), last);
|
test_concurrent_get (std::begin (sequence), last);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_get()
|
|
||||||
{
|
|
||||||
get ("http://www.google.com");
|
|
||||||
}
|
|
||||||
|
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
//test_get();
|
|
||||||
test_concurrent_get (urls_large_data());
|
test_concurrent_get (urls_large_data());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
/*
|
/*
|
||||||
This file is part of rippled: https://github.com/ripple/rippled
|
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||||
Copyright (c) 2012, 2013 Ripple Labs Inc.
|
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
|
||||||
@@ -17,27 +17,28 @@
|
|||||||
*/
|
*/
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
#include <beast/http/basic_message.h>
|
#include <beast/http/message.h>
|
||||||
|
#include <beast/http/parser.h>
|
||||||
#include <beast/unit_test/suite.h>
|
#include <beast/unit_test/suite.h>
|
||||||
|
|
||||||
namespace beast {
|
namespace beast {
|
||||||
namespace http {
|
namespace http {
|
||||||
|
|
||||||
class basic_message_test : public beast::unit_test::suite
|
class message_test : public beast::unit_test::suite
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::pair <basic_message, bool>
|
std::pair <message, bool>
|
||||||
request (std::string const& text)
|
request (std::string const& text)
|
||||||
{
|
{
|
||||||
basic_message m;
|
message m;
|
||||||
basic_message::parser p (m, true);
|
parser p (m, true);
|
||||||
auto result (p.write (boost::asio::buffer(text)));
|
auto result (p.write (boost::asio::buffer(text)));
|
||||||
p.eof();
|
p.write_eof();
|
||||||
return std::make_pair (std::move(m), result.first);
|
return std::make_pair (std::move(m), result.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
run()
|
dump()
|
||||||
{
|
{
|
||||||
auto const result = request (
|
auto const result = request (
|
||||||
"GET / HTTP/1.1\r\n"
|
"GET / HTTP/1.1\r\n"
|
||||||
@@ -51,14 +52,43 @@ public:
|
|||||||
"\r\n"
|
"\r\n"
|
||||||
"x"
|
"x"
|
||||||
);
|
);
|
||||||
|
log << result.first.headers;
|
||||||
log << "|" << result.first.headers["Field"] << "|";
|
log << "|" << result.first.headers["Field"] << "|";
|
||||||
|
}
|
||||||
|
|
||||||
pass();
|
void
|
||||||
|
run()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::string const text =
|
||||||
|
"GET / HTTP/1.1\r\n"
|
||||||
|
"\r\n"
|
||||||
|
;
|
||||||
|
message m;
|
||||||
|
parser p (m, true);
|
||||||
|
auto result (p.write (boost::asio::buffer(text)));
|
||||||
|
expect (result.first);
|
||||||
|
auto result2 (p.write_eof());
|
||||||
|
expect (result2);
|
||||||
|
expect (p.complete());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// malformed
|
||||||
|
std::string const text =
|
||||||
|
"GET\r\n"
|
||||||
|
"\r\n"
|
||||||
|
;
|
||||||
|
message m;
|
||||||
|
parser p (m, true);
|
||||||
|
auto result (p.write (boost::asio::buffer(text)));
|
||||||
|
if (expect (! result.first))
|
||||||
|
expect (p.error().message() == "invalid HTTP method");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
BEAST_DEFINE_TESTSUITE(basic_message,http,beast);
|
BEAST_DEFINE_TESTSUITE(message,http,beast);
|
||||||
|
|
||||||
} // http
|
} // http
|
||||||
} // beast
|
} // beast
|
||||||
Reference in New Issue
Block a user