Merge branch 'asio-client'
This commit is contained in:
commit
2ed47cdd19
13
README.rst
13
README.rst
|
@ -1019,7 +1019,7 @@ libnghttp2_asio: High level HTTP/2 C++ library
|
|||
libnghttp2_asio is C++ library built on top of libnghttp2 and provides
|
||||
high level abstraction API to build HTTP/2 applications. It depends
|
||||
on Boost::ASIO library and OpenSSL. Currently libnghttp2_asio
|
||||
provides server side API.
|
||||
provides client and server API.
|
||||
|
||||
libnghttp2_asio is not built by default. Use ``--enable-asio-lib``
|
||||
configure flag to build libnghttp2_asio. The required Boost libraries
|
||||
|
@ -1035,7 +1035,7 @@ HTTP/2 server looks like this:
|
|||
|
||||
.. code-block:: cpp
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
@ -1043,11 +1043,12 @@ HTTP/2 server looks like this:
|
|||
int main(int argc, char *argv[]) {
|
||||
http2 server;
|
||||
|
||||
server.listen("*", 3000, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
res->write_head(200);
|
||||
res->end("hello, world");
|
||||
server.handle("/", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end("hello, world\n");
|
||||
});
|
||||
|
||||
server.listen_and_serve("*", 3000);
|
||||
}
|
||||
|
||||
For more details, see the documentation of libnghttp2_asio.
|
||||
|
|
|
@ -58,10 +58,18 @@ endif # ENABLE_TINY_NGHTTPD
|
|||
|
||||
if ENABLE_ASIO_LIB
|
||||
|
||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3
|
||||
noinst_PROGRAMS += asio-sv asio-sv2 asio-cl
|
||||
|
||||
ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS}
|
||||
ASIOLDADD = $(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@
|
||||
ASIOLDADD = $(top_builddir)/lib/libnghttp2.la \
|
||||
$(top_builddir)/src/libnghttp2_asio.la @JEMALLOC_LIBS@ \
|
||||
$(top_builddir)/third-party/libhttp-parser.la \
|
||||
${BOOST_LDFLAGS} \
|
||||
${BOOST_ASIO_LIB} \
|
||||
${BOOST_THREAD_LIB} \
|
||||
${BOOST_SYSTEM_LIB} \
|
||||
@OPENSSL_LIBS@ \
|
||||
@APPLDFLAGS@
|
||||
|
||||
asio_sv_SOURCES = asio-sv.cc
|
||||
asio_sv_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
|
@ -71,9 +79,9 @@ asio_sv2_SOURCES = asio-sv2.cc
|
|||
asio_sv2_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv2_LDADD = ${ASIOLDADD}
|
||||
|
||||
asio_sv3_SOURCES = asio-sv3.cc
|
||||
asio_sv3_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_sv3_LDADD = ${ASIOLDADD}
|
||||
asio_cl_SOURCES = asio-cl.cc
|
||||
asio_cl_CPPFLAGS = ${ASIOCPPFLAGS}
|
||||
asio_cl_LDADD = ${ASIOLDADD}
|
||||
|
||||
endif # ENABLE_ASIO_LIB
|
||||
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::client;
|
||||
|
||||
void print_header(const header_map &h) {
|
||||
for (auto &kv : h) {
|
||||
std::cerr << kv.first << ": " << kv.second.value << "\n";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
void print_header(const response &res) {
|
||||
std::cerr << "HTTP/2 " << res.status_code() << "\n";
|
||||
print_header(res.header());
|
||||
}
|
||||
|
||||
void print_header(const request &req) {
|
||||
auto &uri = req.uri();
|
||||
std::cerr << req.method() << " " << uri.scheme << "://" << uri.host
|
||||
<< uri.path;
|
||||
if (!uri.raw_query.empty()) {
|
||||
std::cerr << "?" << uri.raw_query;
|
||||
}
|
||||
std::cerr << " HTTP/2\n";
|
||||
print_header(req.header());
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
try {
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
boost::asio::ssl::context tls_ctx(boost::asio::ssl::context::sslv23);
|
||||
tls_ctx.set_default_verify_paths();
|
||||
// disabled to make development easier...
|
||||
// tls_ctx.set_verify_mode(boost::asio::ssl::verify_peer);
|
||||
configure_tls_context(tls_ctx);
|
||||
|
||||
session sess(io_service, tls_ctx, "localhost", "3000");
|
||||
sess.on_connect([&sess](tcp::resolver::iterator endpoint_it) {
|
||||
std::cerr << "connected to " << (*endpoint_it).endpoint() << std::endl;
|
||||
boost::system::error_code ec;
|
||||
auto req = sess.submit(ec, "GET", "https://localhost:3000/",
|
||||
{{"cookie", {"foo=bar", true}}});
|
||||
if (ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
req->on_response([&sess, req](const response &res) {
|
||||
std::cerr << "response header was received" << std::endl;
|
||||
print_header(res);
|
||||
|
||||
res.on_data([&sess](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
|
||||
req->on_close([&sess](uint32_t error_code) {
|
||||
std::cerr << "request done with error_code=" << error_code << std::endl;
|
||||
});
|
||||
|
||||
req->on_push([](const request &push_req) {
|
||||
std::cerr << "push request was received" << std::endl;
|
||||
|
||||
print_header(push_req);
|
||||
|
||||
push_req.on_response([](const response &res) {
|
||||
std::cerr << "push response header was received" << std::endl;
|
||||
|
||||
res.on_data([](const uint8_t *data, std::size_t len) {
|
||||
std::cerr.write(reinterpret_cast<const char *>(data), len);
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
sess.on_error([](const boost::system::error_code &ec) {
|
||||
std::cerr << "error: " << ec.message() << std::endl;
|
||||
});
|
||||
|
||||
io_service.run();
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -37,7 +37,7 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
@ -62,11 +62,47 @@ int main(int argc, char *argv[]) {
|
|||
server.tls(argv[3], argv[4]);
|
||||
}
|
||||
|
||||
server.listen("*", port, [](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
res->write_head(200, {header{"foo", "bar"}});
|
||||
res->end("hello, world");
|
||||
server.handle("/", [](const request &req, const response &res) {
|
||||
res.write_head(200, {{"foo", {"bar"}}});
|
||||
res.end("hello, world\n");
|
||||
});
|
||||
server.handle("/secret/", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
res.end("under construction!\n");
|
||||
});
|
||||
server.handle("/push", [](const request &req, const response &res) {
|
||||
boost::system::error_code ec;
|
||||
auto push = res.push(ec, "GET", "/push/1");
|
||||
if (!ec) {
|
||||
push->write_head(200);
|
||||
push->end("server push FTW!\n");
|
||||
}
|
||||
|
||||
res.write_head(200);
|
||||
res.end("you'll receive server push!\n");
|
||||
});
|
||||
server.handle("/delay", [](const request &req, const response &res) {
|
||||
res.write_head(200);
|
||||
|
||||
auto timer = std::make_shared<boost::asio::deadline_timer>(
|
||||
res.io_service(), boost::posix_time::seconds(3));
|
||||
auto closed = std::make_shared<bool>();
|
||||
|
||||
res.on_close([timer, closed](uint32_t error_code) {
|
||||
timer->cancel();
|
||||
*closed = true;
|
||||
});
|
||||
|
||||
timer->async_wait([&res, closed](const boost::system::error_code &ec) {
|
||||
if (ec || *closed) {
|
||||
return;
|
||||
}
|
||||
|
||||
res.end("finally!\n");
|
||||
});
|
||||
});
|
||||
server.listen_and_serve("*", port);
|
||||
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
using namespace nghttp2::asio_http2;
|
||||
using namespace nghttp2::asio_http2::server;
|
||||
|
@ -66,12 +66,11 @@ int main(int argc, char *argv[]) {
|
|||
server.tls(argv[4], argv[5]);
|
||||
}
|
||||
|
||||
server.listen("*", port, [&docroot](const std::shared_ptr<request> &req,
|
||||
const std::shared_ptr<response> &res) {
|
||||
auto path = percent_decode(req->path());
|
||||
server.handle("/", [&docroot](const request &req, const response &res) {
|
||||
auto path = percent_decode(req.uri().path);
|
||||
if (!check_path(path)) {
|
||||
res->write_head(404);
|
||||
res->end();
|
||||
res.write_head(404);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -82,22 +81,26 @@ int main(int argc, char *argv[]) {
|
|||
path = docroot + path;
|
||||
auto fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
res->write_head(404);
|
||||
res->end();
|
||||
res.write_head(404);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
auto headers = std::vector<header>();
|
||||
auto header = header_map();
|
||||
|
||||
struct stat stbuf;
|
||||
if (stat(path.c_str(), &stbuf) == 0) {
|
||||
headers.push_back(
|
||||
header{"content-length", std::to_string(stbuf.st_size)});
|
||||
headers.push_back(header{"last-modified", http_date(stbuf.st_mtime)});
|
||||
header.emplace("content-length",
|
||||
header_value{std::to_string(stbuf.st_size)});
|
||||
header.emplace("last-modified",
|
||||
header_value{http_date(stbuf.st_mtime)});
|
||||
}
|
||||
res->write_head(200, std::move(headers));
|
||||
res->end(file_reader_from_fd(fd));
|
||||
res.write_head(200, std::move(header));
|
||||
res.end(file_generator_from_fd(fd));
|
||||
});
|
||||
|
||||
server.listen_and_serve("*", port);
|
||||
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* 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, [](const std::shared_ptr<request> &req,
|
||||
const 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;
|
||||
}
|
|
@ -173,13 +173,32 @@ DISTCLEANFILES = $(pkgconfig_DATA)
|
|||
lib_LTLIBRARIES = libnghttp2_asio.la
|
||||
|
||||
libnghttp2_asio_la_SOURCES = \
|
||||
asio_connection.h \
|
||||
asio_server.cc asio_server.h \
|
||||
asio_io_service_pool.cc asio_io_service_pool.h \
|
||||
asio_http2_handler.cc asio_http2_handler.h \
|
||||
asio_http2_impl.cc asio_http2_impl.h \
|
||||
util.cc util.h http2.cc http2.h \
|
||||
ssl.cc ssl.h
|
||||
ssl.cc ssl.h \
|
||||
asio_common.cc asio_common.h \
|
||||
asio_io_service_pool.cc asio_io_service_pool.h \
|
||||
asio_server_http2.cc \
|
||||
asio_server_http2_impl.cc asio_server_http2_impl.h \
|
||||
asio_server.cc asio_server.h \
|
||||
asio_server_http2_handler.cc asio_server_http2_handler.h \
|
||||
asio_server_connection.h \
|
||||
asio_server_request.cc \
|
||||
asio_server_request_impl.cc asio_server_request_impl.h \
|
||||
asio_server_response.cc \
|
||||
asio_server_response_impl.cc asio_server_response_impl.h \
|
||||
asio_server_stream.cc asio_server_stream.h \
|
||||
asio_server_serve_mux.cc asio_server_serve_mux.h \
|
||||
asio_server_request_handler.cc asio_server_request_handler.h \
|
||||
asio_client_session.cc \
|
||||
asio_client_session_impl.cc asio_client_session_impl.h \
|
||||
asio_client_session_tcp_impl.cc asio_client_session_tcp_impl.h \
|
||||
asio_client_session_tls_impl.cc asio_client_session_tls_impl.h \
|
||||
asio_client_response.cc \
|
||||
asio_client_response_impl.cc asio_client_response_impl.h \
|
||||
asio_client_request.cc \
|
||||
asio_client_request_impl.cc asio_client_request_impl.h \
|
||||
asio_client_stream.cc asio_client_stream.h \
|
||||
asio_client_tls_context.cc asio_client_tls_context.h
|
||||
|
||||
libnghttp2_asio_la_CPPFLAGS = ${AM_CPPFLAGS} ${BOOST_CPPFLAGS}
|
||||
libnghttp2_asio_la_LDFLAGS = -no-undefined -version-info 0:0:0
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
#include "asio_client_request_impl.h"
|
||||
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
request::request() : impl_(make_unique<request_impl>()) {}
|
||||
|
||||
request::~request() {}
|
||||
|
||||
void request::cancel(uint32_t error_code) const { impl_->cancel(error_code); }
|
||||
|
||||
void request::on_response(response_cb cb) const {
|
||||
impl_->on_response(std::move(cb));
|
||||
}
|
||||
|
||||
void request::on_push(request_cb cb) const { impl_->on_push(std::move(cb)); }
|
||||
|
||||
void request::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); }
|
||||
|
||||
const uri_ref &request::uri() const { return impl_->uri(); }
|
||||
|
||||
const std::string &request::method() const { return impl_->method(); }
|
||||
|
||||
const header_map &request::header() const { return impl_->header(); }
|
||||
|
||||
void request::resume() const { impl_->resume(); }
|
||||
|
||||
request_impl &request::impl() const { return *impl_; }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_client_request_impl.h"
|
||||
|
||||
#include "asio_client_stream.h"
|
||||
#include "asio_client_session_impl.h"
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
request_impl::request_impl() : strm_(nullptr) {}
|
||||
|
||||
void request_impl::cancel(uint32_t error_code) {
|
||||
auto sess = strm_->session();
|
||||
sess->cancel(*strm_, error_code);
|
||||
}
|
||||
|
||||
void request_impl::on_response(response_cb cb) { response_cb_ = std::move(cb); }
|
||||
|
||||
void request_impl::call_on_response(response &res) {
|
||||
if (response_cb_) {
|
||||
response_cb_(res);
|
||||
}
|
||||
}
|
||||
|
||||
void request_impl::on_push(request_cb cb) { push_request_cb_ = std::move(cb); }
|
||||
|
||||
void request_impl::call_on_push(request &push_req) {
|
||||
if (push_request_cb_) {
|
||||
push_request_cb_(push_req);
|
||||
}
|
||||
};
|
||||
|
||||
void request_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); }
|
||||
|
||||
void request_impl::call_on_close(uint32_t error_code) {
|
||||
if (close_cb_) {
|
||||
close_cb_(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
void request_impl::on_read(generator_cb cb) { generator_cb_ = std::move(cb); }
|
||||
|
||||
generator_cb::result_type request_impl::call_on_read(uint8_t *buf,
|
||||
std::size_t len,
|
||||
uint32_t *data_flags) {
|
||||
if (generator_cb_) {
|
||||
return generator_cb_(buf, len, data_flags);
|
||||
}
|
||||
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void request_impl::resume() {
|
||||
auto sess = strm_->session();
|
||||
sess->resume(*strm_);
|
||||
}
|
||||
|
||||
void request_impl::header(header_map h) { header_ = std::move(h); }
|
||||
|
||||
header_map &request_impl::header() { return header_; }
|
||||
|
||||
const header_map &request_impl::header() const { return header_; }
|
||||
|
||||
void request_impl::stream(class stream *strm) { strm_ = strm; }
|
||||
|
||||
void request_impl::uri(uri_ref uri) { uri_ = std::move(uri); }
|
||||
|
||||
const uri_ref &request_impl::uri() const { return uri_; }
|
||||
|
||||
uri_ref &request_impl::uri() { return uri_; }
|
||||
|
||||
void request_impl::method(std::string s) { method_ = std::move(s); }
|
||||
|
||||
const std::string &request_impl::method() const { return method_; }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_REQUEST_IMPL_H
|
||||
#define ASIO_CLIENT_REQUEST_IMPL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
class response;
|
||||
class stream;
|
||||
|
||||
class request_impl {
|
||||
public:
|
||||
request_impl();
|
||||
|
||||
request_impl(const request_impl &) = delete;
|
||||
request_impl &operator=(const request_impl &) = delete;
|
||||
|
||||
void cancel(uint32_t error_code);
|
||||
|
||||
void on_response(response_cb cb);
|
||||
void call_on_response(response &res);
|
||||
|
||||
void on_push(request_cb cb);
|
||||
void call_on_push(request &push_req);
|
||||
|
||||
void on_close(close_cb cb);
|
||||
void call_on_close(uint32_t error_code);
|
||||
|
||||
void on_read(generator_cb cb);
|
||||
generator_cb::result_type call_on_read(uint8_t *buf, std::size_t len,
|
||||
uint32_t *data_flags);
|
||||
|
||||
void resume();
|
||||
|
||||
void header(header_map h);
|
||||
header_map &header();
|
||||
const header_map &header() const;
|
||||
|
||||
void stream(class stream *strm);
|
||||
|
||||
void uri(uri_ref uri);
|
||||
const uri_ref &uri() const;
|
||||
uri_ref &uri();
|
||||
|
||||
void method(std::string s);
|
||||
const std::string &method() const;
|
||||
|
||||
private:
|
||||
header_map header_;
|
||||
response_cb response_cb_;
|
||||
request_cb push_request_cb_;
|
||||
close_cb close_cb_;
|
||||
generator_cb generator_cb_;
|
||||
class stream *strm_;
|
||||
uri_ref uri_;
|
||||
std::string method_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_REQUEST_IMPL_H
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
#include "asio_client_response_impl.h"
|
||||
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
response::response() : impl_(make_unique<response_impl>()) {}
|
||||
|
||||
response::~response() {}
|
||||
|
||||
void response::on_data(data_cb cb) const { impl_->on_data(std::move(cb)); }
|
||||
|
||||
int response::status_code() const { return impl_->status_code(); }
|
||||
|
||||
int64_t response::content_length() const { return impl_->content_length(); }
|
||||
|
||||
const header_map &response::header() const { return impl_->header(); }
|
||||
|
||||
response_impl &response::impl() const { return *impl_; }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_client_response_impl.h"
|
||||
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
response_impl::response_impl() : content_length_(-1), status_code_(0) {}
|
||||
|
||||
void response_impl::on_data(data_cb cb) { data_cb_ = std::move(cb); }
|
||||
|
||||
void response_impl::call_on_data(const uint8_t *data, std::size_t len) {
|
||||
if (data_cb_) {
|
||||
data_cb_(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void response_impl::status_code(int sc) { status_code_ = sc; }
|
||||
|
||||
int response_impl::status_code() const { return status_code_; }
|
||||
|
||||
void response_impl::content_length(int64_t n) { content_length_ = n; }
|
||||
|
||||
int64_t response_impl::content_length() const { return content_length_; }
|
||||
|
||||
header_map &response_impl::header() { return header_; }
|
||||
|
||||
const header_map &response_impl::header() const { return header_; }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_RESPONSE_IMPL_H
|
||||
#define ASIO_CLIENT_RESPONSE_IMPL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
class response_impl {
|
||||
public:
|
||||
response_impl();
|
||||
|
||||
response_impl(const response_impl &) = delete;
|
||||
response_impl &operator=(const response_impl &) = delete;
|
||||
|
||||
void on_data(data_cb cb);
|
||||
|
||||
void call_on_data(const uint8_t *data, std::size_t len);
|
||||
|
||||
void status_code(int sc);
|
||||
int status_code() const;
|
||||
|
||||
void content_length(int64_t n);
|
||||
int64_t content_length() const;
|
||||
|
||||
header_map &header();
|
||||
const header_map &header() const;
|
||||
|
||||
private:
|
||||
data_cb data_cb_;
|
||||
|
||||
header_map header_;
|
||||
|
||||
int64_t content_length_;
|
||||
int status_code_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_RESPONSE_IMPL_H
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
#include "asio_client_session_tcp_impl.h"
|
||||
#include "asio_client_session_tls_impl.h"
|
||||
#include "asio_common.h"
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
session::session(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service)
|
||||
: impl_(make_unique<session_tcp_impl>(io_service, host, service)) {}
|
||||
|
||||
session::session(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx, const std::string &host,
|
||||
const std::string &service)
|
||||
: impl_(make_unique<session_tls_impl>(io_service, tls_ctx, host, service)) {
|
||||
}
|
||||
|
||||
session::~session() {}
|
||||
|
||||
void session::on_connect(connect_cb cb) const {
|
||||
impl_->on_connect(std::move(cb));
|
||||
}
|
||||
|
||||
void session::on_error(error_cb cb) const { impl_->on_error(std::move(cb)); }
|
||||
|
||||
void session::shutdown() const { impl_->shutdown(); }
|
||||
|
||||
boost::asio::io_service &session::io_service() const {
|
||||
return impl_->io_service();
|
||||
}
|
||||
|
||||
const request *session::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, header_map h) const {
|
||||
return impl_->submit(ec, method, uri, generator_cb(), std::move(h));
|
||||
}
|
||||
|
||||
const request *session::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, std::string data,
|
||||
header_map h) const {
|
||||
return impl_->submit(ec, method, uri, string_generator(std::move(data)),
|
||||
std::move(h));
|
||||
}
|
||||
|
||||
const request *session::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, generator_cb cb,
|
||||
header_map h) const {
|
||||
return impl_->submit(ec, method, uri, std::move(cb), std::move(h));
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // nghttp2
|
|
@ -0,0 +1,586 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_client_session_impl.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "asio_client_stream.h"
|
||||
#include "asio_client_request_impl.h"
|
||||
#include "asio_client_response_impl.h"
|
||||
#include "asio_common.h"
|
||||
#include "template.h"
|
||||
#include "util.h"
|
||||
#include "http2.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
session_impl::session_impl(boost::asio::io_service &io_service)
|
||||
: wblen_(0), io_service_(io_service), resolver_(io_service),
|
||||
session_(nullptr), data_pending_(nullptr), data_pendinglen_(0),
|
||||
writing_(false), inside_callback_(false) {}
|
||||
|
||||
session_impl::~session_impl() {
|
||||
// finish up all active stream
|
||||
for (auto &p : streams_) {
|
||||
auto &strm = p.second;
|
||||
auto &req = strm->request().impl();
|
||||
req.call_on_close(NGHTTP2_INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
nghttp2_session_del(session_);
|
||||
}
|
||||
|
||||
void session_impl::start_resolve(const std::string &host,
|
||||
const std::string &service) {
|
||||
resolver_.async_resolve({host, service},
|
||||
[this](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
start_connect(endpoint_it);
|
||||
});
|
||||
}
|
||||
|
||||
void session_impl::connected(tcp::resolver::iterator endpoint_it) {
|
||||
if (!setup_session()) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket().set_option(boost::asio::ip::tcp::no_delay(true));
|
||||
|
||||
std::copy_n(NGHTTP2_CLIENT_CONNECTION_PREFACE,
|
||||
NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN, std::begin(wb_));
|
||||
wblen_ = NGHTTP2_CLIENT_CONNECTION_PREFACE_LEN;
|
||||
|
||||
do_write();
|
||||
do_read();
|
||||
|
||||
auto &connect_cb = on_connect();
|
||||
if (connect_cb) {
|
||||
connect_cb(endpoint_it);
|
||||
}
|
||||
}
|
||||
|
||||
void session_impl::not_connected(const boost::system::error_code &ec) {
|
||||
auto &error_cb = on_error();
|
||||
if (error_cb) {
|
||||
error_cb(ec);
|
||||
}
|
||||
}
|
||||
|
||||
void session_impl::on_connect(connect_cb cb) { connect_cb_ = std::move(cb); }
|
||||
|
||||
void session_impl::on_error(error_cb cb) { error_cb_ = std::move(cb); }
|
||||
|
||||
const connect_cb &session_impl::on_connect() const { return connect_cb_; }
|
||||
|
||||
const error_cb &session_impl::on_error() const { return error_cb_; }
|
||||
|
||||
namespace {
|
||||
int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto sess = static_cast<session_impl *>(user_data);
|
||||
sess->create_push_stream(frame->push_promise.promised_stream_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen, uint8_t flags,
|
||||
void *user_data) {
|
||||
auto sess = static_cast<session_impl *>(user_data);
|
||||
stream *strm;
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_HEADERS: {
|
||||
strm = sess->find_stream(frame->hd.stream_id);
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ignore trailers
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
|
||||
!strm->expect_final_response()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto token = http2::lookup_token(name, namelen);
|
||||
|
||||
auto &res = strm->response().impl();
|
||||
if (token == http2::HD__STATUS) {
|
||||
res.status_code(util::parse_uint(value, valuelen));
|
||||
} else {
|
||||
|
||||
if (token == http2::HD_CONTENT_LENGTH) {
|
||||
res.content_length(util::parse_uint(value, valuelen));
|
||||
}
|
||||
|
||||
res.header().emplace(
|
||||
std::string(name, name + namelen),
|
||||
header_value{std::string(value, value + valuelen),
|
||||
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_PUSH_PROMISE: {
|
||||
strm = sess->find_stream(frame->push_promise.promised_stream_id);
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &req = strm->request().impl();
|
||||
auto &uri = req.uri();
|
||||
|
||||
switch (http2::lookup_token(name, namelen)) {
|
||||
case http2::HD__METHOD:
|
||||
req.method(std::string(value, value + valuelen));
|
||||
break;
|
||||
case http2::HD__SCHEME:
|
||||
uri.scheme.assign(value, value + valuelen);
|
||||
break;
|
||||
case http2::HD__PATH:
|
||||
split_path(uri, value, value + valuelen);
|
||||
break;
|
||||
case http2::HD__AUTHORITY:
|
||||
uri.host.assign(value, value + valuelen);
|
||||
break;
|
||||
case http2::HD_HOST:
|
||||
if (uri.host.empty()) {
|
||||
uri.host.assign(value, value + valuelen);
|
||||
}
|
||||
// fall through
|
||||
default:
|
||||
req.header().emplace(
|
||||
std::string(name, name + namelen),
|
||||
header_value{std::string(value, value + valuelen),
|
||||
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
auto sess = static_cast<session_impl *>(user_data);
|
||||
auto strm = sess->find_stream(frame->hd.stream_id);
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA: {
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
strm->response().impl().call_on_data(nullptr, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_HEADERS: {
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ignore trailers
|
||||
if (frame->headers.cat == NGHTTP2_HCAT_HEADERS &&
|
||||
!strm->expect_final_response()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (strm->expect_final_response()) {
|
||||
// wait for final response
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &req = strm->request().impl();
|
||||
req.call_on_response(strm->response());
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
strm->response().impl().call_on_data(nullptr, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NGHTTP2_PUSH_PROMISE: {
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto push_strm = sess->find_stream(frame->push_promise.promised_stream_id);
|
||||
if (!push_strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
strm->request().impl().call_on_push(push_strm->request());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, const uint8_t *data,
|
||||
size_t len, void *user_data) {
|
||||
auto sess = static_cast<session_impl *>(user_data);
|
||||
auto strm = sess->find_stream(stream_id);
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &res = strm->response().impl();
|
||||
res.call_on_data(data, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code, void *user_data) {
|
||||
auto sess = static_cast<session_impl *>(user_data);
|
||||
auto strm = sess->pop_stream(stream_id);
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
strm->request().impl().call_on_close(error_code);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool session_impl::setup_session() {
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
nghttp2_session_callbacks_new(&callbacks);
|
||||
defer(nghttp2_session_callbacks_del, callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
|
||||
auto rv = nghttp2_session_client_new(&session_, callbacks, this);
|
||||
if (rv != 0) {
|
||||
auto &error_cb = on_error();
|
||||
if (error_cb) {
|
||||
error_cb(make_error_code(static_cast<nghttp2_error>(rv)));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nghttp2_settings_entry iv = {NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
|
||||
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &iv, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void session_impl::cancel(stream &strm, uint32_t error_code) {
|
||||
nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(),
|
||||
error_code);
|
||||
signal_write();
|
||||
}
|
||||
|
||||
void session_impl::resume(stream &strm) {
|
||||
nghttp2_session_resume_data(session_, strm.stream_id());
|
||||
signal_write();
|
||||
}
|
||||
|
||||
stream *session_impl::find_stream(int32_t stream_id) {
|
||||
auto it = streams_.find(stream_id);
|
||||
if (it == std::end(streams_)) {
|
||||
return nullptr;
|
||||
}
|
||||
return (*it).second.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<stream> session_impl::pop_stream(int32_t stream_id) {
|
||||
auto it = streams_.find(stream_id);
|
||||
if (it == std::end(streams_)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto strm = std::move((*it).second);
|
||||
streams_.erase(it);
|
||||
return strm;
|
||||
}
|
||||
|
||||
stream *session_impl::create_push_stream(int32_t stream_id) {
|
||||
auto strm = create_stream();
|
||||
strm->stream_id(stream_id);
|
||||
auto p = streams_.emplace(stream_id, std::move(strm));
|
||||
assert(p.second);
|
||||
return (*p.first).second.get();
|
||||
}
|
||||
|
||||
std::unique_ptr<stream> session_impl::create_stream() {
|
||||
return make_unique<stream>(this);
|
||||
}
|
||||
|
||||
const request *session_impl::submit(boost::system::error_code &ec,
|
||||
const std::string &method,
|
||||
const std::string &uri, generator_cb cb,
|
||||
header_map h) {
|
||||
ec.clear();
|
||||
|
||||
http_parser_url u{};
|
||||
// TODO Handle CONNECT method
|
||||
if (http_parser_parse_url(uri.c_str(), uri.size(), 0, &u) != 0) {
|
||||
ec = make_error_code(boost::system::errc::invalid_argument);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((u.field_set & (1 << UF_SCHEMA)) == 0 ||
|
||||
(u.field_set & (1 << UF_HOST)) == 0) {
|
||||
ec = make_error_code(boost::system::errc::invalid_argument);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto strm = create_stream();
|
||||
auto &req = strm->request().impl();
|
||||
auto &uref = req.uri();
|
||||
|
||||
http2::copy_url_component(uref.scheme, &u, UF_SCHEMA, uri.c_str());
|
||||
http2::copy_url_component(uref.host, &u, UF_HOST, uri.c_str());
|
||||
http2::copy_url_component(uref.raw_path, &u, UF_PATH, uri.c_str());
|
||||
http2::copy_url_component(uref.raw_query, &u, UF_QUERY, uri.c_str());
|
||||
|
||||
if (util::ipv6_numeric_addr(uref.host.c_str())) {
|
||||
uref.host = "[" + uref.host;
|
||||
uref.host += "]";
|
||||
}
|
||||
if (u.field_set & (1 << UF_PORT)) {
|
||||
uref.host += ":";
|
||||
uref.host += util::utos(u.port);
|
||||
}
|
||||
uref.path = percent_decode(uref.raw_path);
|
||||
|
||||
auto path = uref.raw_path;
|
||||
if (u.field_set & (1 << UF_QUERY)) {
|
||||
path += "?";
|
||||
path += uref.raw_query;
|
||||
}
|
||||
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(3 + h.size());
|
||||
nva.push_back(http2::make_nv_ls(":method", method));
|
||||
nva.push_back(http2::make_nv_ls(":scheme", uref.scheme));
|
||||
nva.push_back(http2::make_nv_ls(":path", path));
|
||||
nva.push_back(http2::make_nv_ls(":authority", uref.host));
|
||||
for (auto &kv : h) {
|
||||
nva.push_back(
|
||||
http2::make_nv(kv.first, kv.second.value, kv.second.sensitive));
|
||||
}
|
||||
|
||||
req.header(std::move(h));
|
||||
|
||||
nghttp2_data_provider *prdptr = nullptr;
|
||||
nghttp2_data_provider prd;
|
||||
|
||||
if (cb) {
|
||||
strm->request().impl().on_read(std::move(cb));
|
||||
prd.source.ptr = strm.get();
|
||||
prd.read_callback =
|
||||
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
|
||||
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
|
||||
void *user_data) -> ssize_t {
|
||||
auto strm = static_cast<stream *>(source->ptr);
|
||||
return strm->request().impl().call_on_read(buf, length, data_flags);
|
||||
};
|
||||
prdptr = &prd;
|
||||
}
|
||||
|
||||
auto stream_id = nghttp2_submit_request(session_, nullptr, nva.data(),
|
||||
nva.size(), prdptr, strm.get());
|
||||
if (stream_id < 0) {
|
||||
ec = make_error_code(static_cast<nghttp2_error>(stream_id));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
signal_write();
|
||||
|
||||
strm->stream_id(stream_id);
|
||||
|
||||
auto p = streams_.emplace(stream_id, std::move(strm));
|
||||
assert(p.second);
|
||||
return &(*p.first).second->request();
|
||||
}
|
||||
|
||||
void session_impl::shutdown() {
|
||||
nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
|
||||
signal_write();
|
||||
}
|
||||
|
||||
boost::asio::io_service &session_impl::io_service() { return io_service_; }
|
||||
|
||||
void session_impl::signal_write() {
|
||||
if (!inside_callback_) {
|
||||
do_write();
|
||||
}
|
||||
}
|
||||
|
||||
bool session_impl::should_stop() const {
|
||||
return !writing_ && !nghttp2_session_want_read(session_) &&
|
||||
!nghttp2_session_want_write(session_);
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct callback_guard {
|
||||
callback_guard(session_impl &sess) : sess(sess) { sess.enter_callback(); }
|
||||
~callback_guard() { sess.leave_callback(); }
|
||||
|
||||
session_impl &sess;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void session_impl::enter_callback() {
|
||||
assert(!inside_callback_);
|
||||
inside_callback_ = true;
|
||||
}
|
||||
|
||||
void session_impl::leave_callback() {
|
||||
assert(inside_callback_);
|
||||
inside_callback_ = false;
|
||||
}
|
||||
|
||||
void session_impl::do_read() {
|
||||
read_socket([this](const boost::system::error_code &ec,
|
||||
std::size_t bytes_transferred) {
|
||||
if (ec) {
|
||||
if (ec.value() == boost::asio::error::operation_aborted) {
|
||||
shutdown_socket();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
callback_guard cg(*this);
|
||||
|
||||
auto rv =
|
||||
nghttp2_session_mem_recv(session_, rb_.data(), bytes_transferred);
|
||||
|
||||
if (rv != static_cast<ssize_t>(bytes_transferred)) {
|
||||
shutdown_socket();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
do_write();
|
||||
|
||||
if (should_stop()) {
|
||||
shutdown_socket();
|
||||
return;
|
||||
}
|
||||
|
||||
do_read();
|
||||
});
|
||||
}
|
||||
|
||||
void session_impl::do_write() {
|
||||
if (writing_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data_pending_) {
|
||||
std::copy_n(data_pending_, data_pendinglen_, std::begin(wb_) + wblen_);
|
||||
|
||||
wblen_ += data_pendinglen_;
|
||||
|
||||
data_pending_ = nullptr;
|
||||
data_pendinglen_ = 0;
|
||||
}
|
||||
|
||||
{
|
||||
callback_guard cg(*this);
|
||||
|
||||
for (;;) {
|
||||
const uint8_t *data;
|
||||
auto n = nghttp2_session_mem_send(session_, &data);
|
||||
if (n < 0) {
|
||||
shutdown_socket();
|
||||
return;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (wblen_ + n > wb_.size()) {
|
||||
data_pending_ = data;
|
||||
data_pendinglen_ = n;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
std::copy_n(data, n, std::begin(wb_) + wblen_);
|
||||
|
||||
wblen_ += n;
|
||||
}
|
||||
}
|
||||
|
||||
if (wblen_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
writing_ = true;
|
||||
|
||||
write_socket([this](const boost::system::error_code &ec, std::size_t n) {
|
||||
if (ec) {
|
||||
return;
|
||||
}
|
||||
|
||||
wblen_ = 0;
|
||||
writing_ = false;
|
||||
|
||||
do_write();
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // nghttp2
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_SESSION_IMPL_H
|
||||
#define ASIO_CLIENT_SESSION_IMPL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <boost/array.hpp>
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
class stream;
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
class session_impl {
|
||||
public:
|
||||
session_impl(boost::asio::io_service &io_service);
|
||||
virtual ~session_impl();
|
||||
|
||||
void start_resolve(const std::string &host, const std::string &service);
|
||||
|
||||
void connected(tcp::resolver::iterator endpoint_it);
|
||||
void not_connected(const boost::system::error_code &ec);
|
||||
|
||||
void on_connect(connect_cb cb);
|
||||
void on_error(error_cb cb);
|
||||
|
||||
const connect_cb &on_connect() const;
|
||||
const error_cb &on_error() const;
|
||||
|
||||
void cancel(stream &strm, uint32_t error_code);
|
||||
void resume(stream &strm);
|
||||
|
||||
std::unique_ptr<stream> create_stream();
|
||||
std::unique_ptr<stream> pop_stream(int32_t stream_id);
|
||||
stream *create_push_stream(int32_t stream_id);
|
||||
stream *find_stream(int32_t stream_id);
|
||||
|
||||
const request *submit(boost::system::error_code &ec,
|
||||
const std::string &method, const std::string &uri,
|
||||
generator_cb cb, header_map h);
|
||||
|
||||
virtual void start_connect(tcp::resolver::iterator endpoint_it) = 0;
|
||||
virtual tcp::socket &socket() = 0;
|
||||
virtual void read_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h) = 0;
|
||||
virtual void write_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h) = 0;
|
||||
virtual void shutdown_socket() = 0;
|
||||
|
||||
void shutdown();
|
||||
|
||||
boost::asio::io_service &io_service();
|
||||
|
||||
void signal_write();
|
||||
|
||||
void enter_callback();
|
||||
void leave_callback();
|
||||
|
||||
void do_read();
|
||||
void do_write();
|
||||
|
||||
protected:
|
||||
boost::array<uint8_t, 8192> rb_;
|
||||
boost::array<uint8_t, 65536> wb_;
|
||||
std::size_t wblen_;
|
||||
|
||||
private:
|
||||
bool should_stop() const;
|
||||
bool setup_session();
|
||||
|
||||
boost::asio::io_service &io_service_;
|
||||
tcp::resolver resolver_;
|
||||
|
||||
std::map<int32_t, std::unique_ptr<stream>> streams_;
|
||||
|
||||
connect_cb connect_cb_;
|
||||
error_cb error_cb_;
|
||||
|
||||
nghttp2_session *session_;
|
||||
|
||||
const uint8_t *data_pending_;
|
||||
std::size_t data_pendinglen_;
|
||||
|
||||
bool writing_;
|
||||
bool inside_callback_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_SESSION_IMPL_H
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_client_session_tcp_impl.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
session_tcp_impl::session_tcp_impl(boost::asio::io_service &io_service,
|
||||
const std::string &host,
|
||||
const std::string &service)
|
||||
: session_impl(io_service), socket_(io_service) {
|
||||
start_resolve(host, service);
|
||||
}
|
||||
|
||||
session_tcp_impl::~session_tcp_impl() {}
|
||||
|
||||
void session_tcp_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||
boost::asio::async_connect(socket_, endpoint_it,
|
||||
[this](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
connected(endpoint_it);
|
||||
});
|
||||
}
|
||||
|
||||
tcp::socket &session_tcp_impl::socket() { return socket_; }
|
||||
|
||||
void session_tcp_impl::read_socket(
|
||||
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
|
||||
socket_.async_read_some(boost::asio::buffer(rb_), h);
|
||||
}
|
||||
|
||||
void session_tcp_impl::write_socket(
|
||||
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
|
||||
boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h);
|
||||
}
|
||||
|
||||
void session_tcp_impl::shutdown_socket() { socket_.close(); }
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_SESSION_TCP_IMPL_H
|
||||
#define ASIO_CLIENT_SESSION_TCP_IMPL_H
|
||||
|
||||
#include "asio_client_session_impl.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
class session_tcp_impl : public session_impl {
|
||||
public:
|
||||
session_tcp_impl(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service);
|
||||
virtual ~session_tcp_impl();
|
||||
|
||||
virtual void start_connect(tcp::resolver::iterator endpoint_it);
|
||||
virtual tcp::socket &socket();
|
||||
virtual void read_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h);
|
||||
virtual void write_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h);
|
||||
virtual void shutdown_socket();
|
||||
|
||||
private:
|
||||
tcp::socket socket_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_SESSION_TCP_IMPL_H
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_client_session_tls_impl.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
session_tls_impl::session_tls_impl(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx,
|
||||
const std::string &host,
|
||||
const std::string &service)
|
||||
: session_impl(io_service), socket_(io_service, tls_ctx) {
|
||||
// this callback setting is no effect is
|
||||
// ssl::context::set_verify_mode(boost::asio::ssl::verify_peer) is
|
||||
// not used, which is what we want.
|
||||
socket_.set_verify_callback(boost::asio::ssl::rfc2818_verification(host));
|
||||
|
||||
start_resolve(host, service);
|
||||
}
|
||||
|
||||
session_tls_impl::~session_tls_impl() {}
|
||||
|
||||
void session_tls_impl::start_connect(tcp::resolver::iterator endpoint_it) {
|
||||
boost::asio::async_connect(socket(), endpoint_it,
|
||||
[this](const boost::system::error_code &ec,
|
||||
tcp::resolver::iterator endpoint_it) {
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
}
|
||||
|
||||
socket_.async_handshake(
|
||||
boost::asio::ssl::stream_base::client,
|
||||
[this, endpoint_it](const boost::system::error_code &ec) {
|
||||
if (ec) {
|
||||
not_connected(ec);
|
||||
return;
|
||||
}
|
||||
connected(endpoint_it);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
tcp::socket &session_tls_impl::socket() { return socket_.next_layer(); }
|
||||
|
||||
void session_tls_impl::read_socket(
|
||||
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
|
||||
socket_.async_read_some(boost::asio::buffer(rb_), h);
|
||||
}
|
||||
|
||||
void session_tls_impl::write_socket(
|
||||
std::function<void(const boost::system::error_code &ec, std::size_t n)> h) {
|
||||
boost::asio::async_write(socket_, boost::asio::buffer(wb_, wblen_), h);
|
||||
}
|
||||
|
||||
void session_tls_impl::shutdown_socket() {
|
||||
socket_.async_shutdown([](const boost::system::error_code &ec) {});
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_SESSION_TLS_IMPL_H
|
||||
#define ASIO_CLIENT_SESSION_TLS_IMPL_H
|
||||
|
||||
#include "asio_client_session_impl.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
using ssl_socket = boost::asio::ssl::stream<tcp::socket>;
|
||||
|
||||
class session_tls_impl : public session_impl {
|
||||
public:
|
||||
session_tls_impl(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_ctx, const std::string &host,
|
||||
const std::string &service);
|
||||
virtual ~session_tls_impl();
|
||||
|
||||
virtual void start_connect(tcp::resolver::iterator endpoint_it);
|
||||
virtual tcp::socket &socket();
|
||||
virtual void read_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h);
|
||||
virtual void write_socket(std::function<
|
||||
void(const boost::system::error_code &ec, std::size_t n)> h);
|
||||
virtual void shutdown_socket();
|
||||
|
||||
private:
|
||||
ssl_socket socket_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_SESSION_TLS_IMPL_H
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_client_stream.h"
|
||||
|
||||
#include "asio_client_request_impl.h"
|
||||
#include "asio_client_response_impl.h"
|
||||
#include "asio_client_session_impl.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
stream::stream(session_impl *sess) : sess_(sess), stream_id_(0) {
|
||||
request_.impl().stream(this);
|
||||
}
|
||||
|
||||
void stream::stream_id(int32_t stream_id) { stream_id_ = stream_id; }
|
||||
|
||||
int32_t stream::stream_id() const { return stream_id_; }
|
||||
|
||||
class request &stream::request() {
|
||||
return request_;
|
||||
}
|
||||
|
||||
class response &stream::response() {
|
||||
return response_;
|
||||
}
|
||||
|
||||
session_impl *stream::session() const { return sess_; }
|
||||
|
||||
bool stream::expect_final_response() const {
|
||||
return response_.status_code() / 100 == 1;
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_STREAM_H
|
||||
#define ASIO_CLIENT_STREAM_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
class request;
|
||||
class response;
|
||||
class session_impl;
|
||||
|
||||
class stream {
|
||||
public:
|
||||
stream(session_impl *sess);
|
||||
|
||||
stream(const stream &) = delete;
|
||||
stream &operator=(const stream &) = delete;
|
||||
|
||||
void stream_id(int32_t stream_id);
|
||||
int32_t stream_id() const;
|
||||
|
||||
class request &request();
|
||||
class response &response();
|
||||
|
||||
session_impl *session() const;
|
||||
|
||||
bool expect_final_response() const;
|
||||
|
||||
private:
|
||||
nghttp2::asio_http2::client::request request_;
|
||||
nghttp2::asio_http2::client::response response_;
|
||||
session_impl *sess_;
|
||||
uint32_t stream_id_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_CLIENT_STREAM_H
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_client_tls_context.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
#include "ssl.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace client {
|
||||
|
||||
namespace {
|
||||
int client_select_next_proto_cb(SSL *ssl, unsigned char **out,
|
||||
unsigned char *outlen, const unsigned char *in,
|
||||
unsigned int inlen, void *arg) {
|
||||
if (!util::select_h2(const_cast<const unsigned char **>(out), outlen, in,
|
||||
inlen)) {
|
||||
return SSL_TLSEXT_ERR_NOACK;
|
||||
}
|
||||
return SSL_TLSEXT_ERR_OK;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void configure_tls_context(boost::asio::ssl::context &tls_ctx) {
|
||||
auto ctx = tls_ctx.native_handle();
|
||||
|
||||
SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr);
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_CLIENT_TLS_CONTEXT_H
|
||||
#define ASIO_CLIENT_TLS_CONTEXT_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_client.h>
|
||||
|
||||
#endif // ASIO_CLIENT_TLS_CONTEXT_H
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_common.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
|
||||
class nghttp2_category_impl : public boost::system::error_category {
|
||||
public:
|
||||
const char *name() const noexcept { return "nghttp2"; }
|
||||
std::string message(int ev) const { return nghttp2_strerror(ev); }
|
||||
};
|
||||
|
||||
const boost::system::error_category &nghttp2_category() noexcept {
|
||||
static nghttp2_category_impl cat;
|
||||
return cat;
|
||||
}
|
||||
|
||||
boost::system::error_code make_error_code(nghttp2_error ev) {
|
||||
return boost::system::error_code(static_cast<int>(ev), nghttp2_category());
|
||||
}
|
||||
|
||||
generator_cb string_generator(std::string data) {
|
||||
auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
|
||||
data.size());
|
||||
return [strio](uint8_t *buf, size_t len, uint32_t *data_flags) {
|
||||
auto &data = strio->first;
|
||||
auto &left = strio->second;
|
||||
auto n = std::min(len, left);
|
||||
std::copy_n(data.c_str() + data.size() - left, n, buf);
|
||||
left -= n;
|
||||
if (left == 0) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
}
|
||||
|
||||
generator_cb deferred_generator() {
|
||||
return [](uint8_t *buf, size_t len,
|
||||
uint32_t *data_flags) { return NGHTTP2_ERR_DEFERRED; };
|
||||
}
|
||||
|
||||
template <typename F, typename... T>
|
||||
std::shared_ptr<Defer<F, T...>> defer_shared(F &&f, T &&... t) {
|
||||
return std::make_shared<Defer<F, T...>>(std::forward<F>(f),
|
||||
std::forward<T>(t)...);
|
||||
}
|
||||
|
||||
generator_cb file_generator(const std::string &path) {
|
||||
auto fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
return generator_cb();
|
||||
}
|
||||
|
||||
return file_generator_from_fd(fd);
|
||||
}
|
||||
|
||||
generator_cb file_generator_from_fd(int fd) {
|
||||
auto d = defer_shared(close, fd);
|
||||
|
||||
return [fd, d](uint8_t *buf, size_t len, uint32_t *data_flags)
|
||||
-> generator_cb::result_type {
|
||||
ssize_t n;
|
||||
while ((n = read(fd, buf, len)) == -1 && errno == EINTR)
|
||||
;
|
||||
|
||||
if (n == -1) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (n == 0) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
}
|
||||
|
||||
return n;
|
||||
};
|
||||
}
|
||||
|
||||
bool check_path(const std::string &path) { return util::check_path(path); }
|
||||
|
||||
std::string percent_decode(const std::string &s) {
|
||||
return util::percentDecode(std::begin(s), std::end(s));
|
||||
}
|
||||
|
||||
std::string http_date(int64_t t) { return util::http_date(t); }
|
||||
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_COMMON_H
|
||||
#define ASIO_COMMON_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
boost::system::error_code make_error_code(nghttp2_error ev);
|
||||
|
||||
generator_cb string_generator(std::string data);
|
||||
|
||||
// Returns generator_cb, which just returns NGHTTP2_ERR_DEFERRED
|
||||
generator_cb deferred_generator();
|
||||
|
||||
template <typename InputIt>
|
||||
void split_path(uri_ref &dst, InputIt first, InputIt last) {
|
||||
auto path_last = std::find(first, last, '?');
|
||||
InputIt query_first;
|
||||
if (path_last == last) {
|
||||
query_first = path_last = last;
|
||||
} else {
|
||||
query_first = path_last + 1;
|
||||
}
|
||||
dst.path = util::percentDecode(first, path_last);
|
||||
dst.raw_path.assign(first, path_last);
|
||||
dst.raw_query.assign(query_first, last);
|
||||
}
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_COMMON_H
|
|
@ -1,711 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include "asio_http2_handler.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
channel::channel() : impl_(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;
|
||||
}
|
||||
|
||||
namespace server {
|
||||
|
||||
extern std::shared_ptr<std::string> cached_date;
|
||||
|
||||
request::request() : impl_(make_unique<request_impl>()) {}
|
||||
|
||||
const std::vector<header> &request::headers() const { return impl_->headers(); }
|
||||
|
||||
const std::string &request::method() const { return impl_->method(); }
|
||||
|
||||
const std::string &request::scheme() const { return impl_->scheme(); }
|
||||
|
||||
const std::string &request::authority() const { return impl_->authority(); }
|
||||
|
||||
const std::string &request::host() const { return impl_->host(); }
|
||||
|
||||
const std::string &request::path() const { return impl_->path(); }
|
||||
|
||||
bool request::push(std::string method, std::string path,
|
||||
std::vector<header> headers) {
|
||||
return impl_->push(std::move(method), std::move(path), std::move(headers));
|
||||
}
|
||||
|
||||
bool request::pushed() const { return impl_->pushed(); }
|
||||
|
||||
bool request::closed() const { return impl_->closed(); }
|
||||
|
||||
void request::on_data(data_cb cb) { return impl_->on_data(std::move(cb)); }
|
||||
|
||||
void request::on_end(void_cb 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() { return *impl_; }
|
||||
|
||||
response::response() : impl_(make_unique<response_impl>()) {}
|
||||
|
||||
void response::write_head(unsigned int status_code,
|
||||
std::vector<header> headers) {
|
||||
impl_->write_head(status_code, std::move(headers));
|
||||
}
|
||||
|
||||
void response::end(std::string data) { impl_->end(std::move(data)); }
|
||||
|
||||
void response::end(read_cb cb) { impl_->end(std::move(cb)); }
|
||||
|
||||
void response::resume() { impl_->resume(); }
|
||||
|
||||
unsigned int response::status_code() const { return impl_->status_code(); }
|
||||
|
||||
bool response::started() const { return impl_->started(); }
|
||||
|
||||
response_impl &response::impl() { return *impl_; }
|
||||
|
||||
request_impl::request_impl() : pushed_(false) {}
|
||||
|
||||
const std::vector<header> &request_impl::headers() const { return headers_; }
|
||||
|
||||
const std::string &request_impl::method() const { return method_; }
|
||||
|
||||
const std::string &request_impl::scheme() const { return scheme_; }
|
||||
|
||||
const std::string &request_impl::authority() const { return authority_; }
|
||||
|
||||
const std::string &request_impl::host() const { return host_; }
|
||||
|
||||
const std::string &request_impl::path() const { return path_; }
|
||||
|
||||
void request_impl::set_header(std::vector<header> headers) {
|
||||
headers_ = std::move(headers);
|
||||
}
|
||||
|
||||
void request_impl::add_header(std::string name, std::string value) {
|
||||
headers_.push_back(header{std::move(name), std::move(value)});
|
||||
}
|
||||
|
||||
void request_impl::method(std::string arg) { method_ = std::move(arg); }
|
||||
|
||||
void request_impl::scheme(std::string arg) { scheme_ = std::move(arg); }
|
||||
|
||||
void request_impl::authority(std::string arg) { authority_ = std::move(arg); }
|
||||
|
||||
void request_impl::host(std::string arg) { host_ = std::move(arg); }
|
||||
|
||||
void request_impl::path(std::string arg) { path_ = std::move(arg); }
|
||||
|
||||
bool request_impl::push(std::string method, std::string path,
|
||||
std::vector<header> headers) {
|
||||
if (closed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto handler = handler_.lock();
|
||||
auto stream = stream_.lock();
|
||||
auto rv = handler->push_promise(*stream, std::move(method), std::move(path),
|
||||
std::move(headers));
|
||||
return rv == 0;
|
||||
}
|
||||
|
||||
bool request_impl::pushed() const { return pushed_; }
|
||||
|
||||
void request_impl::pushed(bool f) { pushed_ = f; }
|
||||
|
||||
bool request_impl::closed() const {
|
||||
return handler_.expired() || stream_.expired();
|
||||
}
|
||||
|
||||
void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); }
|
||||
|
||||
void request_impl::on_end(void_cb 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) {
|
||||
handler_ = std::move(h);
|
||||
}
|
||||
|
||||
void request_impl::stream(std::weak_ptr<http2_stream> s) {
|
||||
stream_ = std::move(s);
|
||||
}
|
||||
|
||||
void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
|
||||
if (on_data_cb_) {
|
||||
on_data_cb_(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void request_impl::call_on_end() {
|
||||
if (on_end_cb_) {
|
||||
on_end_cb_();
|
||||
}
|
||||
}
|
||||
|
||||
response_impl::response_impl() : status_code_(200), started_(false) {}
|
||||
|
||||
unsigned int response_impl::status_code() const { return status_code_; }
|
||||
|
||||
void response_impl::write_head(unsigned int status_code,
|
||||
std::vector<header> headers) {
|
||||
status_code_ = status_code;
|
||||
headers_ = std::move(headers);
|
||||
}
|
||||
|
||||
void response_impl::end(std::string data) {
|
||||
if (started_) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
|
||||
data.size());
|
||||
auto read_cb = [strio](uint8_t *buf, size_t len) {
|
||||
auto nread = std::min(len, strio->second);
|
||||
memcpy(buf, strio->first.c_str(), nread);
|
||||
strio->second -= nread;
|
||||
if (strio->second == 0) {
|
||||
return std::make_pair(nread, true);
|
||||
}
|
||||
|
||||
return std::make_pair(nread, false);
|
||||
};
|
||||
|
||||
end(std::move(read_cb));
|
||||
}
|
||||
|
||||
void response_impl::end(read_cb cb) {
|
||||
if (started_ || closed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
read_cb_ = std::move(cb);
|
||||
started_ = true;
|
||||
|
||||
auto handler = handler_.lock();
|
||||
auto stream = stream_.lock();
|
||||
|
||||
if (handler->start_response(*stream) != 0) {
|
||||
handler->stream_error(stream->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handler->inside_callback()) {
|
||||
handler->initiate_write();
|
||||
}
|
||||
}
|
||||
|
||||
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 { return started_; }
|
||||
|
||||
const std::vector<header> &response_impl::headers() const { return headers_; }
|
||||
|
||||
void response_impl::handler(std::weak_ptr<http2_handler> h) {
|
||||
handler_ = std::move(h);
|
||||
}
|
||||
|
||||
void response_impl::stream(std::weak_ptr<http2_stream> s) {
|
||||
stream_ = std::move(s);
|
||||
}
|
||||
|
||||
std::pair<ssize_t, bool> response_impl::call_read(uint8_t *data,
|
||||
std::size_t len) {
|
||||
if (read_cb_) {
|
||||
return read_cb_(data, len);
|
||||
}
|
||||
|
||||
return std::make_pair(0, true);
|
||||
}
|
||||
|
||||
http2_stream::http2_stream(int32_t stream_id)
|
||||
: request_(std::make_shared<request>()),
|
||||
response_(std::make_shared<response>()), stream_id_(stream_id) {}
|
||||
|
||||
int32_t http2_stream::get_stream_id() const { return stream_id_; }
|
||||
|
||||
const std::shared_ptr<request> &http2_stream::get_request() { return request_; }
|
||||
|
||||
const std::shared_ptr<response> &http2_stream::get_response() {
|
||||
return response_;
|
||||
}
|
||||
|
||||
namespace {
|
||||
int stream_error(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code) {
|
||||
return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||
error_code);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
handler->create_stream(frame->hd.stream_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen, uint8_t flags,
|
||||
void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
auto stream_id = frame->hd.stream_id;
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto stream = handler->find_stream(stream_id);
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &req = stream->get_request()->impl();
|
||||
|
||||
switch (nghttp2::http2::lookup_token(name, namelen)) {
|
||||
case nghttp2::http2::HD__METHOD:
|
||||
req.method(std::string(value, value + valuelen));
|
||||
break;
|
||||
case nghttp2::http2::HD__SCHEME:
|
||||
req.scheme(std::string(value, value + valuelen));
|
||||
break;
|
||||
case nghttp2::http2::HD__AUTHORITY:
|
||||
req.authority(std::string(value, value + valuelen));
|
||||
break;
|
||||
case nghttp2::http2::HD__PATH:
|
||||
req.path(std::string(value, value + valuelen));
|
||||
break;
|
||||
case nghttp2::http2::HD_HOST:
|
||||
req.host(std::string(value, value + valuelen));
|
||||
// fall through
|
||||
default:
|
||||
req.add_header(std::string(name, name + namelen),
|
||||
std::string(value, value + valuelen));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
auto stream = handler->find_stream(frame->hd.stream_id);
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
if (!stream) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream->get_request()->impl().call_on_end();
|
||||
}
|
||||
|
||||
break;
|
||||
case NGHTTP2_HEADERS: {
|
||||
if (!stream || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto &req = stream->get_request()->impl();
|
||||
|
||||
if (req.host().empty()) {
|
||||
req.host(req.authority());
|
||||
}
|
||||
|
||||
handler->call_on_request(*stream);
|
||||
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
stream->get_request()->impl().call_on_end();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, const uint8_t *data,
|
||||
size_t len, void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
auto stream = handler->find_stream(stream_id);
|
||||
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream->get_request()->impl().call_on_data(data, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code, void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
|
||||
handler->close_stream(stream_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
|
||||
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto stream = handler->find_stream(frame->push_promise.promised_stream_id);
|
||||
|
||||
if (!stream) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
handler->call_on_request(*stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_not_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, int lib_error_code,
|
||||
void *user_data) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Issue RST_STREAM so that stream does not hang around.
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
http2_handler::http2_handler(boost::asio::io_service &io_service,
|
||||
boost::asio::io_service &task_io_service_,
|
||||
connection_write writefun, request_cb cb)
|
||||
: writefun_(writefun), request_cb_(std::move(cb)), io_service_(io_service),
|
||||
task_io_service_(task_io_service_),
|
||||
strand_(std::make_shared<boost::asio::io_service::strand>(io_service_)),
|
||||
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false) {}
|
||||
|
||||
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
|
||||
|
||||
int http2_handler::start() {
|
||||
int rv;
|
||||
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
||||
on_frame_send_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
||||
callbacks, on_frame_not_send_callback);
|
||||
|
||||
nghttp2_option *option;
|
||||
rv = nghttp2_option_new(&option);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto opt_del = defer(nghttp2_option_del, option);
|
||||
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
|
||||
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<http2_stream> http2_handler::create_stream(int32_t stream_id) {
|
||||
auto stream = std::make_shared<http2_stream>(stream_id);
|
||||
streams_.emplace(stream_id, stream);
|
||||
|
||||
auto self = shared_from_this();
|
||||
auto &req = stream->get_request()->impl();
|
||||
auto &res = stream->get_response()->impl();
|
||||
req.handler(self);
|
||||
req.stream(stream);
|
||||
res.handler(self);
|
||||
res.stream(stream);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
void http2_handler::close_stream(int32_t stream_id) {
|
||||
streams_.erase(stream_id);
|
||||
}
|
||||
|
||||
std::shared_ptr<http2_stream> http2_handler::find_stream(int32_t stream_id) {
|
||||
auto i = streams_.find(stream_id);
|
||||
if (i == std::end(streams_)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (*i).second;
|
||||
}
|
||||
|
||||
void http2_handler::call_on_request(http2_stream &stream) {
|
||||
request_cb_(stream.get_request(), stream.get_response());
|
||||
}
|
||||
|
||||
bool http2_handler::should_stop() const {
|
||||
return !nghttp2_session_want_read(session_) &&
|
||||
!nghttp2_session_want_write(session_);
|
||||
}
|
||||
|
||||
int http2_handler::start_response(http2_stream &stream) {
|
||||
int rv;
|
||||
|
||||
auto &res = stream.get_response()->impl();
|
||||
auto &headers = res.headers();
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(2 + headers.size());
|
||||
auto status = util::utos(res.status_code());
|
||||
auto date = cached_date;
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
|
||||
nva.push_back(nghttp2::http2::make_nv_ls("date", *date));
|
||||
for (auto &hd : headers) {
|
||||
nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
|
||||
}
|
||||
|
||||
nghttp2_data_provider prd;
|
||||
prd.source.ptr = &stream;
|
||||
prd.read_callback =
|
||||
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
|
||||
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
|
||||
void *user_data) -> ssize_t {
|
||||
auto &stream = *static_cast<http2_stream *>(source->ptr);
|
||||
auto rv = stream.get_response()->impl().call_read(buf, length);
|
||||
if (rv.first < 0) {
|
||||
return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
if (rv.second) {
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
} else if (rv.first == 0) {
|
||||
return NGHTTP2_ERR_DEFERRED;
|
||||
}
|
||||
|
||||
return rv.first;
|
||||
};
|
||||
|
||||
rv = nghttp2_submit_response(session_, stream.get_stream_id(), nva.data(),
|
||||
nva.size(), &prd);
|
||||
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void http2_handler::enter_callback() {
|
||||
assert(!inside_callback_);
|
||||
inside_callback_ = true;
|
||||
}
|
||||
|
||||
void http2_handler::leave_callback() {
|
||||
assert(inside_callback_);
|
||||
inside_callback_ = false;
|
||||
}
|
||||
|
||||
bool http2_handler::inside_callback() const { return inside_callback_; }
|
||||
|
||||
void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
|
||||
::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
|
||||
}
|
||||
|
||||
void http2_handler::initiate_write() { 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,
|
||||
std::string path, std::vector<header> headers) {
|
||||
int rv;
|
||||
|
||||
auto &req = stream.get_request()->impl();
|
||||
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(5 + headers.size());
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.scheme()));
|
||||
if (!req.authority().empty()) {
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.authority()));
|
||||
}
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":path", path));
|
||||
if (!req.host().empty()) {
|
||||
nva.push_back(nghttp2::http2::make_nv_ls("host", req.host()));
|
||||
}
|
||||
|
||||
for (auto &hd : headers) {
|
||||
nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
|
||||
}
|
||||
|
||||
rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
|
||||
stream.get_stream_id(), nva.data(),
|
||||
nva.size(), nullptr);
|
||||
|
||||
if (rv < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto promised_stream = create_stream(rv);
|
||||
auto &promised_req = promised_stream->get_request()->impl();
|
||||
promised_req.pushed(true);
|
||||
promised_req.method(std::move(method));
|
||||
promised_req.scheme(req.scheme());
|
||||
promised_req.authority(req.authority());
|
||||
promised_req.path(std::move(path));
|
||||
promised_req.host(req.host());
|
||||
promised_req.set_header(std::move(headers));
|
||||
if (!req.host().empty()) {
|
||||
promised_req.add_header("host", req.host());
|
||||
}
|
||||
|
||||
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() { return io_service_; }
|
||||
|
||||
callback_guard::callback_guard(http2_handler &h) : handler(h) {
|
||||
handler.enter_callback();
|
||||
}
|
||||
|
||||
callback_guard::~callback_guard() { handler.leave_callback(); }
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
|
@ -1,265 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef HTTP2_HANDLER_H
|
||||
#define HTTP2_HANDLER_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
namespace server {
|
||||
|
||||
class http2_handler;
|
||||
class http2_stream;
|
||||
|
||||
class request_impl {
|
||||
public:
|
||||
request_impl();
|
||||
|
||||
const std::vector<header> &headers() const;
|
||||
const std::string &method() const;
|
||||
const std::string &scheme() const;
|
||||
const std::string &authority() const;
|
||||
const std::string &host() const;
|
||||
const std::string &path() const;
|
||||
|
||||
bool push(std::string method, std::string path,
|
||||
std::vector<header> headers = {});
|
||||
|
||||
bool pushed() const;
|
||||
bool closed() const;
|
||||
|
||||
void on_data(data_cb cb);
|
||||
void on_end(void_cb cb);
|
||||
|
||||
bool run_task(thread_cb start);
|
||||
|
||||
void set_header(std::vector<header> headers);
|
||||
void add_header(std::string name, std::string value);
|
||||
void method(std::string method);
|
||||
void scheme(std::string scheme);
|
||||
void authority(std::string authority);
|
||||
void host(std::string host);
|
||||
void path(std::string path);
|
||||
void pushed(bool f);
|
||||
void handler(std::weak_ptr<http2_handler> h);
|
||||
void stream(std::weak_ptr<http2_stream> s);
|
||||
void call_on_data(const uint8_t *data, std::size_t len);
|
||||
void call_on_end();
|
||||
|
||||
private:
|
||||
std::vector<header> headers_;
|
||||
std::string method_;
|
||||
std::string scheme_;
|
||||
std::string authority_;
|
||||
std::string host_;
|
||||
std::string path_;
|
||||
data_cb on_data_cb_;
|
||||
void_cb on_end_cb_;
|
||||
std::weak_ptr<http2_handler> handler_;
|
||||
std::weak_ptr<http2_stream> stream_;
|
||||
bool pushed_;
|
||||
};
|
||||
|
||||
class response_impl {
|
||||
public:
|
||||
response_impl();
|
||||
void write_head(unsigned int status_code, std::vector<header> headers = {});
|
||||
void end(std::string data = "");
|
||||
void end(read_cb cb);
|
||||
void resume();
|
||||
bool closed() const;
|
||||
|
||||
unsigned int status_code() const;
|
||||
const std::vector<header> &headers() const;
|
||||
bool started() const;
|
||||
void handler(std::weak_ptr<http2_handler> h);
|
||||
void stream(std::weak_ptr<http2_stream> s);
|
||||
read_cb::result_type call_read(uint8_t *data, std::size_t len);
|
||||
|
||||
private:
|
||||
std::vector<header> headers_;
|
||||
read_cb read_cb_;
|
||||
std::weak_ptr<http2_handler> handler_;
|
||||
std::weak_ptr<http2_stream> stream_;
|
||||
unsigned int status_code_;
|
||||
bool started_;
|
||||
};
|
||||
|
||||
class http2_stream {
|
||||
public:
|
||||
http2_stream(int32_t stream_id);
|
||||
|
||||
int32_t get_stream_id() const;
|
||||
const std::shared_ptr<request> &get_request();
|
||||
const std::shared_ptr<response> &get_response();
|
||||
|
||||
private:
|
||||
std::shared_ptr<request> request_;
|
||||
std::shared_ptr<response> response_;
|
||||
int32_t stream_id_;
|
||||
};
|
||||
|
||||
struct callback_guard {
|
||||
callback_guard(http2_handler &h);
|
||||
~callback_guard();
|
||||
http2_handler &handler;
|
||||
};
|
||||
|
||||
typedef std::function<void(void)> connection_write;
|
||||
|
||||
class http2_handler : public std::enable_shared_from_this<http2_handler> {
|
||||
public:
|
||||
http2_handler(boost::asio::io_service &io_service,
|
||||
boost::asio::io_service &task_io_service,
|
||||
connection_write writefun, request_cb cb);
|
||||
|
||||
~http2_handler();
|
||||
|
||||
int start();
|
||||
|
||||
std::shared_ptr<http2_stream> create_stream(int32_t stream_id);
|
||||
void close_stream(int32_t stream_id);
|
||||
std::shared_ptr<http2_stream> find_stream(int32_t stream_id);
|
||||
|
||||
void call_on_request(http2_stream &stream);
|
||||
|
||||
bool should_stop() const;
|
||||
|
||||
int start_response(http2_stream &stream);
|
||||
|
||||
void stream_error(int32_t stream_id, uint32_t error_code);
|
||||
|
||||
void initiate_write();
|
||||
|
||||
void enter_callback();
|
||||
void leave_callback();
|
||||
bool inside_callback() const;
|
||||
|
||||
void resume(http2_stream &stream);
|
||||
|
||||
int push_promise(http2_stream &stream, std::string method, std::string path,
|
||||
std::vector<header> headers);
|
||||
|
||||
bool run_task(thread_cb start);
|
||||
|
||||
boost::asio::io_service &io_service();
|
||||
|
||||
template <size_t N>
|
||||
int on_read(const boost::array<uint8_t, N> &buffer, std::size_t len) {
|
||||
callback_guard cg(*this);
|
||||
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_session_mem_recv(session_, buffer.data(), len);
|
||||
|
||||
if (rv < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
int on_write(boost::array<uint8_t, N> &buffer, std::size_t &len) {
|
||||
callback_guard cg(*this);
|
||||
|
||||
len = 0;
|
||||
|
||||
if (buf_) {
|
||||
std::copy_n(buf_, buflen_, std::begin(buffer));
|
||||
|
||||
len += buflen_;
|
||||
|
||||
buf_ = nullptr;
|
||||
buflen_ = 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
const uint8_t *data;
|
||||
auto nread = nghttp2_session_mem_send(session_, &data);
|
||||
if (nread < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nread == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len + nread > buffer.size()) {
|
||||
buf_ = data;
|
||||
buflen_ = nread;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
std::copy_n(data, nread, std::begin(buffer) + len);
|
||||
|
||||
len += nread;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<int32_t, std::shared_ptr<http2_stream>> streams_;
|
||||
connection_write writefun_;
|
||||
request_cb request_cb_;
|
||||
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_;
|
||||
const uint8_t *buf_;
|
||||
std::size_t buflen_;
|
||||
bool inside_callback_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp
|
||||
|
||||
#endif // HTTP2_HANDLER_H
|
|
@ -33,22 +33,15 @@
|
|||
// 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 "asio_io_service_pool.h"
|
||||
|
||||
#include "asio_server.h"
|
||||
#include <stdexcept>
|
||||
#include <future>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
namespace server {
|
||||
|
||||
io_service_pool::io_service_pool(std::size_t pool_size,
|
||||
std::size_t thread_pool_size)
|
||||
: next_io_service_(0), thread_pool_size_(thread_pool_size) {
|
||||
io_service_pool::io_service_pool(std::size_t pool_size) : next_io_service_(0) {
|
||||
if (pool_size == 0) {
|
||||
throw std::runtime_error("io_service_pool size is 0");
|
||||
}
|
||||
|
@ -61,16 +54,9 @@ io_service_pool::io_service_pool(std::size_t pool_size,
|
|||
io_services_.push_back(io_service);
|
||||
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() {
|
||||
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.
|
||||
auto futs = std::vector<std::future<std::size_t>>();
|
||||
|
||||
|
@ -85,8 +71,6 @@ void io_service_pool::run() {
|
|||
for (auto &fut : futs) {
|
||||
fut.get();
|
||||
}
|
||||
|
||||
thread_pool_.join_all();
|
||||
}
|
||||
|
||||
void io_service_pool::stop() {
|
||||
|
@ -94,8 +78,6 @@ void io_service_pool::stop() {
|
|||
for (auto &iosv : io_services_) {
|
||||
iosv->stop();
|
||||
}
|
||||
|
||||
task_io_service_.stop();
|
||||
}
|
||||
|
||||
boost::asio::io_service &io_service_pool::get_io_service() {
|
||||
|
@ -108,12 +90,6 @@ boost::asio::io_service &io_service_pool::get_io_service() {
|
|||
return io_service;
|
||||
}
|
||||
|
||||
boost::asio::io_service &io_service_pool::get_task_io_service() {
|
||||
return task_io_service_;
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
|
|
@ -34,14 +34,14 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_IO_SERVICE_POOL_HPP
|
||||
#define HTTP_SERVER2_IO_SERVICE_POOL_HPP
|
||||
#ifndef ASIO_IO_SERVICE_POOL_H
|
||||
#define ASIO_IO_SERVICE_POOL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
|
@ -51,13 +51,11 @@ namespace nghttp2 {
|
|||
|
||||
namespace asio_http2 {
|
||||
|
||||
namespace server {
|
||||
|
||||
/// A pool of io_service objects.
|
||||
class io_service_pool : private boost::noncopyable {
|
||||
public:
|
||||
/// Construct the io_service pool.
|
||||
explicit io_service_pool(std::size_t pool_size, std::size_t thread_pool_size);
|
||||
explicit io_service_pool(std::size_t pool_size);
|
||||
|
||||
/// Run all io_service objects in the pool.
|
||||
void run();
|
||||
|
@ -68,31 +66,19 @@ public:
|
|||
/// Get an io_service to use.
|
||||
boost::asio::io_service &get_io_service();
|
||||
|
||||
boost::asio::io_service &get_task_io_service();
|
||||
|
||||
private:
|
||||
typedef std::shared_ptr<boost::asio::io_service> io_service_ptr;
|
||||
typedef std::shared_ptr<boost::asio::io_service::work> work_ptr;
|
||||
|
||||
/// The pool of io_services.
|
||||
std::vector<io_service_ptr> io_services_;
|
||||
|
||||
boost::asio::io_service task_io_service_;
|
||||
boost::thread_group thread_pool_;
|
||||
std::vector<std::shared_ptr<boost::asio::io_service>> io_services_;
|
||||
|
||||
/// The work that keeps the io_services running.
|
||||
std::vector<work_ptr> work_;
|
||||
std::vector<std::shared_ptr<boost::asio::io_service::work>> work_;
|
||||
|
||||
/// The next io_service to use for a connection.
|
||||
std::size_t next_io_service_;
|
||||
|
||||
std::size_t thread_pool_size_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // HTTP_SERVER2_IO_SERVICE_POOL_HPP
|
||||
#endif // ASIO_IO_SERVICE_POOL_H
|
||||
|
|
|
@ -38,19 +38,21 @@
|
|||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
#include "asio_server_connection.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
server::server(const std::string &address, uint16_t port,
|
||||
std::size_t io_service_pool_size, std::size_t thread_pool_size,
|
||||
request_cb cb,
|
||||
std::size_t io_service_pool_size, serve_mux &mux,
|
||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx, int backlog)
|
||||
: io_service_pool_(io_service_pool_size, thread_pool_size),
|
||||
: io_service_pool_(io_service_pool_size),
|
||||
signals_(io_service_pool_.get_io_service()),
|
||||
tick_timer_(io_service_pool_.get_io_service(),
|
||||
boost::posix_time::seconds(1)),
|
||||
ssl_ctx_(std::move(ssl_ctx)), request_cb_(std::move(cb)) {
|
||||
ssl_ctx_(std::move(ssl_ctx)), mux_(mux) {
|
||||
// Register to handle the signals that indicate when the server should exit.
|
||||
// It is safe to register for the same signal multiple times in a program,
|
||||
// provided all registration for the specified signal is made through Asio.
|
||||
|
@ -83,7 +85,9 @@ server::server(const std::string &address, uint16_t port,
|
|||
acceptors_.push_back(std::move(acceptor));
|
||||
}
|
||||
|
||||
start_accept();
|
||||
for (auto &acceptor : acceptors_) {
|
||||
start_accept(acceptor);
|
||||
}
|
||||
|
||||
start_timer();
|
||||
}
|
||||
|
@ -110,50 +114,44 @@ void server::start_timer() {
|
|||
|
||||
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
|
||||
|
||||
void server::start_accept() {
|
||||
void server::start_accept(boost::asio::ip::tcp::acceptor &acceptor) {
|
||||
if (ssl_ctx_) {
|
||||
auto new_connection = std::make_shared<connection<ssl_socket>>(
|
||||
request_cb_, io_service_pool_.get_task_io_service(),
|
||||
io_service_pool_.get_io_service(), *ssl_ctx_);
|
||||
mux_, io_service_pool_.get_io_service(), *ssl_ctx_);
|
||||
|
||||
for (auto &acceptor : acceptors_) {
|
||||
acceptor.async_accept(
|
||||
new_connection->socket().lowest_layer(),
|
||||
[this, new_connection](const boost::system::error_code &e) {
|
||||
if (!e) {
|
||||
new_connection->socket().lowest_layer().set_option(
|
||||
boost::asio::ip::tcp::no_delay(true));
|
||||
new_connection->socket().async_handshake(
|
||||
boost::asio::ssl::stream_base::server,
|
||||
[new_connection](const boost::system::error_code &e) {
|
||||
if (!e) {
|
||||
new_connection->start();
|
||||
}
|
||||
});
|
||||
}
|
||||
acceptor.async_accept(
|
||||
new_connection->socket().lowest_layer(),
|
||||
[this, &acceptor, new_connection](const boost::system::error_code &e) {
|
||||
if (!e) {
|
||||
new_connection->socket().lowest_layer().set_option(
|
||||
boost::asio::ip::tcp::no_delay(true));
|
||||
new_connection->socket().async_handshake(
|
||||
boost::asio::ssl::stream_base::server,
|
||||
[new_connection](const boost::system::error_code &e) {
|
||||
if (!e) {
|
||||
new_connection->start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
start_accept();
|
||||
});
|
||||
}
|
||||
start_accept(acceptor);
|
||||
});
|
||||
} else {
|
||||
auto new_connection =
|
||||
std::make_shared<connection<boost::asio::ip::tcp::socket>>(
|
||||
request_cb_, io_service_pool_.get_task_io_service(),
|
||||
io_service_pool_.get_io_service());
|
||||
mux_, io_service_pool_.get_io_service());
|
||||
|
||||
for (auto &acceptor : acceptors_) {
|
||||
acceptor.async_accept(
|
||||
new_connection->socket(),
|
||||
[this, new_connection](const boost::system::error_code &e) {
|
||||
if (!e) {
|
||||
new_connection->socket().set_option(
|
||||
boost::asio::ip::tcp::no_delay(true));
|
||||
new_connection->start();
|
||||
}
|
||||
acceptor.async_accept(
|
||||
new_connection->socket(),
|
||||
[this, &acceptor, new_connection](const boost::system::error_code &e) {
|
||||
if (!e) {
|
||||
new_connection->socket().set_option(
|
||||
boost::asio::ip::tcp::no_delay(true));
|
||||
new_connection->start();
|
||||
}
|
||||
|
||||
start_accept();
|
||||
});
|
||||
}
|
||||
start_accept(acceptor);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,21 +34,19 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_SERVER_HPP
|
||||
#define HTTP_SERVER2_SERVER_HPP
|
||||
#ifndef ASIO_SERVER_H
|
||||
#define ASIO_SERVER_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
#include "asio_connection.h"
|
||||
#include "asio_io_service_pool.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
@ -57,14 +55,15 @@ namespace asio_http2 {
|
|||
|
||||
namespace server {
|
||||
|
||||
class serve_mux;
|
||||
|
||||
/// The top-level class of the HTTP server.
|
||||
class server : private boost::noncopyable {
|
||||
public:
|
||||
/// Construct the server to listen on the specified TCP address and port, and
|
||||
/// serve up files from the given directory.
|
||||
explicit server(const std::string &address, uint16_t port,
|
||||
std::size_t io_service_pool_size,
|
||||
std::size_t thread_pool_size, request_cb cb,
|
||||
std::size_t io_service_pool_size, serve_mux &mux_,
|
||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx,
|
||||
int backlog = -1);
|
||||
|
||||
|
@ -73,7 +72,7 @@ public:
|
|||
|
||||
private:
|
||||
/// Initiate an asynchronous accept operation.
|
||||
void start_accept();
|
||||
void start_accept(boost::asio::ip::tcp::acceptor &acceptor);
|
||||
|
||||
void start_timer();
|
||||
|
||||
|
@ -90,7 +89,7 @@ private:
|
|||
|
||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx_;
|
||||
|
||||
request_cb request_cb_;
|
||||
serve_mux &mux_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
|
@ -99,4 +98,4 @@ private:
|
|||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // HTTP_SERVER2_SERVER_HPP
|
||||
#endif // ASIO_SERVER_H
|
||||
|
|
|
@ -34,19 +34,20 @@
|
|||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
|
||||
#ifndef HTTP_SERVER2_CONNECTION_HPP
|
||||
#define HTTP_SERVER2_CONNECTION_HPP
|
||||
#ifndef ASIO_SERVER_CONNECTION_H
|
||||
#define ASIO_SERVER_CONNECTION_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/array.hpp>
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include "asio_http2_handler.h"
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
#include "asio_server_http2_handler.h"
|
||||
#include "asio_server_serve_mux.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
@ -62,16 +63,14 @@ class connection : public std::enable_shared_from_this<connection<socket_type>>,
|
|||
public:
|
||||
/// Construct a connection with the given io_service.
|
||||
template <typename... SocketArgs>
|
||||
explicit connection(request_cb cb, boost::asio::io_service &task_io_service,
|
||||
SocketArgs &&... args)
|
||||
: socket_(std::forward<SocketArgs>(args)...), request_cb_(std::move(cb)),
|
||||
task_io_service_(task_io_service), writing_(false) {}
|
||||
explicit connection(serve_mux &mux, SocketArgs &&... args)
|
||||
: socket_(std::forward<SocketArgs>(args)...), mux_(mux), writing_(false) {
|
||||
}
|
||||
|
||||
/// Start the first asynchronous operation for the connection.
|
||||
void start() {
|
||||
handler_ = std::make_shared<http2_handler>(
|
||||
socket_.get_io_service(), task_io_service_, [this]() { do_write(); },
|
||||
request_cb_);
|
||||
handler_ = std::make_shared<http2_handler>(socket_.get_io_service(),
|
||||
[this]() { do_write(); }, mux_);
|
||||
if (handler_->start() != 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -149,9 +148,7 @@ public:
|
|||
private:
|
||||
socket_type socket_;
|
||||
|
||||
request_cb request_cb_;
|
||||
|
||||
boost::asio::io_service &task_io_service_;
|
||||
serve_mux &mux_;
|
||||
|
||||
std::shared_ptr<http2_handler> handler_;
|
||||
|
||||
|
@ -169,4 +166,4 @@ private:
|
|||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // HTTP_SERVER2_CONNECTION_HPP
|
||||
#endif // ASIO_SERVER_CONNECTION_H
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
#include "asio_server_http2_impl.h"
|
||||
#include "asio_server.h"
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
namespace server {
|
||||
|
||||
http2::http2() : impl_(make_unique<http2_impl>()) {}
|
||||
|
||||
http2::~http2() {}
|
||||
|
||||
void http2::listen_and_serve(const std::string &address, uint16_t port) {
|
||||
impl_->listen_and_serve(address, port);
|
||||
}
|
||||
|
||||
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
|
||||
|
||||
void http2::tls(std::string private_key_file, std::string certificate_file) {
|
||||
impl_->tls(std::move(private_key_file), std::move(certificate_file));
|
||||
}
|
||||
|
||||
void http2::backlog(int backlog) { impl_->backlog(backlog); }
|
||||
|
||||
bool http2::handle(std::string pattern, request_cb cb) {
|
||||
return impl_->handle(std::move(pattern), std::move(cb));
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,439 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include "asio_server_http2_handler.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "asio_common.h"
|
||||
#include "asio_server_serve_mux.h"
|
||||
#include "asio_server_stream.h"
|
||||
#include "asio_server_request_impl.h"
|
||||
#include "asio_server_response_impl.h"
|
||||
#include "http2.h"
|
||||
#include "util.h"
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
namespace server {
|
||||
|
||||
extern std::shared_ptr<std::string> cached_date;
|
||||
|
||||
namespace {
|
||||
int stream_error(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code) {
|
||||
return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
|
||||
error_code);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_begin_headers_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
handler->create_stream(frame->hd.stream_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen,
|
||||
const uint8_t *value, size_t valuelen, uint8_t flags,
|
||||
void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
auto stream_id = frame->hd.stream_id;
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS ||
|
||||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto strm = handler->find_stream(stream_id);
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &req = strm->request().impl();
|
||||
auto &uref = req.uri();
|
||||
|
||||
switch (nghttp2::http2::lookup_token(name, namelen)) {
|
||||
case nghttp2::http2::HD__METHOD:
|
||||
req.method(std::string(value, value + valuelen));
|
||||
break;
|
||||
case nghttp2::http2::HD__SCHEME:
|
||||
uref.scheme.assign(value, value + valuelen);
|
||||
break;
|
||||
case nghttp2::http2::HD__AUTHORITY:
|
||||
uref.host.assign(value, value + valuelen);
|
||||
break;
|
||||
case nghttp2::http2::HD__PATH:
|
||||
split_path(uref, value, value + valuelen);
|
||||
break;
|
||||
case nghttp2::http2::HD_HOST:
|
||||
if (uref.host.empty()) {
|
||||
uref.host.assign(value, value + valuelen);
|
||||
}
|
||||
// fall through
|
||||
default:
|
||||
req.header().emplace(std::string(name, name + namelen),
|
||||
header_value{std::string(value, value + valuelen),
|
||||
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
auto strm = handler->find_stream(frame->hd.stream_id);
|
||||
|
||||
switch (frame->hd.type) {
|
||||
case NGHTTP2_DATA:
|
||||
if (!strm) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
strm->request().impl().call_on_data(nullptr, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
case NGHTTP2_HEADERS: {
|
||||
if (!strm || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
|
||||
break;
|
||||
}
|
||||
|
||||
handler->call_on_request(*strm);
|
||||
|
||||
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||
strm->request().impl().call_on_data(nullptr, 0);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, const uint8_t *data,
|
||||
size_t len, void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
auto strm = handler->find_stream(stream_id);
|
||||
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
strm->request().impl().call_on_data(data, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
|
||||
uint32_t error_code, void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
|
||||
auto strm = handler->find_stream(stream_id);
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
strm->response().impl().call_on_close(error_code);
|
||||
|
||||
handler->close_stream(stream_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
void *user_data) {
|
||||
auto handler = static_cast<http2_handler *>(user_data);
|
||||
|
||||
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto strm = handler->find_stream(frame->push_promise.promised_stream_id);
|
||||
|
||||
if (!strm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto &res = strm->response().impl();
|
||||
res.push_promise_sent();
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
int on_frame_not_send_callback(nghttp2_session *session,
|
||||
const nghttp2_frame *frame, int lib_error_code,
|
||||
void *user_data) {
|
||||
if (frame->hd.type != NGHTTP2_HEADERS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Issue RST_STREAM so that stream does not hang around.
|
||||
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
|
||||
NGHTTP2_INTERNAL_ERROR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
http2_handler::http2_handler(boost::asio::io_service &io_service,
|
||||
connection_write writefun, serve_mux &mux)
|
||||
: writefun_(writefun), mux_(mux), io_service_(io_service),
|
||||
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false) {}
|
||||
|
||||
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
|
||||
|
||||
int http2_handler::start() {
|
||||
int rv;
|
||||
|
||||
nghttp2_session_callbacks *callbacks;
|
||||
rv = nghttp2_session_callbacks_new(&callbacks);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
|
||||
|
||||
nghttp2_session_callbacks_set_on_begin_headers_callback(
|
||||
callbacks, on_begin_headers_callback);
|
||||
nghttp2_session_callbacks_set_on_header_callback(callbacks,
|
||||
on_header_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
|
||||
on_frame_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
|
||||
callbacks, on_data_chunk_recv_callback);
|
||||
nghttp2_session_callbacks_set_on_stream_close_callback(
|
||||
callbacks, on_stream_close_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
|
||||
on_frame_send_callback);
|
||||
nghttp2_session_callbacks_set_on_frame_not_send_callback(
|
||||
callbacks, on_frame_not_send_callback);
|
||||
|
||||
nghttp2_option *option;
|
||||
rv = nghttp2_option_new(&option);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto opt_del = defer(nghttp2_option_del, option);
|
||||
|
||||
nghttp2_option_set_recv_client_preface(option, 1);
|
||||
|
||||
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
|
||||
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream *http2_handler::create_stream(int32_t stream_id) {
|
||||
auto p = streams_.emplace(stream_id, make_unique<stream>(this, stream_id));
|
||||
assert(p.second);
|
||||
return (*p.first).second.get();
|
||||
}
|
||||
|
||||
void http2_handler::close_stream(int32_t stream_id) {
|
||||
streams_.erase(stream_id);
|
||||
}
|
||||
|
||||
stream *http2_handler::find_stream(int32_t stream_id) {
|
||||
auto i = streams_.find(stream_id);
|
||||
if (i == std::end(streams_)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return (*i).second.get();
|
||||
}
|
||||
|
||||
void http2_handler::call_on_request(stream &strm) {
|
||||
auto cb = mux_.handler(strm.request().impl());
|
||||
cb(strm.request(), strm.response());
|
||||
}
|
||||
|
||||
bool http2_handler::should_stop() const {
|
||||
return !nghttp2_session_want_read(session_) &&
|
||||
!nghttp2_session_want_write(session_);
|
||||
}
|
||||
|
||||
int http2_handler::start_response(stream &strm) {
|
||||
int rv;
|
||||
|
||||
auto &res = strm.response().impl();
|
||||
auto &header = res.header();
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(2 + header.size());
|
||||
auto status = util::utos(res.status_code());
|
||||
auto date = cached_date;
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
|
||||
nva.push_back(nghttp2::http2::make_nv_ls("date", *date));
|
||||
for (auto &hd : header) {
|
||||
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
|
||||
hd.second.sensitive));
|
||||
}
|
||||
|
||||
nghttp2_data_provider prd;
|
||||
prd.source.ptr = &strm;
|
||||
prd.read_callback =
|
||||
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
|
||||
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
|
||||
void *user_data) -> ssize_t {
|
||||
auto &strm = *static_cast<stream *>(source->ptr);
|
||||
return strm.response().impl().call_read(buf, length, data_flags);
|
||||
};
|
||||
|
||||
rv = nghttp2_submit_response(session_, strm.get_stream_id(), nva.data(),
|
||||
nva.size(), &prd);
|
||||
|
||||
if (rv != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
signal_write();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void http2_handler::enter_callback() {
|
||||
assert(!inside_callback_);
|
||||
inside_callback_ = true;
|
||||
}
|
||||
|
||||
void http2_handler::leave_callback() {
|
||||
assert(inside_callback_);
|
||||
inside_callback_ = false;
|
||||
}
|
||||
|
||||
void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
|
||||
::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
|
||||
signal_write();
|
||||
}
|
||||
|
||||
void http2_handler::signal_write() {
|
||||
if (!inside_callback_) {
|
||||
initiate_write();
|
||||
}
|
||||
}
|
||||
|
||||
void http2_handler::initiate_write() { writefun_(); }
|
||||
|
||||
void http2_handler::resume(stream &strm) {
|
||||
nghttp2_session_resume_data(session_, strm.get_stream_id());
|
||||
signal_write();
|
||||
}
|
||||
|
||||
response *http2_handler::push_promise(boost::system::error_code &ec,
|
||||
stream &strm, std::string method,
|
||||
std::string raw_path_query,
|
||||
header_map h) {
|
||||
int rv;
|
||||
|
||||
ec.clear();
|
||||
|
||||
auto &req = strm.request().impl();
|
||||
|
||||
auto nva = std::vector<nghttp2_nv>();
|
||||
nva.reserve(4 + h.size());
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.uri().scheme));
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.uri().host));
|
||||
nva.push_back(nghttp2::http2::make_nv_ls(":path", raw_path_query));
|
||||
|
||||
for (auto &hd : h) {
|
||||
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
|
||||
hd.second.sensitive));
|
||||
}
|
||||
|
||||
rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
|
||||
strm.get_stream_id(), nva.data(), nva.size(),
|
||||
nullptr);
|
||||
|
||||
if (rv < 0) {
|
||||
ec = make_error_code(static_cast<nghttp2_error>(rv));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto promised_strm = create_stream(rv);
|
||||
auto &promised_req = promised_strm->request().impl();
|
||||
promised_req.header(std::move(h));
|
||||
promised_req.method(std::move(method));
|
||||
|
||||
auto &uref = promised_req.uri();
|
||||
uref.scheme = req.uri().scheme;
|
||||
uref.host = req.uri().host;
|
||||
split_path(uref, std::begin(raw_path_query), std::end(raw_path_query));
|
||||
|
||||
auto &promised_res = promised_strm->response().impl();
|
||||
promised_res.pushed(true);
|
||||
|
||||
signal_write();
|
||||
|
||||
return &promised_strm->response();
|
||||
}
|
||||
|
||||
boost::asio::io_service &http2_handler::io_service() { return io_service_; }
|
||||
|
||||
callback_guard::callback_guard(http2_handler &h) : handler(h) {
|
||||
handler.enter_callback();
|
||||
}
|
||||
|
||||
callback_guard::~callback_guard() { handler.leave_callback(); }
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef ASIO_SERVER_HTTP2_HANDLER_H
|
||||
#define ASIO_SERVER_HTTP2_HANDLER_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
#include <boost/array.hpp>
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
class http2_handler;
|
||||
class stream;
|
||||
class serve_mux;
|
||||
|
||||
struct callback_guard {
|
||||
callback_guard(http2_handler &h);
|
||||
~callback_guard();
|
||||
http2_handler &handler;
|
||||
};
|
||||
|
||||
using connection_write = std::function<void(void)>;
|
||||
|
||||
class http2_handler : public std::enable_shared_from_this<http2_handler> {
|
||||
public:
|
||||
http2_handler(boost::asio::io_service &io_service, connection_write writefun,
|
||||
serve_mux &mux);
|
||||
|
||||
~http2_handler();
|
||||
|
||||
int start();
|
||||
|
||||
stream *create_stream(int32_t stream_id);
|
||||
void close_stream(int32_t stream_id);
|
||||
stream *find_stream(int32_t stream_id);
|
||||
|
||||
void call_on_request(stream &s);
|
||||
|
||||
bool should_stop() const;
|
||||
|
||||
int start_response(stream &s);
|
||||
|
||||
void stream_error(int32_t stream_id, uint32_t error_code);
|
||||
|
||||
void initiate_write();
|
||||
|
||||
void enter_callback();
|
||||
void leave_callback();
|
||||
|
||||
void resume(stream &s);
|
||||
|
||||
response *push_promise(boost::system::error_code &ec, stream &s,
|
||||
std::string method, std::string raw_path_query,
|
||||
header_map h);
|
||||
|
||||
void signal_write();
|
||||
|
||||
boost::asio::io_service &io_service();
|
||||
|
||||
template <size_t N>
|
||||
int on_read(const boost::array<uint8_t, N> &buffer, std::size_t len) {
|
||||
callback_guard cg(*this);
|
||||
|
||||
int rv;
|
||||
|
||||
rv = nghttp2_session_mem_recv(session_, buffer.data(), len);
|
||||
|
||||
if (rv < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
int on_write(boost::array<uint8_t, N> &buffer, std::size_t &len) {
|
||||
callback_guard cg(*this);
|
||||
|
||||
len = 0;
|
||||
|
||||
if (buf_) {
|
||||
std::copy_n(buf_, buflen_, std::begin(buffer));
|
||||
|
||||
len += buflen_;
|
||||
|
||||
buf_ = nullptr;
|
||||
buflen_ = 0;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
const uint8_t *data;
|
||||
auto nread = nghttp2_session_mem_send(session_, &data);
|
||||
if (nread < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (nread == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (len + nread > buffer.size()) {
|
||||
buf_ = data;
|
||||
buflen_ = nread;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
std::copy_n(data, nread, std::begin(buffer) + len);
|
||||
|
||||
len += nread;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<int32_t, std::shared_ptr<stream>> streams_;
|
||||
connection_write writefun_;
|
||||
serve_mux &mux_;
|
||||
boost::asio::io_service &io_service_;
|
||||
nghttp2_session *session_;
|
||||
const uint8_t *buf_;
|
||||
std::size_t buflen_;
|
||||
bool inside_callback_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp
|
||||
|
||||
#endif // ASIO_SERVER_HTTP2_HANDLER_H
|
|
@ -22,14 +22,10 @@
|
|||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "asio_http2_impl.h"
|
||||
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include "asio_server_http2_impl.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
#include "asio_server.h"
|
||||
#include "util.h"
|
||||
#include "ssl.h"
|
||||
|
@ -41,28 +37,7 @@ namespace asio_http2 {
|
|||
|
||||
namespace server {
|
||||
|
||||
http2::http2() : impl_(make_unique<http2_impl>()) {}
|
||||
|
||||
http2::~http2() {}
|
||||
|
||||
void http2::listen(const std::string &address, uint16_t port, request_cb cb) {
|
||||
impl_->listen(address, port, std::move(cb));
|
||||
}
|
||||
|
||||
void http2::num_threads(size_t num_threads) { impl_->num_threads(num_threads); }
|
||||
|
||||
void http2::tls(std::string private_key_file, std::string 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);
|
||||
}
|
||||
|
||||
void http2::backlog(int backlog) { impl_->backlog(backlog); }
|
||||
|
||||
http2_impl::http2_impl()
|
||||
: num_threads_(1), num_concurrent_tasks_(1), backlog_(-1) {}
|
||||
http2_impl::http2_impl() : num_threads_(1), backlog_(-1) {}
|
||||
|
||||
namespace {
|
||||
std::vector<unsigned char> &get_alpn_token() {
|
||||
|
@ -71,8 +46,7 @@ std::vector<unsigned char> &get_alpn_token() {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
void http2_impl::listen(const std::string &address, uint16_t port,
|
||||
request_cb cb) {
|
||||
void http2_impl::listen_and_serve(const std::string &address, uint16_t port) {
|
||||
std::unique_ptr<boost::asio::ssl::context> ssl_ctx;
|
||||
|
||||
if (!private_key_file_.empty() && !certificate_file_.empty()) {
|
||||
|
@ -92,7 +66,6 @@ void http2_impl::listen(const std::string &address, uint16_t port,
|
|||
SSL_OP_CIPHER_SERVER_PREFERENCE);
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS);
|
||||
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||
|
||||
SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST);
|
||||
|
||||
|
@ -115,8 +88,7 @@ void http2_impl::listen(const std::string &address, uint16_t port,
|
|||
nullptr);
|
||||
}
|
||||
|
||||
server(address, port, num_threads_, num_concurrent_tasks_, std::move(cb),
|
||||
std::move(ssl_ctx), backlog_).run();
|
||||
server(address, port, num_threads_, mux_, std::move(ssl_ctx), backlog_).run();
|
||||
}
|
||||
|
||||
void http2_impl::num_threads(size_t num_threads) { num_threads_ = num_threads; }
|
||||
|
@ -127,57 +99,14 @@ void http2_impl::tls(std::string private_key_file,
|
|||
certificate_file_ = std::move(certificate_file);
|
||||
}
|
||||
|
||||
void http2_impl::num_concurrent_tasks(size_t num_concurrent_tasks) {
|
||||
num_concurrent_tasks_ = num_concurrent_tasks;
|
||||
}
|
||||
|
||||
void http2_impl::backlog(int backlog) { backlog_ = backlog; }
|
||||
|
||||
bool http2_impl::handle(std::string pattern, request_cb cb) {
|
||||
return mux_.handle(std::move(pattern), std::move(cb));
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
|
||||
template <typename F, typename... T>
|
||||
std::shared_ptr<Defer<F, T...>> defer_shared(F &&f, T &&... t) {
|
||||
return std::make_shared<Defer<F, T...>>(std::forward<F>(f),
|
||||
std::forward<T>(t)...);
|
||||
}
|
||||
|
||||
read_cb file_reader(const std::string &path) {
|
||||
auto fd = open(path.c_str(), O_RDONLY);
|
||||
if (fd == -1) {
|
||||
return read_cb();
|
||||
}
|
||||
|
||||
return file_reader_from_fd(fd);
|
||||
}
|
||||
|
||||
read_cb file_reader_from_fd(int fd) {
|
||||
auto d = defer_shared(close, fd);
|
||||
|
||||
return [fd, d](uint8_t *buf, size_t len) -> read_cb::result_type {
|
||||
int rv;
|
||||
while ((rv = read(fd, buf, len)) == -1 && errno == EINTR)
|
||||
;
|
||||
|
||||
if (rv == -1) {
|
||||
return std::make_pair(-1, false);
|
||||
}
|
||||
|
||||
if (rv == 0) {
|
||||
return std::make_pair(rv, true);
|
||||
}
|
||||
|
||||
return std::make_pair(rv, false);
|
||||
};
|
||||
}
|
||||
|
||||
bool check_path(const std::string &path) { return util::check_path(path); }
|
||||
|
||||
std::string percent_decode(const std::string &s) {
|
||||
return util::percentDecode(std::begin(s), std::end(s));
|
||||
}
|
||||
|
||||
std::string http_date(int64_t t) { return util::http_date(t); }
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
|
@ -22,12 +22,14 @@
|
|||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef ASIO_HTTP2_IMPL_H
|
||||
#define ASIO_HTTP2_IMPL_H
|
||||
#ifndef ASIO_SERVER_HTTP2_IMPL_H
|
||||
#define ASIO_SERVER_HTTP2_IMPL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
#include "asio_server_serve_mux.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
|
@ -40,19 +42,19 @@ class server;
|
|||
class http2_impl {
|
||||
public:
|
||||
http2_impl();
|
||||
void listen(const std::string &address, uint16_t port, request_cb cb);
|
||||
void listen_and_serve(const std::string &address, uint16_t port);
|
||||
void num_threads(size_t num_threads);
|
||||
void tls(std::string private_key_file, std::string certificate_file);
|
||||
void num_concurrent_tasks(size_t num_concurrent_tasks);
|
||||
void backlog(int backlog);
|
||||
bool handle(std::string pattern, request_cb cb);
|
||||
|
||||
private:
|
||||
std::string private_key_file_;
|
||||
std::string certificate_file_;
|
||||
std::unique_ptr<server> server_;
|
||||
std::size_t num_threads_;
|
||||
std::size_t num_concurrent_tasks_;
|
||||
int backlog_;
|
||||
serve_mux mux_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
|
@ -61,4 +63,4 @@ private:
|
|||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_HTTP2_IMPL_H
|
||||
#endif // ASIO_SERVER_HTTP2_IMPL_H
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
#include "asio_server_request_impl.h"
|
||||
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
request::request() : impl_(make_unique<request_impl>()) {}
|
||||
|
||||
request::~request() {}
|
||||
|
||||
const header_map &request::header() const { return impl_->header(); }
|
||||
|
||||
const std::string &request::method() const { return impl_->method(); }
|
||||
|
||||
const uri_ref &request::uri() const { return impl_->uri(); }
|
||||
|
||||
void request::on_data(data_cb cb) const {
|
||||
return impl_->on_data(std::move(cb));
|
||||
}
|
||||
|
||||
request_impl &request::impl() const { return *impl_; }
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_server_request_handler.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "http2.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
namespace {
|
||||
std::string create_html(int status_code) {
|
||||
std::string res;
|
||||
res.reserve(512);
|
||||
auto status = ::nghttp2::http2::get_status_string(status_code);
|
||||
res += R"(<!DOCTYPE html><html lang="en"><title>)";
|
||||
res += status;
|
||||
res += "</title><body><h1>";
|
||||
res += status;
|
||||
res += "</h1></body></html>";
|
||||
return res;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
request_cb redirect_handler(int status_code, std::string uri) {
|
||||
return [status_code, uri](const request &req, const response &res) {
|
||||
header_map h;
|
||||
h.emplace("location", header_value{std::move(uri)});
|
||||
std::string html;
|
||||
if (req.method() == "GET") {
|
||||
html = create_html(status_code);
|
||||
}
|
||||
h.emplace("content-length", header_value{util::utos(html.size())});
|
||||
|
||||
res.write_head(status_code, std::move(h));
|
||||
res.end(std::move(html));
|
||||
};
|
||||
}
|
||||
|
||||
request_cb status_handler(int status_code) {
|
||||
return [status_code](const request &req, const response &res) {
|
||||
auto html = create_html(status_code);
|
||||
header_map h;
|
||||
h.emplace("content-length", header_value{util::utos(html.size())});
|
||||
h.emplace("content-type", header_value{"text/html; charset=utf-8"});
|
||||
|
||||
res.write_head(status_code, std::move(h));
|
||||
res.end(std::move(html));
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_SERVER_REQUEST_HANDLER_H
|
||||
#define ASIO_SERVER_REQUEST_HANDLER_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
#endif // ASIO_SERVER_REQUEST_HANDLER_H
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_server_request_impl.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
request_impl::request_impl() : strm_(nullptr) {}
|
||||
|
||||
const header_map &request_impl::header() const { return header_; }
|
||||
|
||||
const std::string &request_impl::method() const { return method_; }
|
||||
|
||||
const uri_ref &request_impl::uri() const { return uri_; }
|
||||
|
||||
uri_ref &request_impl::uri() { return uri_; }
|
||||
|
||||
void request_impl::header(header_map h) { header_ = std::move(h); }
|
||||
|
||||
header_map &request_impl::header() { return header_; }
|
||||
|
||||
void request_impl::method(std::string arg) { method_ = std::move(arg); }
|
||||
|
||||
void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); }
|
||||
|
||||
void request_impl::stream(class stream *s) { strm_ = s; }
|
||||
|
||||
void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
|
||||
if (on_data_cb_) {
|
||||
on_data_cb_(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_SERVER_REQUEST_IMPL_H
|
||||
#define ASIO_SERVER_REQUEST_IMPL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
class stream;
|
||||
|
||||
class request_impl {
|
||||
public:
|
||||
request_impl();
|
||||
|
||||
void header(header_map h);
|
||||
const header_map &header() const;
|
||||
header_map &header();
|
||||
|
||||
void method(std::string method);
|
||||
const std::string &method() const;
|
||||
|
||||
const uri_ref &uri() const;
|
||||
uri_ref &uri();
|
||||
|
||||
void on_data(data_cb cb);
|
||||
|
||||
void stream(class stream *s);
|
||||
void call_on_data(const uint8_t *data, std::size_t len);
|
||||
|
||||
private:
|
||||
class stream *strm_;
|
||||
header_map header_;
|
||||
std::string method_;
|
||||
uri_ref uri_;
|
||||
data_cb on_data_cb_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_SERVER_REQUEST_IMPL_H
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
#include "asio_server_response_impl.h"
|
||||
|
||||
#include "template.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
response::response() : impl_(make_unique<response_impl>()) {}
|
||||
|
||||
response::~response() {}
|
||||
|
||||
void response::write_head(unsigned int status_code, header_map h) const {
|
||||
impl_->write_head(status_code, std::move(h));
|
||||
}
|
||||
|
||||
void response::end(std::string data) const { impl_->end(std::move(data)); }
|
||||
|
||||
void response::end(generator_cb cb) const { impl_->end(std::move(cb)); }
|
||||
|
||||
void response::on_close(close_cb cb) const { impl_->on_close(std::move(cb)); }
|
||||
|
||||
void response::cancel(uint32_t error_code) const { impl_->cancel(error_code); }
|
||||
|
||||
const response *response::push(boost::system::error_code &ec,
|
||||
std::string method, std::string path,
|
||||
header_map h) const {
|
||||
return impl_->push(ec, std::move(method), std::move(path), std::move(h));
|
||||
}
|
||||
|
||||
void response::resume() const { impl_->resume(); }
|
||||
|
||||
unsigned int response::status_code() const { return impl_->status_code(); }
|
||||
|
||||
boost::asio::io_service &response::io_service() const {
|
||||
return impl_->io_service();
|
||||
}
|
||||
|
||||
response_impl &response::impl() const { return *impl_; }
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_server_response_impl.h"
|
||||
|
||||
#include "asio_server_stream.h"
|
||||
#include "asio_server_request_impl.h"
|
||||
#include "asio_server_http2_handler.h"
|
||||
#include "asio_common.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
response_impl::response_impl()
|
||||
: strm_(nullptr), generator_cb_(deferred_generator()), status_code_(200),
|
||||
state_(response_state::INITIAL), pushed_(false),
|
||||
push_promise_sent_(false) {}
|
||||
|
||||
unsigned int response_impl::status_code() const { return status_code_; }
|
||||
|
||||
void response_impl::write_head(unsigned int status_code, header_map h) {
|
||||
if (state_ != response_state::INITIAL) {
|
||||
return;
|
||||
}
|
||||
|
||||
status_code_ = status_code;
|
||||
header_ = std::move(h);
|
||||
|
||||
state_ = response_state::HEADER_DONE;
|
||||
|
||||
if (pushed_ && !push_promise_sent_) {
|
||||
return;
|
||||
}
|
||||
|
||||
start_response();
|
||||
}
|
||||
|
||||
void response_impl::end(std::string data) {
|
||||
end(string_generator(std::move(data)));
|
||||
}
|
||||
|
||||
void response_impl::end(generator_cb cb) {
|
||||
if (state_ == response_state::BODY_STARTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
generator_cb_ = std::move(cb);
|
||||
|
||||
if (state_ == response_state::INITIAL) {
|
||||
write_head(status_code_);
|
||||
} else {
|
||||
// generator_cb is changed, start writing in case it is deferred.
|
||||
auto handler = strm_->handler();
|
||||
handler->resume(*strm_);
|
||||
}
|
||||
|
||||
state_ = response_state::BODY_STARTED;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool expect_response_body(const std::string &method, int status_code) {
|
||||
return method != "HEAD" && status_code / 100 != 1 && status_code != 304 &&
|
||||
status_code != 204;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void response_impl::start_response() {
|
||||
auto handler = strm_->handler();
|
||||
|
||||
auto &req = strm_->request().impl();
|
||||
|
||||
// if response body is not expected, nullify it so that HEADERS has
|
||||
// END_STREAM flag set.
|
||||
if (!expect_response_body(req.method(), status_code_)) {
|
||||
state_ = response_state::BODY_STARTED;
|
||||
generator_cb_ = generator_cb();
|
||||
}
|
||||
|
||||
if (handler->start_response(*strm_) != 0) {
|
||||
handler->stream_error(strm_->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void response_impl::on_close(close_cb cb) { close_cb_ = std::move(cb); }
|
||||
|
||||
void response_impl::call_on_close(uint32_t error_code) {
|
||||
if (close_cb_) {
|
||||
close_cb_(error_code);
|
||||
}
|
||||
}
|
||||
|
||||
void response_impl::cancel(uint32_t error_code) {
|
||||
auto handler = strm_->handler();
|
||||
handler->stream_error(strm_->get_stream_id(), error_code);
|
||||
}
|
||||
|
||||
response *response_impl::push(boost::system::error_code &ec, std::string method,
|
||||
std::string raw_path_query, header_map h) const {
|
||||
auto handler = strm_->handler();
|
||||
return handler->push_promise(ec, *strm_, std::move(method),
|
||||
std::move(raw_path_query), std::move(h));
|
||||
}
|
||||
|
||||
void response_impl::resume() {
|
||||
auto handler = strm_->handler();
|
||||
handler->resume(*strm_);
|
||||
}
|
||||
|
||||
boost::asio::io_service &response_impl::io_service() {
|
||||
return strm_->handler()->io_service();
|
||||
}
|
||||
|
||||
void response_impl::pushed(bool f) { pushed_ = f; }
|
||||
|
||||
void response_impl::push_promise_sent() {
|
||||
if (push_promise_sent_) {
|
||||
return;
|
||||
}
|
||||
push_promise_sent_ = true;
|
||||
if (state_ == response_state::INITIAL) {
|
||||
return;
|
||||
}
|
||||
start_response();
|
||||
}
|
||||
|
||||
const header_map &response_impl::header() const { return header_; }
|
||||
|
||||
void response_impl::stream(class stream *s) { strm_ = s; }
|
||||
|
||||
generator_cb::result_type
|
||||
response_impl::call_read(uint8_t *data, std::size_t len, uint32_t *data_flags) {
|
||||
if (generator_cb_) {
|
||||
return generator_cb_(data, len, data_flags);
|
||||
}
|
||||
|
||||
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_SERVER_RESPONSE_IMPL_H
|
||||
#define ASIO_SERVER_RESPONSE_IMPL_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
class stream;
|
||||
|
||||
enum class response_state {
|
||||
INITIAL,
|
||||
// response_impl::write_head() was called
|
||||
HEADER_DONE,
|
||||
// response_impl::end() was called
|
||||
BODY_STARTED,
|
||||
};
|
||||
|
||||
class response_impl {
|
||||
public:
|
||||
response_impl();
|
||||
void write_head(unsigned int status_code, header_map h = {});
|
||||
void end(std::string data = "");
|
||||
void end(generator_cb cb);
|
||||
void on_close(close_cb cb);
|
||||
void resume();
|
||||
|
||||
void cancel(uint32_t error_code);
|
||||
|
||||
response *push(boost::system::error_code &ec, std::string method,
|
||||
std::string raw_path_query, header_map h = {}) const;
|
||||
|
||||
boost::asio::io_service &io_service();
|
||||
|
||||
void start_response();
|
||||
|
||||
unsigned int status_code() const;
|
||||
const header_map &header() const;
|
||||
void pushed(bool f);
|
||||
void push_promise_sent();
|
||||
void stream(class stream *s);
|
||||
generator_cb::result_type call_read(uint8_t *data, std::size_t len,
|
||||
uint32_t *data_flags);
|
||||
void call_on_close(uint32_t error_code);
|
||||
|
||||
private:
|
||||
class stream *strm_;
|
||||
header_map header_;
|
||||
generator_cb generator_cb_;
|
||||
close_cb close_cb_;
|
||||
unsigned int status_code_;
|
||||
response_state state_;
|
||||
// true if this is pushed stream's response
|
||||
bool pushed_;
|
||||
// true if PUSH_PROMISE is sent if this is response of a pushed
|
||||
// stream
|
||||
bool push_promise_sent_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_SERVER_RESPONSE_IMPL_H
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_server_serve_mux.h"
|
||||
|
||||
#include "asio_server_request_impl.h"
|
||||
#include "asio_server_request_handler.h"
|
||||
#include "util.h"
|
||||
#include "http2.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
namespace server {
|
||||
|
||||
bool serve_mux::handle(std::string pattern, request_cb cb) {
|
||||
if (pattern.empty() || !cb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto it = mux_.find(pattern);
|
||||
if (it != std::end(mux_) && (*it).second.user_defined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if pattern ends with '/' (e.g., /foo/), add implicit permanent
|
||||
// redirect for '/foo'.
|
||||
if (pattern.size() >= 2 && pattern.back() == '/') {
|
||||
auto redirect_pattern = pattern.substr(0, pattern.size() - 1);
|
||||
auto it = mux_.find(redirect_pattern);
|
||||
if (it == std::end(mux_) || !(*it).second.user_defined) {
|
||||
std::string path;
|
||||
if (pattern[0] == '/') {
|
||||
path = pattern;
|
||||
} else {
|
||||
// skip host part
|
||||
path = pattern.substr(pattern.find('/'));
|
||||
}
|
||||
if (it == std::end(mux_)) {
|
||||
mux_.emplace(std::move(redirect_pattern),
|
||||
handler_entry{false,
|
||||
redirect_handler(301, std::move(path)),
|
||||
pattern});
|
||||
} else {
|
||||
(*it).second = handler_entry{
|
||||
false, redirect_handler(301, std::move(path)), pattern};
|
||||
}
|
||||
}
|
||||
}
|
||||
mux_.emplace(pattern, handler_entry{true, std::move(cb), pattern});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
request_cb serve_mux::handler(request_impl &req) const {
|
||||
auto &path = req.uri().path;
|
||||
if (req.method() != "CONNECT") {
|
||||
auto clean_path = ::nghttp2::http2::path_join(
|
||||
nullptr, 0, nullptr, 0, path.c_str(), path.size(), nullptr, 0);
|
||||
if (clean_path != path) {
|
||||
auto new_uri = util::percent_encode_path(clean_path);
|
||||
auto &uref = req.uri();
|
||||
if (!uref.raw_query.empty()) {
|
||||
new_uri += "?";
|
||||
new_uri += uref.raw_query;
|
||||
}
|
||||
|
||||
return redirect_handler(301, std::move(new_uri));
|
||||
}
|
||||
}
|
||||
auto &host = req.uri().host;
|
||||
|
||||
auto cb = match(host + path);
|
||||
if (cb) {
|
||||
return cb;
|
||||
}
|
||||
cb = match(path);
|
||||
if (cb) {
|
||||
return cb;
|
||||
}
|
||||
return status_handler(404);
|
||||
}
|
||||
|
||||
request_cb serve_mux::match(const std::string &path) const {
|
||||
const handler_entry *ent = nullptr;
|
||||
size_t best = 0;
|
||||
for (auto &kv : mux_) {
|
||||
auto &pattern = kv.first;
|
||||
if (!util::startsWith(path, pattern)) {
|
||||
continue;
|
||||
}
|
||||
if (path.size() == pattern.size() || pattern.back() == '/' ||
|
||||
path[pattern.size()] == '/') {
|
||||
if (!ent || best < pattern.size()) {
|
||||
best = pattern.size();
|
||||
ent = &kv.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ent) {
|
||||
return ent->cb;
|
||||
}
|
||||
return request_cb();
|
||||
}
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_SERVER_SERVE_MUX_H
|
||||
#define ASIO_SERVER_SERVE_MUX_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
namespace server {
|
||||
|
||||
class request_impl;
|
||||
|
||||
// port from go's ServeMux
|
||||
|
||||
struct handler_entry {
|
||||
bool user_defined;
|
||||
request_cb cb;
|
||||
std::string pattern;
|
||||
};
|
||||
|
||||
class serve_mux {
|
||||
public:
|
||||
bool handle(std::string pattern, request_cb cb);
|
||||
request_cb handler(request_impl &req) const;
|
||||
request_cb match(const std::string &path) const;
|
||||
|
||||
private:
|
||||
std::map<std::string, handler_entry> mux_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_SERVER_SERVE_MUX_H
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#include "asio_server_stream.h"
|
||||
|
||||
#include "asio_server_http2_handler.h"
|
||||
#include "asio_server_request_impl.h"
|
||||
#include "asio_server_response_impl.h"
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
stream::stream(http2_handler *h, int32_t stream_id)
|
||||
: handler_(h), stream_id_(stream_id) {
|
||||
request_.impl().stream(this);
|
||||
response_.impl().stream(this);
|
||||
}
|
||||
|
||||
int32_t stream::get_stream_id() const { return stream_id_; }
|
||||
|
||||
class request &stream::request() {
|
||||
return request_;
|
||||
}
|
||||
|
||||
class response &stream::response() {
|
||||
return response_;
|
||||
}
|
||||
|
||||
http2_handler *stream::handler() const { return handler_; }
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_SERVER_STREAM_H
|
||||
#define ASIO_SERVER_STREAM_H
|
||||
|
||||
#include "nghttp2_config.h"
|
||||
|
||||
#include <nghttp2/asio_http2_server.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
namespace asio_http2 {
|
||||
namespace server {
|
||||
|
||||
class http2_handler;
|
||||
|
||||
class stream {
|
||||
public:
|
||||
stream(http2_handler *h, int32_t stream_id);
|
||||
|
||||
int32_t get_stream_id() const;
|
||||
class request &request();
|
||||
class response &response();
|
||||
|
||||
http2_handler *handler() const;
|
||||
|
||||
private:
|
||||
http2_handler *handler_;
|
||||
class request request_;
|
||||
class response response_;
|
||||
int32_t stream_id_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
} // namespace asio_http2
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_SERVER_STREAM_H
|
|
@ -20,4 +20,5 @@
|
|||
# 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.
|
||||
nobase_include_HEADERS = nghttp2/asio_http2.h
|
||||
nobase_include_HEADERS = nghttp2/asio_http2.h nghttp2/asio_http2_client.h \
|
||||
nghttp2/asio_http2_server.h
|
||||
|
|
|
@ -30,205 +30,86 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
#include <nghttp2/nghttp2.h>
|
||||
|
||||
namespace boost {
|
||||
namespace system {
|
||||
|
||||
template <> struct is_error_code_enum<nghttp2_error> {
|
||||
BOOST_STATIC_CONSTANT(bool, value = true);
|
||||
};
|
||||
|
||||
} // namespace system
|
||||
} // namespace boost
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
struct header {
|
||||
std::string name;
|
||||
struct header_value {
|
||||
// header field value
|
||||
std::string value;
|
||||
// true if the header field value is sensitive information, such as
|
||||
// authorization information or short length secret cookies. If
|
||||
// true, those header fields are not indexed by HPACK (but still
|
||||
// huffman-encoded), which results in lesser compression.
|
||||
bool sensitive;
|
||||
};
|
||||
|
||||
// header fields. The header field name must be lower-cased.
|
||||
using header_map = std::multimap<std::string, header_value>;
|
||||
|
||||
const boost::system::error_category &nghttp2_category() noexcept;
|
||||
|
||||
struct uri_ref {
|
||||
std::string scheme;
|
||||
std::string host;
|
||||
// form after percent-encoding decoded
|
||||
std::string path;
|
||||
// original path, percent-encoded
|
||||
std::string raw_path;
|
||||
// original query, percent-encoded
|
||||
std::string raw_query;
|
||||
std::string fragment;
|
||||
};
|
||||
|
||||
// Callback function when data is arrived. EOF is indicated by
|
||||
// passing 0 to the second parameter.
|
||||
typedef std::function<void(const uint8_t *, std::size_t)> data_cb;
|
||||
typedef std::function<void(void)> void_cb;
|
||||
typedef std::function<void(const boost::system::error_code &ec)> error_cb;
|
||||
// Callback function when request and response are finished. The
|
||||
// parameter indicates the cause of closure.
|
||||
typedef std::function<void(uint32_t)> close_cb;
|
||||
|
||||
// Callback function to generate response body. The implementation of
|
||||
// this callback must fill at most |len| bytes data to |buf|. The
|
||||
// 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
|
||||
// reached, return true. If there is error and application wants to
|
||||
// terminate stream, return std::make_pair(-1, false). Returning
|
||||
// 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>(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;
|
||||
|
||||
namespace server {
|
||||
|
||||
class request_impl;
|
||||
class response_impl;
|
||||
|
||||
class request {
|
||||
public:
|
||||
// Application must not call this directly.
|
||||
request();
|
||||
|
||||
// Returns request headers. The pusedo headers, which start with
|
||||
// colon (;), are exluced from this list.
|
||||
const std::vector<header> &headers() const;
|
||||
|
||||
// Returns method (e.g., GET).
|
||||
const std::string &method() const;
|
||||
|
||||
// Returns scheme (e.g., https).
|
||||
const std::string &scheme() const;
|
||||
|
||||
// Returns authority (e.g., example.org). This could be empty
|
||||
// string. In this case, check host().
|
||||
|
||||
const std::string &authority() const;
|
||||
// Returns host (e.g., example.org). If host header field is not
|
||||
// present, this value is copied from authority().
|
||||
|
||||
const std::string &host() const;
|
||||
|
||||
// Returns path (e.g., /index.html).
|
||||
const std::string &path() const;
|
||||
|
||||
// Sets callback when chunk of request body is received.
|
||||
void on_data(data_cb cb);
|
||||
|
||||
// Sets callback when request was completed.
|
||||
void on_end(void_cb cb);
|
||||
|
||||
// Pushes resource denoted by |path| using |method|. The additional
|
||||
// headers can be given in |headers|. request_cb will be called for
|
||||
// pushed resource later on. This function returns true if it
|
||||
// succeeds, or false.
|
||||
bool push(std::string method, std::string path,
|
||||
std::vector<header> headers = {});
|
||||
|
||||
// Returns true if this is pushed request.
|
||||
bool pushed() const;
|
||||
|
||||
// Returns true if stream has been closed.
|
||||
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.
|
||||
request_impl &impl();
|
||||
|
||||
private:
|
||||
std::unique_ptr<request_impl> impl_;
|
||||
};
|
||||
|
||||
class response {
|
||||
public:
|
||||
// Application must not call this directly.
|
||||
response();
|
||||
|
||||
// Write response header using |status_code| (e.g., 200) and
|
||||
// additional headers in |headers|.
|
||||
void write_head(unsigned int status_code, std::vector<header> headers = {});
|
||||
|
||||
// Sends |data| as request body. No further call of end() is
|
||||
// allowed.
|
||||
void end(std::string data = "");
|
||||
|
||||
// Sets callback |cb| as a generator of the response body. No
|
||||
// further call of end() is allowed.
|
||||
void end(read_cb cb);
|
||||
|
||||
// Resumes deferred response.
|
||||
void resume();
|
||||
|
||||
// Returns status code.
|
||||
unsigned int status_code() const;
|
||||
|
||||
// Returns true if response has been started.
|
||||
bool started() const;
|
||||
|
||||
// Application must not call this directly.
|
||||
response_impl &impl();
|
||||
|
||||
private:
|
||||
std::unique_ptr<response_impl> impl_;
|
||||
};
|
||||
|
||||
// This is so called request callback. Called every time request is
|
||||
// received.
|
||||
typedef std::function<void(const std::shared_ptr<request> &,
|
||||
const std::shared_ptr<response> &)> request_cb;
|
||||
|
||||
class http2_impl;
|
||||
|
||||
class http2 {
|
||||
public:
|
||||
http2();
|
||||
~http2();
|
||||
|
||||
// Starts listening connection on given address and port. The
|
||||
// incoming requests are handled by given callback |cb|.
|
||||
void listen(const std::string &address, uint16_t port, request_cb cb);
|
||||
|
||||
// Sets number of native threads to handle incoming HTTP request.
|
||||
// It defaults to 1.
|
||||
void num_threads(size_t num_threads);
|
||||
|
||||
// Sets TLS private key file and certificate file. Both files must
|
||||
// be in PEM format.
|
||||
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);
|
||||
|
||||
// Sets the maximum length to which the queue of pending
|
||||
// connections.
|
||||
void backlog(int backlog);
|
||||
|
||||
private:
|
||||
std::unique_ptr<http2_impl> impl_;
|
||||
};
|
||||
|
||||
} // namespace server
|
||||
// Callback function to generate response body. This function has the
|
||||
// same semantics with nghttp2_data_source_read_callback. Just source
|
||||
// and user_data parameters are removed.
|
||||
//
|
||||
// Basically, write at most |len| bytes to |data| and returns the
|
||||
// number of bytes written. If there is no data left to send, set
|
||||
// NGHTTP2_DATA_FLAG_EOF to *data_flags (e.g., *data_flags |=
|
||||
// NGHTTP2_DATA_FLAG_EOF). If there is still data to send but they
|
||||
// are not available right now, return NGHTTP2_ERR_DEFERRED. In case
|
||||
// of the error and request/response must be closed, return
|
||||
// NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE.
|
||||
typedef std::function<
|
||||
ssize_t(uint8_t *buf, std::size_t len, uint32_t *data_flags)> generator_cb;
|
||||
|
||||
// Convenient function to create function to read file denoted by
|
||||
// |path|. This can be passed to response::end().
|
||||
read_cb file_reader(const std::string &path);
|
||||
generator_cb file_generator(const std::string &path);
|
||||
|
||||
// Like file_reader(const std::string&), but it takes opened file
|
||||
// Like file_generator(const std::string&), but it takes opened file
|
||||
// descriptor. The passed descriptor will be closed when returned
|
||||
// function object is destroyed.
|
||||
read_cb file_reader_from_fd(int fd);
|
||||
generator_cb file_generator_from_fd(int fd);
|
||||
|
||||
// Validates path so that it does not contain directory traversal
|
||||
// vector. Returns true if path is safe. The |path| must start with
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_HTTP2_CLIENT_H
|
||||
#define ASIO_HTTP2_CLIENT_H
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
namespace client {
|
||||
|
||||
class response_impl;
|
||||
|
||||
class response {
|
||||
public:
|
||||
// Application must not call this directly.
|
||||
response();
|
||||
~response();
|
||||
|
||||
// Sets callback which is invoked when chunk of response body is
|
||||
// received.
|
||||
void on_data(data_cb cb) const;
|
||||
|
||||
// Returns status code.
|
||||
int status_code() const;
|
||||
|
||||
// Returns content-length. -1 if it is unknown.
|
||||
int64_t content_length() const;
|
||||
|
||||
// Returns the response header fields. The pusedo header fields,
|
||||
// which start with colon (:), are exluced from this list.
|
||||
const header_map &header() const;
|
||||
|
||||
// Application must not call this directly.
|
||||
response_impl &impl() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<response_impl> impl_;
|
||||
};
|
||||
|
||||
class request;
|
||||
|
||||
using response_cb = std::function<void(const response &)>;
|
||||
using request_cb = std::function<void(const request &)>;
|
||||
using connect_cb =
|
||||
std::function<void(boost::asio::ip::tcp::resolver::iterator)>;
|
||||
|
||||
class request_impl;
|
||||
|
||||
class request {
|
||||
public:
|
||||
// Application must not call this directly.
|
||||
request();
|
||||
~request();
|
||||
|
||||
// Sets callback which is invoked when response header is received.
|
||||
void on_response(response_cb cb) const;
|
||||
|
||||
// Sets callback which is invoked when push request header is
|
||||
// received.
|
||||
void on_push(request_cb cb) const;
|
||||
|
||||
// Sets callback which is invoked when this request and response are
|
||||
// finished. After the invocation of this callback, the application
|
||||
// must not access request and response object.
|
||||
void on_close(close_cb cb) const;
|
||||
|
||||
// Cancels this request and response with given error code.
|
||||
void cancel(uint32_t error_code = NGHTTP2_INTERNAL_ERROR) const;
|
||||
|
||||
// Resumes deferred uploading.
|
||||
void resume() const;
|
||||
|
||||
// Returns method (e.g., GET).
|
||||
const std::string &method() const;
|
||||
|
||||
// Returns request URI, split into components.
|
||||
const uri_ref &uri() const;
|
||||
|
||||
// Returns request header fields. The pusedo header fields, which
|
||||
// start with colon (:), are exluced from this list.
|
||||
const header_map &header() const;
|
||||
|
||||
// Application must not call this directly.
|
||||
request_impl &impl() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<request_impl> impl_;
|
||||
};
|
||||
|
||||
class session_impl;
|
||||
|
||||
class session {
|
||||
public:
|
||||
// Starts HTTP/2 session by connecting to |host| and |service|
|
||||
// (e.g., "80") using clear text TCP connection.
|
||||
session(boost::asio::io_service &io_service, const std::string &host,
|
||||
const std::string &service);
|
||||
|
||||
// Starts HTTP/2 session by connecting to |host| and |service|
|
||||
// (e.g., "443") using encrypted SSL/TLS connection.
|
||||
session(boost::asio::io_service &io_service,
|
||||
boost::asio::ssl::context &tls_context, const std::string &host,
|
||||
const std::string &service);
|
||||
~session();
|
||||
|
||||
// Sets callback which is invoked after connection is established.
|
||||
void on_connect(connect_cb cb) const;
|
||||
|
||||
// Sets callback which is invoked there is connection level error
|
||||
// and session is terminated.
|
||||
void on_error(error_cb cb) const;
|
||||
|
||||
// Shutdowns connection.
|
||||
void shutdown() const;
|
||||
|
||||
// Returns underlying io_service object.
|
||||
boost::asio::io_service &io_service() const;
|
||||
|
||||
// Submits request to server using |method| (e.g., "GET"), |uri|
|
||||
// (e.g., "http://localhost/") and optionally additional header
|
||||
// fields. This function returns pointer to request object if it
|
||||
// succeeds, or nullptr and |ec| contains error message.
|
||||
const request *submit(boost::system::error_code &ec,
|
||||
const std::string &method, const std::string &uri,
|
||||
header_map h = {}) const;
|
||||
|
||||
// Submits request to server using |method| (e.g., "GET"), |uri|
|
||||
// (e.g., "http://localhost/") and optionally additional header
|
||||
// fields. The |data| is request body. This function returns
|
||||
// pointer to request object if it succeeds, or nullptr and |ec|
|
||||
// contains error message.
|
||||
const request *submit(boost::system::error_code &ec,
|
||||
const std::string &method, const std::string &uri,
|
||||
std::string data, header_map h = {}) const;
|
||||
|
||||
// Submits request to server using |method| (e.g., "GET"), |uri|
|
||||
// (e.g., "http://localhost/") and optionally additional header
|
||||
// fields. The |cb| is used to generate request body. This
|
||||
// function returns pointer to request object if it succeeds, or
|
||||
// nullptr and |ec| contains error message.
|
||||
const request *submit(boost::system::error_code &ec,
|
||||
const std::string &method, const std::string &uri,
|
||||
generator_cb cb, header_map h = {}) const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<session_impl> impl_;
|
||||
};
|
||||
|
||||
// configure |tls_ctx| for client use. Currently, we just set NPN
|
||||
// callback for HTTP/2.
|
||||
void configure_tls_context(boost::asio::ssl::context &tls_ctx);
|
||||
|
||||
} // namespace client
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_HTTP2_CLIENT_H
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* nghttp2 - HTTP/2 C Library
|
||||
*
|
||||
* Copyright (c) 2015 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.
|
||||
*/
|
||||
#ifndef ASIO_HTTP2_SERVER_H
|
||||
#define ASIO_HTTP2_SERVER_H
|
||||
|
||||
#include <nghttp2/asio_http2.h>
|
||||
|
||||
namespace nghttp2 {
|
||||
|
||||
namespace asio_http2 {
|
||||
|
||||
namespace server {
|
||||
|
||||
class request_impl;
|
||||
class response_impl;
|
||||
|
||||
class request {
|
||||
public:
|
||||
// Application must not call this directly.
|
||||
request();
|
||||
~request();
|
||||
|
||||
// Returns request header fields. The pusedo header fields, which
|
||||
// start with colon (:), are exluced from this list.
|
||||
const header_map &header() const;
|
||||
|
||||
// Returns method (e.g., GET).
|
||||
const std::string &method() const;
|
||||
|
||||
// Returns request URI, split into components.
|
||||
const uri_ref &uri() const;
|
||||
|
||||
// Sets callback which is invoked when chunk of request body is
|
||||
// received.
|
||||
void on_data(data_cb cb) const;
|
||||
|
||||
// Application must not call this directly.
|
||||
request_impl &impl() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<request_impl> impl_;
|
||||
};
|
||||
|
||||
class response {
|
||||
public:
|
||||
// Application must not call this directly.
|
||||
response();
|
||||
~response();
|
||||
|
||||
// Write response header using |status_code| (e.g., 200) and
|
||||
// additional header fields in |h|.
|
||||
void write_head(unsigned int status_code, header_map h = {}) const;
|
||||
|
||||
// Sends |data| as request body. No further call of end() is
|
||||
// allowed.
|
||||
void end(std::string data = "") const;
|
||||
|
||||
// Sets callback as a generator of the response body. No further
|
||||
// call of end() is allowed.
|
||||
void end(generator_cb cb) const;
|
||||
|
||||
// Sets callback which is invoked when this request and response are
|
||||
// finished. After the invocation of this callback, the application
|
||||
// must not access request and response object.
|
||||
void on_close(close_cb cb) const;
|
||||
|
||||
// Cancels this request and response with given error code.
|
||||
void cancel(uint32_t error_code = NGHTTP2_INTERNAL_ERROR) const;
|
||||
|
||||
// Resumes deferred response.
|
||||
void resume() const;
|
||||
|
||||
// Pushes resource denoted by |raw_path_query| using |method|. The
|
||||
// additional header fields can be given in |h|. This function
|
||||
// returns pointer to response object for promised stream, otherwise
|
||||
// nullptr and error code is filled in |ec|. Be aware that the
|
||||
// header field name given in |h| must be lower-cased.
|
||||
const response *push(boost::system::error_code &ec, std::string method,
|
||||
std::string raw_path_query, header_map h = {}) const;
|
||||
|
||||
// Returns status code.
|
||||
unsigned int status_code() const;
|
||||
|
||||
// Returns boost::asio::io_service this response is running on.
|
||||
boost::asio::io_service &io_service() const;
|
||||
|
||||
// Application must not call this directly.
|
||||
response_impl &impl() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<response_impl> impl_;
|
||||
};
|
||||
|
||||
// This is so called request callback. Called every time request is
|
||||
// received. The life time of |request| and |response| objects end
|
||||
// when callback set by response::on_close() is called. After that,
|
||||
// the application must not access to those objects.
|
||||
typedef std::function<void(const request &, const response &)> request_cb;
|
||||
|
||||
class http2_impl;
|
||||
|
||||
class http2 {
|
||||
public:
|
||||
http2();
|
||||
~http2();
|
||||
|
||||
// Starts listening connection on given address and port and serves
|
||||
// incoming requests.
|
||||
void listen_and_serve(const std::string &address, uint16_t port);
|
||||
|
||||
// Registers request handler |cb| with path pattern |pattern|. This
|
||||
// function will fail and returns false if same pattern has been
|
||||
// already registered or |pattern| is empty string. Otherwise
|
||||
// returns true. The pattern match rule is the same as
|
||||
// net/http/ServeMux in golang.
|
||||
bool handle(std::string pattern, request_cb cb);
|
||||
|
||||
// Sets number of native threads to handle incoming HTTP request.
|
||||
// It defaults to 1.
|
||||
void num_threads(size_t num_threads);
|
||||
|
||||
// Sets TLS private key file and certificate file. Both files must
|
||||
// be in PEM format.
|
||||
void tls(std::string private_key_file, std::string certificate_file);
|
||||
|
||||
// Sets the maximum length to which the queue of pending
|
||||
// connections.
|
||||
void backlog(int backlog);
|
||||
|
||||
private:
|
||||
std::unique_ptr<http2_impl> impl_;
|
||||
};
|
||||
|
||||
// Returns request handler to do redirect to |uri| using
|
||||
// |status_code|. The |uri| appears in "location" header field as is.
|
||||
request_cb redirect_handler(int status_code, std::string uri);
|
||||
|
||||
// Returns request handler to reply with given |status_code| and HTML
|
||||
// including message about status code.
|
||||
request_cb status_handler(int status_code);
|
||||
|
||||
} // namespace server
|
||||
|
||||
} // namespace asio_http2
|
||||
|
||||
} // namespace nghttp2
|
||||
|
||||
#endif // ASIO_HTTP2_SERVER_H
|
|
@ -126,6 +126,8 @@ int main(int argc, char *argv[]) {
|
|||
!CU_add_test(pSuite, "util_to_token68", shrpx::test_util_to_token68) ||
|
||||
!CU_add_test(pSuite, "util_percent_encode_token",
|
||||
shrpx::test_util_percent_encode_token) ||
|
||||
!CU_add_test(pSuite, "util_percent_encode_path",
|
||||
shrpx::test_util_percent_encode_path) ||
|
||||
!CU_add_test(pSuite, "util_percent_decode",
|
||||
shrpx::test_util_percent_decode) ||
|
||||
!CU_add_test(pSuite, "util_quote_string",
|
||||
|
|
|
@ -39,7 +39,7 @@ std::string create_error_html(unsigned int status_code) {
|
|||
std::string res;
|
||||
res.reserve(512);
|
||||
auto status = http2::get_status_string(status_code);
|
||||
res += "<!DOCTYPE html><html lang=en><title>";
|
||||
res += R"(<!DOCTYPE html><html lang="en"><title>)";
|
||||
res += status;
|
||||
res += "</title><body><h1>";
|
||||
res += status;
|
||||
|
|
41
src/util.cc
41
src/util.cc
|
@ -70,6 +70,13 @@ bool inRFC3986UnreservedChars(const char c) {
|
|||
std::find(&unreserved[0], &unreserved[4], c) != &unreserved[4];
|
||||
}
|
||||
|
||||
bool in_rfc3986_sub_delims(const char c) {
|
||||
static const char sub_delims[] = {'!', '$', '&', '\'', '(', ')',
|
||||
'*', '+', ',', ';', '='};
|
||||
return std::find(std::begin(sub_delims), std::end(sub_delims), c) !=
|
||||
std::end(sub_delims);
|
||||
}
|
||||
|
||||
std::string percentEncode(const unsigned char *target, size_t len) {
|
||||
std::string dest;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
|
@ -91,6 +98,21 @@ std::string percentEncode(const std::string &target) {
|
|||
target.size());
|
||||
}
|
||||
|
||||
std::string percent_encode_path(const std::string &s) {
|
||||
std::string dest;
|
||||
for (auto c : s) {
|
||||
if (inRFC3986UnreservedChars(c) || in_rfc3986_sub_delims(c) || c == '/') {
|
||||
dest += c;
|
||||
continue;
|
||||
}
|
||||
|
||||
dest += "%";
|
||||
dest += UPPER_XDIGITS[(c >> 4) & 0x0f];
|
||||
dest += UPPER_XDIGITS[(c & 0x0f)];
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
bool in_token(char c) {
|
||||
static const char extra[] = {'!', '#', '$', '%', '&', '\'', '*', '+',
|
||||
'-', '.', '^', '_', '`', '|', '~'};
|
||||
|
@ -137,25 +159,6 @@ uint32_t hex_to_uint(char c) {
|
|||
return c;
|
||||
}
|
||||
|
||||
std::string percentDecode(std::string::const_iterator first,
|
||||
std::string::const_iterator last) {
|
||||
std::string result;
|
||||
for (; first != last; ++first) {
|
||||
if (*first == '%') {
|
||||
if (first + 1 != last && first + 2 != last && isHexDigit(*(first + 1)) &&
|
||||
isHexDigit(*(first + 2))) {
|
||||
result += (hex_to_uint(*(first + 1)) << 4) + hex_to_uint(*(first + 2));
|
||||
first += 2;
|
||||
continue;
|
||||
}
|
||||
result += *first;
|
||||
continue;
|
||||
}
|
||||
result += *first;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string quote_string(const std::string &target) {
|
||||
auto cnt = std::count(std::begin(target), std::end(target), '"');
|
||||
|
||||
|
|
25
src/util.h
25
src/util.h
|
@ -162,6 +162,8 @@ bool isHexDigit(const char c);
|
|||
|
||||
bool inRFC3986UnreservedChars(const char c);
|
||||
|
||||
bool in_rfc3986_sub_delims(const char c);
|
||||
|
||||
// Returns true if |c| is in token (HTTP-p1, Section 3.2.6)
|
||||
bool in_token(char c);
|
||||
|
||||
|
@ -175,8 +177,27 @@ std::string percentEncode(const unsigned char *target, size_t len);
|
|||
|
||||
std::string percentEncode(const std::string &target);
|
||||
|
||||
std::string percentDecode(std::string::const_iterator first,
|
||||
std::string::const_iterator last);
|
||||
// percent-encode path component of URI |s|.
|
||||
std::string percent_encode_path(const std::string &s);
|
||||
|
||||
template <typename InputIt>
|
||||
std::string percentDecode(InputIt first, InputIt last) {
|
||||
std::string result;
|
||||
for (; first != last; ++first) {
|
||||
if (*first == '%') {
|
||||
if (first + 1 != last && first + 2 != last && isHexDigit(*(first + 1)) &&
|
||||
isHexDigit(*(first + 2))) {
|
||||
result += (hex_to_uint(*(first + 1)) << 4) + hex_to_uint(*(first + 2));
|
||||
first += 2;
|
||||
continue;
|
||||
}
|
||||
result += *first;
|
||||
continue;
|
||||
}
|
||||
result += *first;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Percent encode |target| if character is not in token or '%'.
|
||||
std::string percent_encode_token(const std::string &target);
|
||||
|
|
|
@ -139,6 +139,11 @@ void test_util_percent_encode_token(void) {
|
|||
CU_ASSERT("http%202" == util::percent_encode_token("http 2"));
|
||||
}
|
||||
|
||||
void test_util_percent_encode_path(void) {
|
||||
CU_ASSERT("/foo1/bar%3F&/%0A" == util::percent_encode_path("/foo1/bar?&/"
|
||||
"\x0a"));
|
||||
}
|
||||
|
||||
void test_util_percent_decode(void) {
|
||||
{
|
||||
std::string s = "%66%6F%6f%62%61%72";
|
||||
|
|
|
@ -33,6 +33,7 @@ void test_util_inp_strlower(void);
|
|||
void test_util_to_base64(void);
|
||||
void test_util_to_token68(void);
|
||||
void test_util_percent_encode_token(void);
|
||||
void test_util_percent_encode_path(void);
|
||||
void test_util_percent_decode(void);
|
||||
void test_util_quote_string(void);
|
||||
void test_util_utox(void);
|
||||
|
|
Loading…
Reference in New Issue