mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-27 14:35:52 +00:00
HTTP support improvements:
* RFC2616 compliance * Case insensitive equality, inequality operators for strings * Improvements to http::parser * Tidy up HTTP method enumeration
This commit is contained in:
@@ -24,13 +24,16 @@
|
||||
#include <beast/http/impl/basic_url.cpp>
|
||||
#include <beast/http/impl/get.cpp>
|
||||
#include <beast/http/impl/joyent_parser.cpp>
|
||||
#include <beast/http/impl/message_parser.cpp>
|
||||
#include <beast/http/impl/method.cpp>
|
||||
#include <beast/http/impl/ParsedURL.cpp>
|
||||
#include <beast/http/impl/parser.cpp>
|
||||
#include <beast/http/impl/raw_parser.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/client_session.test.cpp>
|
||||
#include <beast/http/tests/ParsedURL.cpp>
|
||||
#include <beast/http/tests/rfc2616.test.cpp>
|
||||
#include <beast/http/tests/urls_large_data.cpp>
|
||||
|
||||
|
||||
509
beast/http/basic_message.h
Normal file
509
beast/http/basic_message.h
Normal file
@@ -0,0 +1,509 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_);
|
||||
}
|
||||
|
||||
headers_t&
|
||||
operator= (headers_t const& other)
|
||||
{
|
||||
clear();
|
||||
for (auto const& e : other.list_)
|
||||
append (e.field, e.value);
|
||||
}
|
||||
|
||||
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);
|
||||
auto const iter (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
|
||||
@@ -1,224 +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/message_parser.h>
|
||||
#include <beast/http/impl/joyent_parser.h>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
message_parser::message_parser (bool request)
|
||||
: complete_ (false)
|
||||
, checked_url_ (false)
|
||||
{
|
||||
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 = &message_parser::cb_message_start;
|
||||
h->on_url = &message_parser::cb_url;
|
||||
h->on_status = &message_parser::cb_status;
|
||||
h->on_header_field = &message_parser::cb_header_field;
|
||||
h->on_header_value = &message_parser::cb_header_value;
|
||||
h->on_headers_complete = &message_parser::cb_headers_done;
|
||||
h->on_body = &message_parser::cb_body;
|
||||
h->on_message_complete = &message_parser::cb_message_complete;
|
||||
|
||||
joyent::http_parser_init (s, request
|
||||
? joyent::http_parser_type::HTTP_REQUEST
|
||||
: joyent::http_parser_type::HTTP_RESPONSE);
|
||||
}
|
||||
|
||||
std::pair <message_parser::error_code, std::size_t>
|
||||
message_parser::write_one (void const* in, std::size_t bytes)
|
||||
{
|
||||
std::pair <error_code, std::size_t> result (error_code(), 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*> (in), bytes);
|
||||
result.first = ec_;
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int
|
||||
message_parser::check_url()
|
||||
{
|
||||
if (! checked_url_)
|
||||
{
|
||||
checked_url_ = true;
|
||||
auto const p (reinterpret_cast <joyent::http_parser const*> (&state_));
|
||||
ec_ = on_request (
|
||||
joyent::convert_http_method (joyent::http_method(p->method)),
|
||||
p->http_major, p->http_minor, url_);
|
||||
if (ec_)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::do_message_start ()
|
||||
{
|
||||
return ec_ ? 1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::do_url (char const* in, std::size_t bytes)
|
||||
{
|
||||
url_.append (static_cast <char const*> (in), bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::do_status (char const* in, std::size_t bytes)
|
||||
{
|
||||
return ec_ ? 1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::do_header_field (char const* in, std::size_t bytes)
|
||||
{
|
||||
if (check_url())
|
||||
return 1;
|
||||
if (! value_.empty())
|
||||
{
|
||||
ec_ = on_field (field_, value_);
|
||||
if (ec_)
|
||||
return 1;
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
field_.append (static_cast <char const*> (in), bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::do_header_value (char const* in, std::size_t bytes)
|
||||
{
|
||||
value_.append (static_cast <char const*> (in), bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Returning 1 from here tells the joyent parser
|
||||
// that the message has no body (e.g. a HEAD request).
|
||||
//
|
||||
int
|
||||
message_parser::do_headers_done ()
|
||||
{
|
||||
if (check_url())
|
||||
return 1;
|
||||
if (! value_.empty())
|
||||
{
|
||||
ec_ = on_field (field_, value_);
|
||||
if (ec_)
|
||||
return 1;
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
return ec_ ? 1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::do_body (char const* in, std::size_t bytes)
|
||||
{
|
||||
return ec_ ? 1 : 0;
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::do_message_complete ()
|
||||
{
|
||||
complete_ = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int
|
||||
message_parser::cb_message_start (joyent::http_parser* p)
|
||||
{
|
||||
return reinterpret_cast <message_parser*> (
|
||||
p->data)->do_message_start();
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::cb_url (joyent::http_parser* p,
|
||||
char const* in, std::size_t bytes)
|
||||
{
|
||||
return reinterpret_cast <message_parser*> (
|
||||
p->data)->do_url (in, bytes);
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::cb_status (joyent::http_parser* p,
|
||||
char const* in, std::size_t bytes)
|
||||
{
|
||||
return reinterpret_cast <message_parser*> (
|
||||
p->data)->do_status (in, bytes);
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::cb_header_field (joyent::http_parser* p,
|
||||
char const* in, std::size_t bytes)
|
||||
{
|
||||
return reinterpret_cast <message_parser*> (
|
||||
p->data)->do_header_field (in, bytes);
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::cb_header_value (joyent::http_parser* p,
|
||||
char const* in, std::size_t bytes)
|
||||
{
|
||||
return reinterpret_cast <message_parser*> (
|
||||
p->data)->do_header_value (in, bytes);
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::cb_headers_done (joyent::http_parser* p)
|
||||
{
|
||||
return reinterpret_cast <message_parser*> (
|
||||
p->data)->do_headers_done();
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::cb_body (joyent::http_parser* p,
|
||||
char const* in, std::size_t bytes)
|
||||
{
|
||||
return reinterpret_cast <message_parser*> (
|
||||
p->data)->do_body (
|
||||
in, bytes);
|
||||
}
|
||||
|
||||
int
|
||||
message_parser::cb_message_complete (joyent::http_parser* p)
|
||||
{
|
||||
return reinterpret_cast <message_parser*> (
|
||||
p->data)->do_message_complete();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
124
beast/http/impl/method.cpp
Normal file
124
beast/http/impl/method.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/method.h>
|
||||
#include <cassert>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
std::string
|
||||
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";
|
||||
}
|
||||
|
||||
std::string
|
||||
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";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
232
beast/http/impl/parser.cpp
Normal file
232
beast/http/impl/parser.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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/parser.h>
|
||||
#include <beast/http/impl/joyent_parser.h>
|
||||
#include <beast/http/rfc2616.h>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
parser::parser (bool request)
|
||||
{
|
||||
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 = &parser::cb_message_start;
|
||||
h->on_url = &parser::cb_url;
|
||||
h->on_status = &parser::cb_status;
|
||||
h->on_header_field = &parser::cb_header_field;
|
||||
h->on_header_value = &parser::cb_header_value;
|
||||
h->on_headers_complete = &parser::cb_headers_complete;
|
||||
h->on_body = &parser::cb_body;
|
||||
h->on_message_complete = &parser::cb_message_complete;
|
||||
|
||||
joyent::http_parser_init (s, request
|
||||
? joyent::http_parser_type::HTTP_REQUEST
|
||||
: joyent::http_parser_type::HTTP_RESPONSE);
|
||||
}
|
||||
|
||||
std::pair <parser::error_code, std::size_t>
|
||||
parser::write (void const* data, std::size_t bytes)
|
||||
{
|
||||
std::pair <error_code, std::size_t> result (error_code(), 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 = ec_;
|
||||
return result;
|
||||
}
|
||||
|
||||
parser::error_code
|
||||
parser::eof()
|
||||
{
|
||||
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 ec_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void
|
||||
parser::check_header()
|
||||
{
|
||||
if (! value_.empty())
|
||||
{
|
||||
rfc2616::trim_right_in_place (value_);
|
||||
on_field (field_, value_);
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
parser::do_message_start ()
|
||||
{
|
||||
complete_ = false;
|
||||
url_.clear();
|
||||
status_.clear();
|
||||
field_.clear();
|
||||
value_.clear();
|
||||
on_start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
parser::do_url (char const* in, std::size_t bytes)
|
||||
{
|
||||
url_.append (static_cast <char const*> (in), bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
parser::do_status (char const* in, std::size_t bytes)
|
||||
{
|
||||
status_.append (static_cast <char const*> (in), bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
parser::do_header_field (char const* in, std::size_t bytes)
|
||||
{
|
||||
check_header();
|
||||
field_.append (static_cast <char const*> (in), bytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
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
|
||||
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
|
||||
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
|
||||
parser::do_message_complete ()
|
||||
{
|
||||
complete_ = true;
|
||||
on_complete();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int
|
||||
parser::cb_message_start (joyent::http_parser* p)
|
||||
{
|
||||
return reinterpret_cast <parser*> (
|
||||
p->data)->do_message_start();
|
||||
}
|
||||
|
||||
int
|
||||
parser::cb_url (joyent::http_parser* p,
|
||||
char const* in, std::size_t bytes)
|
||||
{
|
||||
return reinterpret_cast <parser*> (
|
||||
p->data)->do_url (in, bytes);
|
||||
}
|
||||
|
||||
int
|
||||
parser::cb_status (joyent::http_parser* p,
|
||||
char const* in, std::size_t bytes)
|
||||
{
|
||||
return reinterpret_cast <parser*> (
|
||||
p->data)->do_status (in, bytes);
|
||||
}
|
||||
|
||||
int
|
||||
parser::cb_header_field (joyent::http_parser* p,
|
||||
char const* in, std::size_t bytes)
|
||||
{
|
||||
return reinterpret_cast <parser*> (
|
||||
p->data)->do_header_field (in, bytes);
|
||||
}
|
||||
|
||||
int
|
||||
parser::cb_header_value (joyent::http_parser* p,
|
||||
char const* in, std::size_t bytes)
|
||||
{
|
||||
return reinterpret_cast <parser*> (
|
||||
p->data)->do_header_value (in, bytes);
|
||||
}
|
||||
|
||||
int
|
||||
parser::cb_headers_complete (joyent::http_parser* p)
|
||||
{
|
||||
return reinterpret_cast <parser*> (
|
||||
p->data)->do_headers_complete();
|
||||
}
|
||||
|
||||
int
|
||||
parser::cb_body (joyent::http_parser* p,
|
||||
char const* in, std::size_t bytes)
|
||||
{
|
||||
return reinterpret_cast <parser*> (
|
||||
p->data)->do_body (
|
||||
in, bytes);
|
||||
}
|
||||
|
||||
int
|
||||
parser::cb_message_complete (joyent::http_parser* p)
|
||||
{
|
||||
return reinterpret_cast <parser*> (
|
||||
p->data)->do_message_complete();
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
@@ -21,6 +21,7 @@
|
||||
#define BEAST_HTTP_METHOD_H_INCLUDED
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
@@ -65,6 +66,20 @@ enum class method_t
|
||||
http_purge
|
||||
};
|
||||
|
||||
std::string
|
||||
to_string (method_t m);
|
||||
|
||||
template <class Stream>
|
||||
Stream&
|
||||
operator<< (Stream& s, method_t m)
|
||||
{
|
||||
return s << to_string(m);
|
||||
}
|
||||
|
||||
/** Returns the string corresponding to the numeric HTTP status code. */
|
||||
std::string
|
||||
status_text (int status);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#ifndef BEAST_HTTP_MESSAGE_PARSER_H_INCLUDED
|
||||
#define BEAST_HTTP_MESSAGE_PARSER_H_INCLUDED
|
||||
#ifndef BEAST_HTTP_PARSER_H_INCLUDED
|
||||
#define BEAST_HTTP_PARSER_H_INCLUDED
|
||||
|
||||
#include <beast/http/method.h>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
@@ -35,7 +36,7 @@ struct http_parser;
|
||||
|
||||
namespace http {
|
||||
|
||||
class message_parser
|
||||
class parser
|
||||
{
|
||||
public:
|
||||
typedef boost::system::error_code error_code;
|
||||
@@ -82,9 +83,9 @@ private:
|
||||
char state_ [sizeof(state_t)];
|
||||
char hooks_ [sizeof(hooks_t)];
|
||||
|
||||
bool complete_;
|
||||
bool complete_ = false;
|
||||
std::string url_;
|
||||
bool checked_url_;
|
||||
std::string status_;
|
||||
std::string field_;
|
||||
std::string value_;
|
||||
|
||||
@@ -94,7 +95,7 @@ protected:
|
||||
process an HTTP request.
|
||||
*/
|
||||
explicit
|
||||
message_parser (bool request);
|
||||
parser (bool request);
|
||||
|
||||
public:
|
||||
/** Returns `true` if parsing is complete.
|
||||
@@ -109,18 +110,18 @@ public:
|
||||
/** 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_one (void const* in, std::size_t bytes);
|
||||
|
||||
template <class ConstBuffer>
|
||||
std::pair <error_code, std::size_t>
|
||||
write_one (ConstBuffer const& buffer)
|
||||
{
|
||||
return write_one (boost::asio::buffer_cast <void const*> (buffer),
|
||||
boost::asio::buffer_size (buffer));
|
||||
}
|
||||
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)
|
||||
@@ -129,7 +130,9 @@ public:
|
||||
for (auto const& buffer : buffers)
|
||||
{
|
||||
std::size_t bytes_consumed;
|
||||
std::tie (result.first, bytes_consumed) = write_one (buffer);
|
||||
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;
|
||||
@@ -137,25 +140,96 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual
|
||||
/** 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
|
||||
on_request (method_t method, int http_major,
|
||||
int http_minor, std::string const& url) = 0;
|
||||
eof();
|
||||
|
||||
protected:
|
||||
/** Called once when a new message begins. */
|
||||
virtual
|
||||
error_code
|
||||
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:
|
||||
int check_url();
|
||||
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_done ();
|
||||
int do_headers_complete ();
|
||||
int do_body (char const* in, std::size_t bytes);
|
||||
int do_message_complete ();
|
||||
|
||||
@@ -164,7 +238,7 @@ private:
|
||||
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_done (joyent::http_parser*);
|
||||
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*);
|
||||
};
|
||||
239
beast/http/rfc2616.h
Normal file
239
beast/http/rfc2616.h
Normal file
@@ -0,0 +1,239 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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_RFC2616_H_INCLUDED
|
||||
#define BEAST_HTTP_RFC2616_H_INCLUDED
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
/** Routines for performing RFC2616 compliance.
|
||||
RFC2616:
|
||||
Hypertext Transfer Protocol -- HTTP/1.1
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616
|
||||
*/
|
||||
namespace rfc2616 {
|
||||
|
||||
/** Returns `true` if `c` is linear white space.
|
||||
This excludes the CRLF sequence allowed for line continuations.
|
||||
*/
|
||||
template <class CharT>
|
||||
bool
|
||||
is_lws (CharT c)
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
/** Returns `true` if `c` is any whitespace character. */
|
||||
template <class CharT>
|
||||
bool
|
||||
is_white (CharT c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case ' ': case '\f': case '\n':
|
||||
case '\r': case '\t': case '\v':
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns `true` if `c` is a control character. */
|
||||
template <class CharT>
|
||||
bool
|
||||
is_ctl (CharT c)
|
||||
{
|
||||
return c <= 31 || c >= 127;
|
||||
}
|
||||
|
||||
/** Returns `true` if `c` is a separator. */
|
||||
template <class CharT>
|
||||
bool
|
||||
is_sep (CharT c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '(': case ')': case '<': case '>': case '@':
|
||||
case ',': case ';': case ':': case '\\': case '"':
|
||||
case '{': case '}': case ' ': case '\t':
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class FwdIter>
|
||||
FwdIter
|
||||
trim_left (FwdIter first, FwdIter last)
|
||||
{
|
||||
return std::find_if_not (first, last,
|
||||
&is_white <typename FwdIter::value_type>);
|
||||
}
|
||||
|
||||
template <class FwdIter>
|
||||
FwdIter
|
||||
trim_right (FwdIter first, FwdIter last)
|
||||
{
|
||||
if (first == last)
|
||||
return last;
|
||||
do
|
||||
{
|
||||
--last;
|
||||
if (! is_white (*last))
|
||||
return ++last;
|
||||
}
|
||||
while (last != first);
|
||||
return first;
|
||||
}
|
||||
|
||||
template <class CharT, class Traits, class Allocator>
|
||||
void
|
||||
trim_right_in_place (std::basic_string <
|
||||
CharT, Traits, Allocator>& s)
|
||||
{
|
||||
s.resize (std::distance (s.begin(),
|
||||
trim_right (s.begin(), s.end())));
|
||||
}
|
||||
|
||||
template <class FwdIter>
|
||||
std::pair <FwdIter, FwdIter>
|
||||
trim (FwdIter first, FwdIter last)
|
||||
{
|
||||
first = trim_left (first, last);
|
||||
last = trim_right (first, last);
|
||||
return std::make_pair (first, last);
|
||||
}
|
||||
|
||||
template <class String>
|
||||
String
|
||||
trim (String const& s)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
auto first (begin(s));
|
||||
auto last (end(s));
|
||||
std::tie (first, last) = trim (first, last);
|
||||
return { first, last };
|
||||
}
|
||||
|
||||
template <class String>
|
||||
String
|
||||
trim_right (String const& s)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
auto first (begin(s));
|
||||
auto last (end(s));
|
||||
last = trim_right (first, last);
|
||||
return { first, last };
|
||||
}
|
||||
|
||||
inline
|
||||
std::string
|
||||
trim (std::string const& s)
|
||||
{
|
||||
return trim <std::string> (s);
|
||||
}
|
||||
|
||||
/** Call a functor for each comma delimited element.
|
||||
Quotes and escape sequences will be parsed and converted appropriately.
|
||||
Excess white space, commas, double quotes, and empty elements are not
|
||||
passed to func.
|
||||
Format:
|
||||
#(token|quoted-string)
|
||||
Reference:
|
||||
http://www.w3.org/Protocols/rfc2616/rfc2616-sec2.html#sec2
|
||||
*/
|
||||
template <class FwdIter, class Function>
|
||||
void
|
||||
for_each_element (FwdIter first, FwdIter last, Function func)
|
||||
{
|
||||
FwdIter iter (first);
|
||||
std::string e;
|
||||
while (iter != last)
|
||||
{
|
||||
if (*iter == '"')
|
||||
{
|
||||
// quoted-string
|
||||
++iter;
|
||||
while (iter != last)
|
||||
{
|
||||
if (*iter == '"')
|
||||
{
|
||||
++iter;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*iter == '\\')
|
||||
{
|
||||
// quoted-pair
|
||||
++iter;
|
||||
if (iter != last)
|
||||
e.append (1, *iter++);
|
||||
}
|
||||
else
|
||||
{
|
||||
// qdtext
|
||||
e.append (1, *iter++);
|
||||
}
|
||||
}
|
||||
if (! e.empty())
|
||||
{
|
||||
func (e);
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
else if (*iter == ',')
|
||||
{
|
||||
e = trim_right (e);
|
||||
if (! e.empty())
|
||||
{
|
||||
func (e);
|
||||
e.clear();
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
else if (is_lws (*iter))
|
||||
{
|
||||
++iter;
|
||||
}
|
||||
else
|
||||
{
|
||||
e.append (1, *iter++);
|
||||
}
|
||||
}
|
||||
|
||||
if (! e.empty())
|
||||
{
|
||||
e = trim_right (e);
|
||||
if (! e.empty())
|
||||
func (e);
|
||||
}
|
||||
}
|
||||
|
||||
} // rfc2616
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of Beast: https://github.com/vinniefalco/Beast
|
||||
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
|
||||
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
|
||||
@@ -17,29 +17,48 @@
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#if BEAST_INCLUDE_BEASTCONFIG
|
||||
#include "../../BeastConfig.h"
|
||||
#endif
|
||||
|
||||
#include <beast/http/basic_message.h>
|
||||
#include <beast/unit_test/suite.h>
|
||||
|
||||
namespace beast {
|
||||
namespace http {
|
||||
|
||||
class basic_message_tests : public unit_test::suite
|
||||
class basic_message_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void testCreate()
|
||||
std::pair <basic_message, bool>
|
||||
request (std::string const& text)
|
||||
{
|
||||
pass();
|
||||
basic_message m;
|
||||
basic_message::parser p (m, true);
|
||||
auto result (p.write (boost::asio::buffer(text)));
|
||||
auto result2 (p.eof());
|
||||
return std::make_pair (std::move(m), result.first);
|
||||
}
|
||||
|
||||
void run()
|
||||
void
|
||||
run()
|
||||
{
|
||||
testCreate();
|
||||
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["Field"] << "|";
|
||||
|
||||
pass();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(basic_message,http,beast);
|
||||
}
|
||||
}
|
||||
|
||||
} // http
|
||||
} // beast
|
||||
|
||||
93
beast/http/tests/rfc2616.test.cpp
Normal file
93
beast/http/tests/rfc2616.test.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
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 http {
|
||||
namespace rfc2616 {
|
||||
|
||||
class rfc2616_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
check (std::string const& value,
|
||||
std::vector <std::string> const& expected)
|
||||
{
|
||||
std::vector <std::string> parsed;
|
||||
for_each_element (value.begin(), value.end(),
|
||||
[&](std::string const& element)
|
||||
{
|
||||
parsed.push_back (element);
|
||||
});
|
||||
expect (parsed == expected);
|
||||
}
|
||||
|
||||
void
|
||||
run()
|
||||
{
|
||||
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"});
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(rfc2616,http,beast);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,11 +20,54 @@
|
||||
#ifndef BEAST_UTILITY_CI_CHAR_TRAITS_H_INCLUDED
|
||||
#define BEAST_UTILITY_CI_CHAR_TRAITS_H_INCLUDED
|
||||
|
||||
#include <beast/cxx14/algorithm.h> // <algorithm>
|
||||
#include <locale>
|
||||
#include <string>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Case-insensitive function object for performing less than comparisons. */
|
||||
struct ci_less
|
||||
{
|
||||
static bool const is_transparent = true;
|
||||
|
||||
template <class String>
|
||||
bool
|
||||
operator() (String const& lhs, String const& rhs) const
|
||||
{
|
||||
typedef typename String::value_type char_type;
|
||||
return std::lexicographical_compare (std::begin(lhs), std::end(lhs),
|
||||
std::begin(rhs), std::end(rhs),
|
||||
[] (char_type lhs, char_type rhs)
|
||||
{
|
||||
return std::tolower(lhs) < std::tolower(rhs);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/** Case-insensitive function object for performing equal to comparisons. */
|
||||
struct ci_equal_to
|
||||
{
|
||||
static bool const is_transparent = true;
|
||||
|
||||
template <class String>
|
||||
bool
|
||||
operator() (String const& lhs, String const& rhs) const
|
||||
{
|
||||
typedef typename String::value_type char_type;
|
||||
return std::equal (lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
|
||||
[] (char_type lhs, char_type rhs)
|
||||
{
|
||||
return std::tolower(lhs) == std::tolower(rhs);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// DEPRECATED VFALCO This causes far more problems than it solves!
|
||||
//
|
||||
/** Case insensitive character traits. */
|
||||
struct ci_char_traits : std::char_traits <char>
|
||||
{
|
||||
static
|
||||
|
||||
Reference in New Issue
Block a user