libnghttp2_asio: Add request::run_task to execute task in separate thread
This commit is contained in:
parent
409316018d
commit
88d7abcc23
|
@ -59,7 +59,7 @@ endif # HAVE_EPOLL
|
||||||
|
|
||||||
if ENABLE_ASIO_LIB
|
if ENABLE_ASIO_LIB
|
||||||
|
|
||||||
noinst_PROGRAMS += asio-sv asio-sv2
|
noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3
|
||||||
|
|
||||||
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
||||||
ASIOLDFLAGS = @JEMALLOC_LIBS@
|
ASIOLDFLAGS = @JEMALLOC_LIBS@
|
||||||
|
@ -75,6 +75,11 @@ asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
asio_sv2_LDFLAGS = ${ASIOLDFLAGS}
|
asio_sv2_LDFLAGS = ${ASIOLDFLAGS}
|
||||||
asio_sv2_LDADD = ${ASIOLDADD}
|
asio_sv2_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
|
asio_sv3_SOURCES = asio-sv3.cc
|
||||||
|
asio_sv3_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||||
|
asio_sv3_LDFLAGS = ${ASIOLDFLAGS}
|
||||||
|
asio_sv3_LDADD = ${ASIOLDADD}
|
||||||
|
|
||||||
endif # ENABLE_ASIO_LIB
|
endif # ENABLE_ASIO_LIB
|
||||||
|
|
||||||
endif # ENABLE_EXAMPLES
|
endif # ENABLE_EXAMPLES
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* nghttp2 - HTTP/2 C Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2014 Tatsuhiro Tsujikawa
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
// We wrote this code based on the original code which has the
|
||||||
|
// following license:
|
||||||
|
//
|
||||||
|
// main.cpp
|
||||||
|
// ~~~~~~~~
|
||||||
|
//
|
||||||
|
// Copyright (c) 2003-2013 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
using namespace nghttp2::asio_http2;
|
||||||
|
using namespace nghttp2::asio_http2::server;
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// Check command line arguments.
|
||||||
|
if (argc < 4) {
|
||||||
|
std::cerr << "Usage: asio-sv3 <port> <threads> <tasks> "
|
||||||
|
<< " <private-key-file> <cert-file>\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t port = std::stoi(argv[1]);
|
||||||
|
std::size_t num_threads = std::stoi(argv[2]);
|
||||||
|
std::size_t num_concurrent_tasks = std::stoi(argv[3]);
|
||||||
|
|
||||||
|
http2 server;
|
||||||
|
|
||||||
|
server.num_threads(num_threads);
|
||||||
|
|
||||||
|
if(argc >= 5) {
|
||||||
|
server.tls(argv[4], argv[5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.num_concurrent_tasks(num_concurrent_tasks);
|
||||||
|
|
||||||
|
server.listen
|
||||||
|
("*", port,
|
||||||
|
[](std::shared_ptr<request> req, std::shared_ptr<response> res)
|
||||||
|
{
|
||||||
|
res->write_head(200);
|
||||||
|
|
||||||
|
auto msgq = std::make_shared<std::deque<std::string>>();
|
||||||
|
|
||||||
|
res->end
|
||||||
|
([msgq](uint8_t *buf, std::size_t len) -> std::pair<ssize_t, bool>
|
||||||
|
{
|
||||||
|
if(msgq->empty()) {
|
||||||
|
// if msgq is empty, tells the library that don't call
|
||||||
|
// this callback until we call res->resume(). This is
|
||||||
|
// done by returing std::make_pair(0, false).
|
||||||
|
return std::make_pair(0, false);
|
||||||
|
}
|
||||||
|
auto msg = std::move(msgq->front());
|
||||||
|
msgq->pop_front();
|
||||||
|
|
||||||
|
if(msg.empty()) {
|
||||||
|
// The empty message signals the end of response in
|
||||||
|
// this simple protocol.
|
||||||
|
return std::make_pair(0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nwrite = std::min(len, msg.size());
|
||||||
|
std::copy(std::begin(msg), std::begin(msg) + nwrite, buf);
|
||||||
|
if(msg.size() > nwrite) {
|
||||||
|
msgq->push_front(msg.substr(nwrite));
|
||||||
|
}
|
||||||
|
return std::make_pair(nwrite, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
req->run_task
|
||||||
|
([res, msgq](channel& channel)
|
||||||
|
{
|
||||||
|
// executed in different thread from request callback
|
||||||
|
// was called.
|
||||||
|
|
||||||
|
// Using res and msgq is not safe inside this callback.
|
||||||
|
// But using them in callback passed to channel::post is
|
||||||
|
// safe.
|
||||||
|
|
||||||
|
// We just emit simple message "message N\n" in every 1
|
||||||
|
// second and 3 times in total.
|
||||||
|
for(std::size_t i = 0; i < 3; ++i) {
|
||||||
|
msgq->push_back("message " + std::to_string(i + 1) + "\n");
|
||||||
|
|
||||||
|
channel.post([res]()
|
||||||
|
{
|
||||||
|
// executed in same thread where
|
||||||
|
// request callback was called.
|
||||||
|
|
||||||
|
// Tells library we have new message.
|
||||||
|
res->resume();
|
||||||
|
});
|
||||||
|
|
||||||
|
sleep(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send empty message to signal the end of response
|
||||||
|
// body.
|
||||||
|
msgq->push_back("");
|
||||||
|
|
||||||
|
channel.post([res]()
|
||||||
|
{
|
||||||
|
// executed in same thread where request
|
||||||
|
// callback was called.
|
||||||
|
res->resume();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
std::cerr << "exception: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -64,9 +64,12 @@ class connection
|
||||||
public:
|
public:
|
||||||
/// Construct a connection with the given io_service.
|
/// Construct a connection with the given io_service.
|
||||||
template<typename... SocketArgs>
|
template<typename... SocketArgs>
|
||||||
explicit connection(request_cb cb, SocketArgs&&... args)
|
explicit connection(request_cb cb,
|
||||||
|
boost::asio::io_service& task_io_service,
|
||||||
|
SocketArgs&&... args)
|
||||||
: socket_(std::forward<SocketArgs>(args)...),
|
: socket_(std::forward<SocketArgs>(args)...),
|
||||||
request_cb_(std::move(cb)),
|
request_cb_(std::move(cb)),
|
||||||
|
task_io_service_(task_io_service),
|
||||||
writing_(false)
|
writing_(false)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -75,6 +78,7 @@ public:
|
||||||
{
|
{
|
||||||
handler_ = std::make_shared<http2_handler>
|
handler_ = std::make_shared<http2_handler>
|
||||||
(socket_.get_io_service(),
|
(socket_.get_io_service(),
|
||||||
|
task_io_service_,
|
||||||
[this]()
|
[this]()
|
||||||
{
|
{
|
||||||
do_write();
|
do_write();
|
||||||
|
@ -168,6 +172,8 @@ private:
|
||||||
|
|
||||||
request_cb request_cb_;
|
request_cb request_cb_;
|
||||||
|
|
||||||
|
boost::asio::io_service& task_io_service_;
|
||||||
|
|
||||||
std::shared_ptr<http2_handler> handler_;
|
std::shared_ptr<http2_handler> handler_;
|
||||||
|
|
||||||
/// Buffer for incoming data.
|
/// Buffer for incoming data.
|
||||||
|
|
|
@ -97,6 +97,11 @@ void request::on_end(void_cb cb)
|
||||||
return impl_->on_end(std::move(cb));
|
return impl_->on_end(std::move(cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool request::run_task(thread_cb start)
|
||||||
|
{
|
||||||
|
return impl_->run_task(std::move(start));
|
||||||
|
}
|
||||||
|
|
||||||
request_impl& request::impl()
|
request_impl& request::impl()
|
||||||
{
|
{
|
||||||
return *impl_;
|
return *impl_;
|
||||||
|
@ -122,6 +127,11 @@ void response::end(read_cb cb)
|
||||||
impl_->end(std::move(cb));
|
impl_->end(std::move(cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void response::resume()
|
||||||
|
{
|
||||||
|
impl_->resume();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int response::status_code() const
|
unsigned int response::status_code() const
|
||||||
{
|
{
|
||||||
return impl_->status_code();
|
return impl_->status_code();
|
||||||
|
@ -209,7 +219,7 @@ void request_impl::path(std::string arg)
|
||||||
bool request_impl::push(std::string method, std::string path,
|
bool request_impl::push(std::string method, std::string path,
|
||||||
std::vector<header> headers)
|
std::vector<header> headers)
|
||||||
{
|
{
|
||||||
if(handler_.expired() || stream_.expired()) {
|
if(closed()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,6 +255,17 @@ void request_impl::on_end(void_cb cb)
|
||||||
on_end_cb_ = std::move(cb);
|
on_end_cb_ = std::move(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool request_impl::run_task(thread_cb start)
|
||||||
|
{
|
||||||
|
if(closed()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto handler = handler_.lock();
|
||||||
|
|
||||||
|
return handler->run_task(std::move(start));
|
||||||
|
}
|
||||||
|
|
||||||
void request_impl::handler(std::weak_ptr<http2_handler> h)
|
void request_impl::handler(std::weak_ptr<http2_handler> h)
|
||||||
{
|
{
|
||||||
handler_ = std::move(h);
|
handler_ = std::move(h);
|
||||||
|
@ -311,7 +332,7 @@ void response_impl::end(std::string data)
|
||||||
|
|
||||||
void response_impl::end(read_cb cb)
|
void response_impl::end(read_cb cb)
|
||||||
{
|
{
|
||||||
if(started_ || handler_.expired() || stream_.expired()) {
|
if(started_ || closed()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,6 +352,26 @@ void response_impl::end(read_cb cb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool response_impl::closed() const
|
||||||
|
{
|
||||||
|
return handler_.expired() || stream_.expired();
|
||||||
|
}
|
||||||
|
|
||||||
|
void response_impl::resume()
|
||||||
|
{
|
||||||
|
if(closed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto handler = handler_.lock();
|
||||||
|
auto stream = stream_.lock();
|
||||||
|
handler->resume(*stream);
|
||||||
|
|
||||||
|
if(!handler->inside_callback()) {
|
||||||
|
handler->initiate_write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool response_impl::started() const
|
bool response_impl::started() const
|
||||||
{
|
{
|
||||||
return started_;
|
return started_;
|
||||||
|
@ -361,6 +402,34 @@ std::pair<ssize_t, bool> response_impl::call_read
|
||||||
return std::make_pair(0, true);
|
return std::make_pair(0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
channel::channel()
|
||||||
|
: impl_(util::make_unique<channel_impl>())
|
||||||
|
{}
|
||||||
|
|
||||||
|
void channel::post(void_cb cb)
|
||||||
|
{
|
||||||
|
impl_->post(std::move(cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
channel_impl& channel::impl()
|
||||||
|
{
|
||||||
|
return *impl_;
|
||||||
|
}
|
||||||
|
|
||||||
|
channel_impl::channel_impl()
|
||||||
|
: strand_(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void channel_impl::post(void_cb cb)
|
||||||
|
{
|
||||||
|
strand_->post(std::move(cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
void channel_impl::strand(boost::asio::io_service::strand *strand)
|
||||||
|
{
|
||||||
|
strand_ = strand;
|
||||||
|
}
|
||||||
|
|
||||||
http2_stream::http2_stream(int32_t stream_id)
|
http2_stream::http2_stream(int32_t stream_id)
|
||||||
: request_(std::make_shared<request>()),
|
: request_(std::make_shared<request>()),
|
||||||
response_(std::make_shared<response>()),
|
response_(std::make_shared<response>()),
|
||||||
|
@ -608,10 +677,14 @@ int on_frame_not_send_callback
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
http2_handler::http2_handler
|
http2_handler::http2_handler
|
||||||
(boost::asio::io_service& io_service, connection_write writefun, request_cb cb)
|
(boost::asio::io_service& io_service,
|
||||||
|
boost::asio::io_service& task_io_service_,
|
||||||
|
connection_write writefun, request_cb cb)
|
||||||
: writefun_(writefun),
|
: writefun_(writefun),
|
||||||
request_cb_(std::move(cb)),
|
request_cb_(std::move(cb)),
|
||||||
io_service_(io_service),
|
io_service_(io_service),
|
||||||
|
task_io_service_(task_io_service_),
|
||||||
|
strand_(std::make_shared<boost::asio::io_service::strand>(io_service_)),
|
||||||
session_(nullptr),
|
session_(nullptr),
|
||||||
buf_(nullptr),
|
buf_(nullptr),
|
||||||
buflen_(0),
|
buflen_(0),
|
||||||
|
@ -789,6 +862,11 @@ void http2_handler::initiate_write()
|
||||||
writefun_();
|
writefun_();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void http2_handler::resume(http2_stream& stream)
|
||||||
|
{
|
||||||
|
nghttp2_session_resume_data(session_, stream.get_stream_id());
|
||||||
|
}
|
||||||
|
|
||||||
int http2_handler::push_promise(http2_stream& stream, std::string method,
|
int http2_handler::push_promise(http2_stream& stream, std::string method,
|
||||||
std::string path,
|
std::string path,
|
||||||
std::vector<header> headers)
|
std::vector<header> headers)
|
||||||
|
@ -837,6 +915,26 @@ int http2_handler::push_promise(http2_stream& stream, std::string method,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool http2_handler::run_task(thread_cb start)
|
||||||
|
{
|
||||||
|
auto strand = strand_;
|
||||||
|
|
||||||
|
try {
|
||||||
|
task_io_service_.post
|
||||||
|
([start, strand]()
|
||||||
|
{
|
||||||
|
channel chan;
|
||||||
|
chan.impl().strand(strand.get());
|
||||||
|
|
||||||
|
start(chan);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch(std::exception& ex) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boost::asio::io_service& http2_handler::io_service()
|
boost::asio::io_service& http2_handler::io_service()
|
||||||
{
|
{
|
||||||
return io_service_;
|
return io_service_;
|
||||||
|
|
|
@ -65,6 +65,8 @@ public:
|
||||||
void on_data(data_cb cb);
|
void on_data(data_cb cb);
|
||||||
void on_end(void_cb cb);
|
void on_end(void_cb cb);
|
||||||
|
|
||||||
|
bool run_task(thread_cb start);
|
||||||
|
|
||||||
void set_header(std::vector<header> headers);
|
void set_header(std::vector<header> headers);
|
||||||
void add_header(std::string name, std::string value);
|
void add_header(std::string name, std::string value);
|
||||||
void method(std::string method);
|
void method(std::string method);
|
||||||
|
@ -97,6 +99,8 @@ public:
|
||||||
void write_head(unsigned int status_code, std::vector<header> headers = {});
|
void write_head(unsigned int status_code, std::vector<header> headers = {});
|
||||||
void end(std::string data = "");
|
void end(std::string data = "");
|
||||||
void end(read_cb cb);
|
void end(read_cb cb);
|
||||||
|
void resume();
|
||||||
|
bool closed() const;
|
||||||
|
|
||||||
unsigned int status_code() const;
|
unsigned int status_code() const;
|
||||||
const std::vector<header>& headers() const;
|
const std::vector<header>& headers() const;
|
||||||
|
@ -113,6 +117,15 @@ private:
|
||||||
bool started_;
|
bool started_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class channel_impl {
|
||||||
|
public:
|
||||||
|
channel_impl();
|
||||||
|
void post(void_cb cb);
|
||||||
|
void strand(boost::asio::io_service::strand *strand);
|
||||||
|
private:
|
||||||
|
boost::asio::io_service::strand *strand_;
|
||||||
|
};
|
||||||
|
|
||||||
class http2_stream {
|
class http2_stream {
|
||||||
public:
|
public:
|
||||||
http2_stream(int32_t stream_id);
|
http2_stream(int32_t stream_id);
|
||||||
|
@ -137,6 +150,7 @@ typedef std::function<void(void)> connection_write;
|
||||||
class http2_handler : public std::enable_shared_from_this<http2_handler> {
|
class http2_handler : public std::enable_shared_from_this<http2_handler> {
|
||||||
public:
|
public:
|
||||||
http2_handler(boost::asio::io_service& io_service,
|
http2_handler(boost::asio::io_service& io_service,
|
||||||
|
boost::asio::io_service& task_io_service,
|
||||||
connection_write writefun,
|
connection_write writefun,
|
||||||
request_cb cb);
|
request_cb cb);
|
||||||
|
|
||||||
|
@ -162,10 +176,14 @@ public:
|
||||||
void leave_callback();
|
void leave_callback();
|
||||||
bool inside_callback() const;
|
bool inside_callback() const;
|
||||||
|
|
||||||
|
void resume(http2_stream& stream);
|
||||||
|
|
||||||
int push_promise(http2_stream& stream, std::string method,
|
int push_promise(http2_stream& stream, std::string method,
|
||||||
std::string path,
|
std::string path,
|
||||||
std::vector<header> headers);
|
std::vector<header> headers);
|
||||||
|
|
||||||
|
bool run_task(thread_cb start);
|
||||||
|
|
||||||
boost::asio::io_service& io_service();
|
boost::asio::io_service& io_service();
|
||||||
|
|
||||||
template<size_t N>
|
template<size_t N>
|
||||||
|
@ -231,6 +249,8 @@ private:
|
||||||
connection_write writefun_;
|
connection_write writefun_;
|
||||||
request_cb request_cb_;
|
request_cb request_cb_;
|
||||||
boost::asio::io_service& io_service_;
|
boost::asio::io_service& io_service_;
|
||||||
|
boost::asio::io_service& task_io_service_;
|
||||||
|
std::shared_ptr<boost::asio::io_service::strand> strand_;
|
||||||
nghttp2_session *session_;
|
nghttp2_session *session_;
|
||||||
const uint8_t *buf_;
|
const uint8_t *buf_;
|
||||||
std::size_t buflen_;
|
std::size_t buflen_;
|
||||||
|
|
|
@ -63,8 +63,14 @@ void http2::tls(std::string private_key_file,
|
||||||
impl_->tls(std::move(private_key_file), std::move(certificate_file));
|
impl_->tls(std::move(private_key_file), std::move(certificate_file));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void http2::num_concurrent_tasks(size_t num_concurrent_tasks)
|
||||||
|
{
|
||||||
|
impl_->num_concurrent_tasks(num_concurrent_tasks);
|
||||||
|
}
|
||||||
|
|
||||||
http2_impl::http2_impl()
|
http2_impl::http2_impl()
|
||||||
: num_threads_(1)
|
: num_threads_(1),
|
||||||
|
num_concurrent_tasks_(1)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -126,7 +132,8 @@ void http2_impl::listen(const std::string& address, uint16_t port,
|
||||||
}, nullptr);
|
}, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
server(address, port, num_threads_, std::move(cb), std::move(ssl_ctx)).run();
|
server(address, port, num_threads_, num_concurrent_tasks_,
|
||||||
|
std::move(cb), std::move(ssl_ctx)).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
void http2_impl::num_threads(size_t num_threads)
|
void http2_impl::num_threads(size_t num_threads)
|
||||||
|
@ -141,6 +148,11 @@ void http2_impl::tls(std::string private_key_file,
|
||||||
certificate_file_ = std::move(certificate_file);
|
certificate_file_ = std::move(certificate_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void http2_impl::num_concurrent_tasks(size_t num_concurrent_tasks)
|
||||||
|
{
|
||||||
|
num_concurrent_tasks_ = num_concurrent_tasks;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T, typename F>
|
template<typename T, typename F>
|
||||||
std::shared_ptr<util::Defer<T, F>> defer_shared(T&& t, F f)
|
std::shared_ptr<util::Defer<T, F>> defer_shared(T&& t, F f)
|
||||||
{
|
{
|
||||||
|
|
|
@ -44,11 +44,13 @@ public:
|
||||||
request_cb cb);
|
request_cb cb);
|
||||||
void num_threads(size_t num_threads);
|
void num_threads(size_t num_threads);
|
||||||
void tls(std::string private_key_file, std::string certificate_file);
|
void tls(std::string private_key_file, std::string certificate_file);
|
||||||
|
void num_concurrent_tasks(size_t num_concurrent_tasks);
|
||||||
private:
|
private:
|
||||||
std::string private_key_file_;
|
std::string private_key_file_;
|
||||||
std::string certificate_file_;
|
std::string certificate_file_;
|
||||||
std::unique_ptr<server> server_;
|
std::unique_ptr<server> server_;
|
||||||
std::size_t num_threads_;
|
std::size_t num_threads_;
|
||||||
|
std::size_t num_concurrent_tasks_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|
|
@ -46,8 +46,10 @@ namespace asio_http2 {
|
||||||
|
|
||||||
namespace server {
|
namespace server {
|
||||||
|
|
||||||
io_service_pool::io_service_pool(std::size_t pool_size)
|
io_service_pool::io_service_pool(std::size_t pool_size,
|
||||||
: next_io_service_(0)
|
std::size_t thread_pool_size)
|
||||||
|
: next_io_service_(0),
|
||||||
|
thread_pool_size_(thread_pool_size)
|
||||||
{
|
{
|
||||||
if (pool_size == 0) {
|
if (pool_size == 0) {
|
||||||
throw std::runtime_error("io_service_pool size is 0");
|
throw std::runtime_error("io_service_pool size is 0");
|
||||||
|
@ -55,17 +57,28 @@ io_service_pool::io_service_pool(std::size_t pool_size)
|
||||||
|
|
||||||
// Give all the io_services work to do so that their run() functions will not
|
// Give all the io_services work to do so that their run() functions will not
|
||||||
// exit until they are explicitly stopped.
|
// exit until they are explicitly stopped.
|
||||||
for (std::size_t i = 0; i < pool_size; ++i)
|
for (std::size_t i = 0; i < pool_size; ++i) {
|
||||||
{
|
|
||||||
auto io_service = std::make_shared<boost::asio::io_service>();
|
auto io_service = std::make_shared<boost::asio::io_service>();
|
||||||
auto work = std::make_shared<boost::asio::io_service::work>(*io_service);
|
auto work = std::make_shared<boost::asio::io_service::work>(*io_service);
|
||||||
io_services_.push_back(io_service);
|
io_services_.push_back(io_service);
|
||||||
work_.push_back(work);
|
work_.push_back(work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto work = std::make_shared<boost::asio::io_service::work>
|
||||||
|
(task_io_service_);
|
||||||
|
work_.push_back(work);
|
||||||
}
|
}
|
||||||
|
|
||||||
void io_service_pool::run()
|
void io_service_pool::run()
|
||||||
{
|
{
|
||||||
|
for(std::size_t i = 0; i < thread_pool_size_; ++i) {
|
||||||
|
thread_pool_.create_thread
|
||||||
|
([this]()
|
||||||
|
{
|
||||||
|
task_io_service_.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Create a pool of threads to run all of the io_services.
|
// Create a pool of threads to run all of the io_services.
|
||||||
auto futs = std::vector<std::future<std::size_t>>();
|
auto futs = std::vector<std::future<std::size_t>>();
|
||||||
|
|
||||||
|
@ -81,6 +94,8 @@ void io_service_pool::run()
|
||||||
for (auto& fut : futs) {
|
for (auto& fut : futs) {
|
||||||
fut.get();
|
fut.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread_pool_.join_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
void io_service_pool::stop()
|
void io_service_pool::stop()
|
||||||
|
@ -89,6 +104,8 @@ void io_service_pool::stop()
|
||||||
for (auto& iosv : io_services_) {
|
for (auto& iosv : io_services_) {
|
||||||
iosv->stop();
|
iosv->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task_io_service_.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::asio::io_service& io_service_pool::get_io_service()
|
boost::asio::io_service& io_service_pool::get_io_service()
|
||||||
|
@ -102,6 +119,11 @@ boost::asio::io_service& io_service_pool::get_io_service()
|
||||||
return io_service;
|
return io_service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::asio::io_service& io_service_pool::get_task_io_service()
|
||||||
|
{
|
||||||
|
return task_io_service_;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|
||||||
} // namespace asio_http2
|
} // namespace asio_http2
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
#include <nghttp2/asio_http2.h>
|
#include <nghttp2/asio_http2.h>
|
||||||
|
|
||||||
|
@ -58,7 +59,8 @@ class io_service_pool
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Construct the io_service pool.
|
/// Construct the io_service pool.
|
||||||
explicit io_service_pool(std::size_t pool_size);
|
explicit io_service_pool(std::size_t pool_size,
|
||||||
|
std::size_t thread_pool_size);
|
||||||
|
|
||||||
/// Run all io_service objects in the pool.
|
/// Run all io_service objects in the pool.
|
||||||
void run();
|
void run();
|
||||||
|
@ -69,6 +71,8 @@ public:
|
||||||
/// Get an io_service to use.
|
/// Get an io_service to use.
|
||||||
boost::asio::io_service& get_io_service();
|
boost::asio::io_service& get_io_service();
|
||||||
|
|
||||||
|
boost::asio::io_service& get_task_io_service();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::shared_ptr<boost::asio::io_service> io_service_ptr;
|
typedef std::shared_ptr<boost::asio::io_service> io_service_ptr;
|
||||||
typedef std::shared_ptr<boost::asio::io_service::work> work_ptr;
|
typedef std::shared_ptr<boost::asio::io_service::work> work_ptr;
|
||||||
|
@ -76,11 +80,16 @@ private:
|
||||||
/// The pool of io_services.
|
/// The pool of io_services.
|
||||||
std::vector<io_service_ptr> io_services_;
|
std::vector<io_service_ptr> io_services_;
|
||||||
|
|
||||||
|
boost::asio::io_service task_io_service_;
|
||||||
|
boost::thread_group thread_pool_;
|
||||||
|
|
||||||
/// The work that keeps the io_services running.
|
/// The work that keeps the io_services running.
|
||||||
std::vector<work_ptr> work_;
|
std::vector<work_ptr> work_;
|
||||||
|
|
||||||
/// The next io_service to use for a connection.
|
/// The next io_service to use for a connection.
|
||||||
std::size_t next_io_service_;
|
std::size_t next_io_service_;
|
||||||
|
|
||||||
|
std::size_t thread_pool_size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace server
|
} // namespace server
|
||||||
|
|
|
@ -44,9 +44,10 @@ namespace server {
|
||||||
|
|
||||||
server::server(const std::string& address, uint16_t port,
|
server::server(const std::string& address, uint16_t port,
|
||||||
std::size_t io_service_pool_size,
|
std::size_t io_service_pool_size,
|
||||||
|
std::size_t thread_pool_size,
|
||||||
request_cb cb,
|
request_cb cb,
|
||||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx)
|
std::unique_ptr<boost::asio::ssl::context> ssl_ctx)
|
||||||
: io_service_pool_(io_service_pool_size),
|
: io_service_pool_(io_service_pool_size, thread_pool_size),
|
||||||
signals_(io_service_pool_.get_io_service()),
|
signals_(io_service_pool_.get_io_service()),
|
||||||
tick_timer_(io_service_pool_.get_io_service(),
|
tick_timer_(io_service_pool_.get_io_service(),
|
||||||
boost::posix_time::seconds(1)),
|
boost::posix_time::seconds(1)),
|
||||||
|
@ -116,7 +117,8 @@ void server::start_accept()
|
||||||
if(ssl_ctx_) {
|
if(ssl_ctx_) {
|
||||||
auto new_connection =
|
auto new_connection =
|
||||||
std::make_shared<connection<ssl_socket>>
|
std::make_shared<connection<ssl_socket>>
|
||||||
(request_cb_, io_service_pool_.get_io_service(), *ssl_ctx_);
|
(request_cb_, io_service_pool_.get_task_io_service(),
|
||||||
|
io_service_pool_.get_io_service(), *ssl_ctx_);
|
||||||
|
|
||||||
acceptor_.async_accept
|
acceptor_.async_accept
|
||||||
(new_connection->socket().lowest_layer(),
|
(new_connection->socket().lowest_layer(),
|
||||||
|
@ -140,7 +142,8 @@ void server::start_accept()
|
||||||
} else {
|
} else {
|
||||||
auto new_connection =
|
auto new_connection =
|
||||||
std::make_shared<connection<boost::asio::ip::tcp::socket>>
|
std::make_shared<connection<boost::asio::ip::tcp::socket>>
|
||||||
(request_cb_, io_service_pool_.get_io_service());
|
(request_cb_, io_service_pool_.get_task_io_service(),
|
||||||
|
io_service_pool_.get_io_service());
|
||||||
|
|
||||||
acceptor_.async_accept
|
acceptor_.async_accept
|
||||||
(new_connection->socket(),
|
(new_connection->socket(),
|
||||||
|
|
|
@ -66,6 +66,7 @@ public:
|
||||||
/// serve up files from the given directory.
|
/// serve up files from the given directory.
|
||||||
explicit server(const std::string& address, uint16_t port,
|
explicit server(const std::string& address, uint16_t port,
|
||||||
std::size_t io_service_pool_size,
|
std::size_t io_service_pool_size,
|
||||||
|
std::size_t thread_pool_size,
|
||||||
request_cb cb,
|
request_cb cb,
|
||||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx);
|
std::unique_ptr<boost::asio::ssl::context> ssl_ctx);
|
||||||
|
|
||||||
|
|
|
@ -53,11 +53,36 @@ typedef std::function<void(void)> void_cb;
|
||||||
// return value is pair of written bytes and bool value indicating
|
// return value is pair of written bytes and bool value indicating
|
||||||
// that this is the end of the body. If the end of the body was
|
// that this is the end of the body. If the end of the body was
|
||||||
// reached, return true. If there is error and application wants to
|
// reached, return true. If there is error and application wants to
|
||||||
// terminate stream, return std::make_pair(-1, false). Currently,
|
// terminate stream, return std::make_pair(-1, false). Returning
|
||||||
// returning std::make_pair(0, false) is reserved for future use.
|
// std::make_pair(0, false) tells the library that don't call this
|
||||||
|
// callback until application calls response::resume(). This is
|
||||||
|
// useful when there is no data to send at the moment but there will
|
||||||
|
// be more to come in near future.
|
||||||
typedef std::function<std::pair<ssize_t, bool>
|
typedef std::function<std::pair<ssize_t, bool>
|
||||||
(uint8_t *buf, std::size_t len)> read_cb;
|
(uint8_t *buf, std::size_t len)> read_cb;
|
||||||
|
|
||||||
|
class channel_impl;
|
||||||
|
|
||||||
|
class channel {
|
||||||
|
public:
|
||||||
|
// Application must not call this directly.
|
||||||
|
channel();
|
||||||
|
|
||||||
|
// Schedules the execution of callback |cb| in the same thread where
|
||||||
|
// request callback is called. Therefore, it is same to use request
|
||||||
|
// or response object in |cb|. The callbacks are executed in the
|
||||||
|
// same order they are posted though same channel object if they are
|
||||||
|
// posted from the same thread.
|
||||||
|
void post(void_cb cb);
|
||||||
|
|
||||||
|
// Application must not call this directly.
|
||||||
|
channel_impl& impl();
|
||||||
|
private:
|
||||||
|
std::unique_ptr<channel_impl> impl_;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::function<void(channel&)> thread_cb;
|
||||||
|
|
||||||
class request {
|
class request {
|
||||||
public:
|
public:
|
||||||
// Application must not call this directly.
|
// Application must not call this directly.
|
||||||
|
@ -104,6 +129,17 @@ public:
|
||||||
// Returns true if stream has been closed.
|
// Returns true if stream has been closed.
|
||||||
bool closed() const;
|
bool closed() const;
|
||||||
|
|
||||||
|
// Runs function |start| in one of background threads. Returns true
|
||||||
|
// if scheduling task was done successfully.
|
||||||
|
//
|
||||||
|
// Since |start| is called in different thread, calling any method
|
||||||
|
// of request or response object in the callback may cause undefined
|
||||||
|
// behavior. To safely use them, use channel::post(). A callback
|
||||||
|
// passed to channel::post() is executed in the same thread where
|
||||||
|
// request callback is called, so it is safe to use request or
|
||||||
|
// response object. Example::
|
||||||
|
bool run_task(thread_cb start);
|
||||||
|
|
||||||
// Application must not call this directly.
|
// Application must not call this directly.
|
||||||
request_impl& impl();
|
request_impl& impl();
|
||||||
private:
|
private:
|
||||||
|
@ -127,7 +163,7 @@ public:
|
||||||
// further call of end() is allowed.
|
// further call of end() is allowed.
|
||||||
void end(read_cb cb);
|
void end(read_cb cb);
|
||||||
|
|
||||||
// Resumes deferred response. Not implemented yet.
|
// Resumes deferred response.
|
||||||
void resume();
|
void resume();
|
||||||
|
|
||||||
// Returns status code.
|
// Returns status code.
|
||||||
|
@ -142,6 +178,8 @@ private:
|
||||||
std::unique_ptr<response_impl> impl_;
|
std::unique_ptr<response_impl> impl_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is so called request callback. Called every time request is
|
||||||
|
// received.
|
||||||
typedef std::function<void(std::shared_ptr<request>,
|
typedef std::function<void(std::shared_ptr<request>,
|
||||||
std::shared_ptr<response>)> request_cb;
|
std::shared_ptr<response>)> request_cb;
|
||||||
|
|
||||||
|
@ -157,12 +195,19 @@ public:
|
||||||
void listen(const std::string& address, uint16_t port,
|
void listen(const std::string& address, uint16_t port,
|
||||||
request_cb cb);
|
request_cb cb);
|
||||||
|
|
||||||
// Sets number of native threads.
|
// Sets number of native threads to handle incoming HTTP request.
|
||||||
|
// It defaults to 1.
|
||||||
void num_threads(size_t num_threads);
|
void num_threads(size_t num_threads);
|
||||||
|
|
||||||
// Sets TLS private key file and certificate file. Both files must
|
// Sets TLS private key file and certificate file. Both files must
|
||||||
// be in PEM format.
|
// be in PEM format.
|
||||||
void tls(std::string private_key_file, std::string certificate_file);
|
void tls(std::string private_key_file, std::string certificate_file);
|
||||||
|
|
||||||
|
// Sets number of background threads to run concurrent tasks (see
|
||||||
|
// request::run_task()). It defaults to 1. This is not the number
|
||||||
|
// of thread to handle incoming HTTP request. For this purpose, see
|
||||||
|
// num_threads().
|
||||||
|
void num_concurrent_tasks(size_t num_concurrent_tasks);
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<http2_impl> impl_;
|
std::unique_ptr<http2_impl> impl_;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue