Files
beast/test/doc/http_examples.cpp
T

459 lines
12 KiB
C++
Raw Normal View History

2017-06-04 17:25:55 -07:00
//
2017-07-24 09:42:36 -07:00
// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
2017-06-04 17:25:55 -07:00
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
2017-07-20 13:40:34 -07:00
// Official repository: https://github.com/boostorg/beast
//
2017-06-04 17:25:55 -07:00
2017-06-14 20:26:44 -07:00
#include "example/doc/http_examples.hpp"
2017-06-04 17:25:55 -07:00
2017-07-20 13:40:34 -07:00
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/core/read_size.hpp>
#include <boost/beast/core/ostream.hpp>
#include <boost/beast/core/detail/clamp.hpp>
#include <boost/beast/http/chunk_encode.hpp>
#include <boost/beast/http/parser.hpp>
#include <boost/beast/http/read.hpp>
#include <boost/beast/http/write.hpp>
#include <boost/beast/test/pipe_stream.hpp>
#include <boost/beast/test/string_istream.hpp>
#include <boost/beast/test/string_ostream.hpp>
#include <boost/beast/test/yield_to.hpp>
#include <boost/beast/unit_test/suite.hpp>
2017-06-04 17:25:55 -07:00
#include <sstream>
2017-06-26 13:28:55 +02:00
#include <array>
2017-07-12 18:06:36 -07:00
#include <limits>
2017-06-26 13:28:55 +02:00
#include <list>
2017-07-21 08:55:25 -07:00
#include <sstream>
2017-07-12 18:06:36 -07:00
#include <vector>
2017-06-04 17:25:55 -07:00
2017-07-20 13:40:34 -07:00
namespace boost {
2017-06-04 17:25:55 -07:00
namespace beast {
namespace http {
2017-07-28 18:59:14 -07:00
class examples_test
2017-06-04 17:25:55 -07:00
: public beast::unit_test::suite
, public beast::test::enable_yield_to
{
public:
// two threads, for some examples using a pipe
2017-07-28 18:59:14 -07:00
examples_test()
2017-06-04 17:25:55 -07:00
: enable_yield_to(2)
{
}
2017-07-21 08:55:25 -07:00
template<bool isRequest, class Body, class Fields>
static
std::string
to_string(message<isRequest, Body, Fields> const& m)
{
std::stringstream ss;
ss << m;
return ss.str();
}
template<class ConstBufferSequence>
static
std::string
to_string(ConstBufferSequence const& bs)
{
using boost::asio::buffer_cast;
using boost::asio::buffer_size;
std::string s;
s.reserve(buffer_size(bs));
for(boost::asio::const_buffer b : bs)
s.append(buffer_cast<char const*>(b),
buffer_size(b));
return s;
}
2017-06-04 17:25:55 -07:00
template<bool isRequest>
bool
equal_body(string_view sv, string_view body)
{
test::string_istream si{
get_io_service(), sv.to_string()};
message<isRequest, string_body, fields> m;
multi_buffer b;
try
{
read(si, b, m);
return m.body == body;
}
catch(std::exception const& e)
{
log << "equal_body: " << e.what() << std::endl;
return false;
}
}
void
doExpect100Continue()
{
test::pipe p{ios_};
yield_to(
[&](yield_context)
{
error_code ec;
flat_buffer buffer;
receive_expect_100_continue(
p.server, buffer, ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
2017-06-04 17:25:55 -07:00
},
[&](yield_context)
{
flat_buffer buffer;
2017-06-05 19:28:17 -07:00
request<string_body> req;
2017-06-04 17:25:55 -07:00
req.version = 11;
2017-06-06 17:26:11 -07:00
req.method_string("POST");
2017-06-04 17:25:55 -07:00
req.target("/");
2017-06-06 12:49:03 -07:00
req.insert(field::user_agent, "test");
2017-06-04 17:25:55 -07:00
req.body = "Hello, world!";
req.prepare_payload();
2017-06-04 17:25:55 -07:00
error_code ec;
send_expect_100_continue(
p.client, buffer, req, ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
2017-06-04 17:25:55 -07:00
});
}
void
doCgiResponse()
{
std::string const s = "Hello, world!";
test::pipe child{ios_};
child.server.read_size(3);
ostream(child.server.buffer) << s;
child.client.close();
test::pipe p{ios_};
error_code ec;
send_cgi_response(child.server, p.client, ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(equal_body<false>(p.server.str(), s));
2017-06-04 17:25:55 -07:00
}
void
doRelay()
{
2017-06-05 19:28:17 -07:00
request<string_body> req;
2017-06-04 17:25:55 -07:00
req.version = 11;
2017-06-06 17:26:11 -07:00
req.method_string("POST");
2017-06-04 17:25:55 -07:00
req.target("/");
2017-06-06 12:49:03 -07:00
req.insert(field::user_agent, "test");
2017-06-04 17:25:55 -07:00
req.body = "Hello, world!";
req.prepare_payload();
2017-06-04 17:25:55 -07:00
test::pipe downstream{ios_};
downstream.server.read_size(3);
test::pipe upstream{ios_};
upstream.client.write_size(3);
error_code ec;
write(downstream.client, req);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
2017-06-04 17:25:55 -07:00
downstream.client.close();
flat_buffer buffer;
relay<true>(upstream.client, downstream.server, buffer, ec,
2017-06-18 14:57:32 -07:00
[&](header<true, fields>& h, error_code& ev)
2017-06-04 17:25:55 -07:00
{
2017-06-18 14:57:32 -07:00
ev = {};
2017-06-05 19:28:17 -07:00
h.erase("Content-Length");
2017-06-15 06:55:43 -07:00
h.set("Transfer-Encoding", "chunked");
2017-06-04 17:25:55 -07:00
});
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(equal_body<true>(
2017-06-04 17:25:55 -07:00
upstream.server.str(), req.body));
}
void
doReadStdStream()
{
std::string const s =
"HTTP/1.0 200 OK\r\n"
"User-Agent: test\r\n"
"\r\n"
"Hello, world!";
std::istringstream is(s);
error_code ec;
flat_buffer buffer;
response<string_body> res;
read_istream(is, buffer, res, ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(to_string(res) == s);
2017-06-04 17:25:55 -07:00
}
void
doWriteStdStream()
{
std::ostringstream os;
request<string_body> req;
req.version = 11;
req.method(verb::get);
req.target("/");
2017-06-06 12:49:03 -07:00
req.insert(field::user_agent, "test");
2017-06-04 17:25:55 -07:00
error_code ec;
write_ostream(os, req, ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(to_string(req) == os.str());
2017-06-04 17:25:55 -07:00
}
void
doCustomParser()
{
{
string_view s{
"POST / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Length: 13\r\n"
"\r\n"
"Hello, world!"
};
error_code ec;
custom_parser<true> p;
p.put(boost::asio::buffer(
s.data(), s.size()), ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
2017-06-04 17:25:55 -07:00
}
{
string_view s{
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"d\r\n"
"Hello, world!"
"\r\n"
"0\r\n\r\n"
};
error_code ec;
custom_parser<false> p;
p.put(boost::asio::buffer(
s.data(), s.size()), ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
2017-06-04 17:25:55 -07:00
}
}
void
doHEAD()
{
test::pipe p{ios_};
yield_to(
[&](yield_context)
{
error_code ec;
flat_buffer buffer;
do_server_head(p.server, buffer, ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
2017-06-04 17:25:55 -07:00
},
[&](yield_context)
{
error_code ec;
flat_buffer buffer;
auto res = do_head_request(p.client, buffer, "/", ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
2017-06-04 17:25:55 -07:00
});
}
struct handler
{
std::string body;
template<class Body>
void
operator()(request<Body>&&)
{
}
void
operator()(request<string_body>&& req)
{
body = req.body;
}
};
2017-06-04 17:25:55 -07:00
void
doDeferredBody()
{
test::pipe p{ios_};
ostream(p.server.buffer) <<
"POST / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"Content-Type: multipart/form-data\r\n"
2017-06-04 17:25:55 -07:00
"Content-Length: 13\r\n"
"\r\n"
"Hello, world!";
handler h;
2017-06-04 17:25:55 -07:00
flat_buffer buffer;
do_form_request(p.server, buffer, h);
2017-07-25 12:35:54 -07:00
BEAST_EXPECT(h.body == "Hello, world!");
2017-06-04 17:25:55 -07:00
}
2017-06-08 22:03:58 -07:00
//--------------------------------------------------------------------------
void
doIncrementalRead()
{
test::pipe c{ios_};
std::string s(2048, '*');
ostream(c.server.buffer) <<
"HTTP/1.1 200 OK\r\n"
"Content-Length: 2048\r\n"
"Server: test\r\n"
"\r\n" <<
s;
error_code ec;
flat_buffer b;
std::stringstream ss;
read_and_print_body<false>(ss, c.server, b, ec);
2017-07-25 12:35:54 -07:00
if(BEAST_EXPECTS(! ec, ec.message()))
BEAST_EXPECT(ss.str() == s);
}
//--------------------------------------------------------------------------
2017-07-09 20:09:30 -07:00
void
doExplicitChunkSerialize()
{
auto const buf =
[](string_view s)
{
return boost::asio::const_buffers_1{
s.data(), s.size()};
};
test::pipe p{ios_};
response<empty_body> res{status::ok, 11};
res.set(field::server, "test");
res.set(field::accept, "Expires, Content-MD5");
res.chunked(true);
error_code ec;
response_serializer<empty_body> sr{res};
write_header(p.client, sr, ec);
chunk_extensions exts;
boost::asio::write(p.client,
make_chunk(buf("First")), ec);
exts.insert("quality", "1.0");
boost::asio::write(p.client,
make_chunk(buf("Hello, world!"), exts), ec);
exts.clear();
exts.insert("file", "abc.txt");
exts.insert("quality", "0.7");
boost::asio::write(p.client,
make_chunk(buf("The Next Chunk"), std::move(exts)), ec);
exts.clear();
exts.insert("last");
boost::asio::write(p.client,
make_chunk(buf("Last one"), std::move(exts),
std::allocator<double>{}), ec);
fields trailers;
trailers.set(field::expires, "never");
trailers.set(field::content_md5, "f4a5c16584f03d90");
boost::asio::write(p.client,
make_chunk_last(
trailers,
std::allocator<double>{}
), ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECT(
2017-07-21 08:55:25 -07:00
to_string(p.server.buffer.data()) ==
2017-07-09 20:09:30 -07:00
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
"Accept: Expires, Content-MD5\r\n"
"Transfer-Encoding: chunked\r\n"
"\r\n"
"5\r\n"
"First\r\n"
"d;quality=1.0\r\n"
"Hello, world!\r\n"
"e;file=abc.txt;quality=0.7\r\n"
"The Next Chunk\r\n"
"8;last\r\n"
"Last one\r\n"
"0\r\n"
"Expires: never\r\n"
"Content-MD5: f4a5c16584f03d90\r\n"
"\r\n");
}
//--------------------------------------------------------------------------
void
doExplicitChunkParse()
{
test::pipe c{ios_};
ostream(c.client.buffer) <<
"HTTP/1.1 200 OK\r\n"
"Server: test\r\n"
2017-07-12 18:06:36 -07:00
"Trailer: Expires, Content-MD5\r\n"
2017-07-09 20:09:30 -07:00
"Transfer-Encoding: chunked\r\n"
"\r\n"
"5\r\n"
"First\r\n"
"d;quality=1.0\r\n"
"Hello, world!\r\n"
"e;file=abc.txt;quality=0.7\r\n"
"The Next Chunk\r\n"
"8;last\r\n"
"Last one\r\n"
"0\r\n"
"Expires: never\r\n"
"Content-MD5: f4a5c16584f03d90\r\n"
"\r\n";
2017-07-12 18:06:36 -07:00
error_code ec;
flat_buffer b;
std::stringstream ss;
print_chunked_body<false>(ss, c.client, b, ec);
2017-07-25 12:35:54 -07:00
BEAST_EXPECTS(! ec, ec.message());
BEAST_EXPECT(ss.str() ==
2017-07-12 18:06:36 -07:00
"Chunk Body: First\n"
"Extension: quality = 1.0\n"
"Chunk Body: Hello, world!\n"
"Extension: file = abc.txt\n"
"Extension: quality = 0.7\n"
"Chunk Body: The Next Chunk\n"
"Extension: last\n"
"Chunk Body: Last one\n"
"Expires: never\n"
"Content-MD5: f4a5c16584f03d90\n");
2017-07-09 20:09:30 -07:00
}
//--------------------------------------------------------------------------
2017-06-04 17:25:55 -07:00
void
run()
{
doExpect100Continue();
doCgiResponse();
doRelay();
doReadStdStream();
doWriteStdStream();
doCustomParser();
doHEAD();
doDeferredBody();
doIncrementalRead();
2017-07-09 20:09:30 -07:00
doExplicitChunkSerialize();
doExplicitChunkParse();
2017-06-04 17:25:55 -07:00
}
};
2017-08-01 17:01:57 -07:00
BEAST_DEFINE_TESTSUITE(beast,http,examples);
2017-06-04 17:25:55 -07:00
} // http
} // beast
2017-07-20 13:40:34 -07:00
} // boost