diff --git a/examples/Makefile.am b/examples/Makefile.am index 94120991..ab44912c 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -58,10 +58,17 @@ endif # ENABLE_TINY_NGHTTPD if ENABLE_ASIO_LIB -noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3 +noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3 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@ \ + ${BOOST_LDFLAGS} \ + ${BOOST_ASIO_LIB} \ + ${BOOST_THREAD_LIB} \ + ${BOOST_SYSTEM_LIB} \ + @OPENSSL_LIBS@ \ + @APPLDFLAGS@ asio_sv_SOURCES = asio-sv.cc asio_sv_CPPFLAGS = ${ASIOCPPFLAGS} @@ -75,6 +82,10 @@ 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 endif # ENABLE_EXAMPLES diff --git a/examples/asio-cl.cc b/examples/asio-cl.cc new file mode 100644 index 00000000..84d75107 --- /dev/null +++ b/examples/asio-cl.cc @@ -0,0 +1,119 @@ +/* + * 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 +#include + +#include + +#include +#include + +using boost::asio::ip::tcp; + +using namespace nghttp2::asio_http2; +using namespace nghttp2::asio_http2::client; + +void print_header(const http_header &h) { + for (auto &kv : h.items()) { + 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) { + std::cerr << req.method() << " " << req.scheme() << "://" << req.authority() + << req.path() << " " + << "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); + configure_tls_context(tls_ctx); + + tcp::resolver resolver(io_service); + auto endpoint_it = resolver.resolve({"localhost", "3000"}); + + session sess(io_service, tls_ctx, endpoint_it); + sess.on_connect([&sess]() { + std::cerr << "connected" << std::endl; + boost::system::error_code ec; + auto req = sess.submit(ec, "GET", "https://localhost:3000/", + "hello world", {{"cookie", {"foobar", true}}}); + if (ec) { + std::cerr << "error: " << ec.message() << std::endl; + return; + } + + req->on_response([&sess, req](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(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([](request &push_req) { + std::cerr << "push request was received" << std::endl; + + print_header(push_req); + + push_req.on_response([](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(data), len); + // std::cerr << std::endl; + }); + }); + }); + }); + + sess.on_error([](const std::string &error) { + std::cerr << "error: " << error << std::endl; + }); + + io_service.run(); + } catch (std::exception &e) { + std::cerr << "exception: " << e.what() << "\n"; + } + + return 0; +} diff --git a/src/Makefile.am b/src/Makefile.am index 1b13db54..2b7f027f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -179,7 +179,18 @@ libnghttp2_asio_la_SOURCES = \ 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_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 diff --git a/src/asio_client_request.cc b/src/asio_client_request.cc new file mode 100644 index 00000000..2ad6eed8 --- /dev/null +++ b/src/asio_client_request.cc @@ -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 + +#include "asio_client_request_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +request::request() : impl_(make_unique()) {} + +request::~request() {} + +void request::cancel() { impl_->cancel(); } + +void request::on_response(response_cb cb) { impl_->on_response(std::move(cb)); } + +void request::on_push(request_cb cb) { impl_->on_push(std::move(cb)); } + +void request::on_close(close_cb cb) { impl_->on_close(std::move(cb)); } + +const std::string &request::method() const { return impl_->method(); } + +const std::string &request::scheme() const { return impl_->scheme(); } + +const std::string &request::path() const { return impl_->path(); } + +const std::string &request::authority() const { return impl_->authority(); } + +const std::string &request::host() const { return impl_->host(); } + +const http_header &request::header() const { return impl_->header(); } + +request_impl &request::impl() { return *impl_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_request_impl.cc b/src/asio_client_request_impl.cc new file mode 100644 index 00000000..ed33abd7 --- /dev/null +++ b/src/asio_client_request_impl.cc @@ -0,0 +1,101 @@ +/* + * 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 "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +request_impl::request_impl() : strm_(nullptr) {} + +void request_impl::cancel() { strm_->cancel(); } + +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(read_cb cb) { read_cb_ = std::move(cb); } + +read_cb::result_type request_impl::call_on_read(uint8_t *buf, std::size_t len) { + if (read_cb_) { + return read_cb_(buf, len); + } + return read_cb::result_type{}; +} + +void request_impl::header(http_header h) { header_ = std::move(h); } + +http_header &request_impl::header() { return header_; } + +const http_header &request_impl::header() const { return header_; } + +void request_impl::stream(class stream *strm) { strm_ = strm; } + +void request_impl::method(std::string s) { method_ = std::move(s); } + +const std::string &request_impl::method() const { return method_; } + +void request_impl::scheme(std::string s) { scheme_ = std::move(s); } + +const std::string &request_impl::scheme() const { return scheme_; } + +void request_impl::path(std::string s) { path_ = std::move(s); } + +const std::string &request_impl::path() const { return path_; } + +void request_impl::authority(std::string s) { authority_ = std::move(s); } + +const std::string &request_impl::authority() const { return authority_; } + +void request_impl::host(std::string s) { host_ = std::move(s); } + +const std::string &request_impl::host() const { return host_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_request_impl.h b/src/asio_client_request_impl.h new file mode 100644 index 00000000..81dd8e82 --- /dev/null +++ b/src/asio_client_request_impl.h @@ -0,0 +1,99 @@ +/* + * 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 + +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(); + + 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(read_cb cb); + read_cb::result_type call_on_read(uint8_t *buf, std::size_t len); + + void header(http_header h); + http_header &header(); + const http_header &header() const; + + void stream(class stream *strm); + + void method(std::string s); + const std::string &method() const; + + void scheme(std::string s); + const std::string &scheme() const; + + void path(std::string s); + const std::string &path() const; + + void authority(std::string s); + const std::string &authority() const; + + void host(std::string s); + const std::string &host() const; + +private: + http_header header_; + response_cb response_cb_; + request_cb push_request_cb_; + close_cb close_cb_; + read_cb read_cb_; + class stream *strm_; + std::string method_; + std::string scheme_; + std::string path_; + std::string authority_; + std::string host_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_REQUEST_IMPL_H diff --git a/src/asio_client_response.cc b/src/asio_client_response.cc new file mode 100644 index 00000000..87b5ba5e --- /dev/null +++ b/src/asio_client_response.cc @@ -0,0 +1,51 @@ +/* + * 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 + +#include "asio_client_response_impl.h" + +#include "template.h" + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +response::response() : impl_(make_unique()) {} + +response::~response() {} + +void response::on_data(data_cb cb) { 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 http_header &response::header() const { return impl_->header(); } + +response_impl &response::impl() { return *impl_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_response_impl.cc b/src/asio_client_response_impl.cc new file mode 100644 index 00000000..9379b892 --- /dev/null +++ b/src/asio_client_response_impl.cc @@ -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_; } + +http_header &response_impl::header() { return header_; } + +const http_header &response_impl::header() const { return header_; } + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_response_impl.h b/src/asio_client_response_impl.h new file mode 100644 index 00000000..9c2ae2a5 --- /dev/null +++ b/src/asio_client_response_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. + */ +#ifndef ASIO_CLIENT_RESPONSE_IMPL_H +#define ASIO_CLIENT_RESPONSE_IMPL_H + +#include "nghttp2_config.h" + +#include + +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; + + http_header &header(); + const http_header &header() const; + +private: + data_cb data_cb_; + + http_header header_; + + int64_t content_length_; + int status_code_; +}; + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_CLIENT_RESPONSE_IMPL_H diff --git a/src/asio_client_session.cc b/src/asio_client_session.cc new file mode 100644 index 00000000..c49f8cbb --- /dev/null +++ b/src/asio_client_session.cc @@ -0,0 +1,78 @@ +/* + * 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 + +#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, + tcp::resolver::iterator endpoint_it) + : impl_(make_unique(io_service, endpoint_it)) {} + +session::session(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_ctx, + tcp::resolver::iterator endpoint_it) + : impl_(make_unique(io_service, tls_ctx, endpoint_it)) {} + +session::~session() {} + +void session::on_connect(void_cb cb) { impl_->on_connect(std::move(cb)); } + +void session::on_error(error_cb cb) { impl_->on_error(std::move(cb)); } + +void session::shutdown() { impl_->shutdown(); } + +request *session::submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + http_header h) { + return impl_->submit(ec, method, uri, read_cb(), std::move(h)); +} + +request *session::submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + std::string data, http_header h) { + return impl_->submit(ec, method, uri, string_reader(std::move(data)), + std::move(h)); +} + +request *session::submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + read_cb cb, http_header h) { + return impl_->submit(ec, method, uri, std::move(cb), std::move(h)); +} + +} // namespace client +} // namespace asio_http2 +} // nghttp2 diff --git a/src/asio_client_session_impl.cc b/src/asio_client_session_impl.cc new file mode 100644 index 00000000..6d146c3e --- /dev/null +++ b/src/asio_client_session_impl.cc @@ -0,0 +1,539 @@ +/* + * 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 + +#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() + : wblen_(0), session_(nullptr), data_pending_(nullptr), data_pendinglen_(0), + writing_(false), inside_callback_(false) {} + +session_impl::~session_impl() { + // finish up all active stream with CANCEL error code + for (auto &p : streams_) { + auto &strm = p.second; + auto &req = strm->request().impl(); + req.call_on_close(NGHTTP2_CANCEL); + } + + nghttp2_session_del(session_); +} + +void session_impl::connected() { + 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(); + } +} + +void session_impl::not_connected(const boost::system::error_code &ec) { + auto &error_cb = on_error(); + if (error_cb) { + error_cb(ec.message()); + } +} + +void session_impl::on_connect(void_cb cb) { connect_cb_ = std::move(cb); } + +void session_impl::on_error(error_cb cb) { error_cb_ = std::move(cb); } + +const void_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(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(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().add(std::string(name, name + namelen), + std::string(value, value + valuelen), + flags & NGHTTP2_NV_FLAG_NO_INDEX); + } + break; + } + case NGHTTP2_PUSH_PROMISE: { + strm = sess->find_stream(frame->push_promise.promised_stream_id); + if (!strm) { + return 0; + } + + auto &req = strm->request().impl(); + + switch (http2::lookup_token(name, namelen)) { + case http2::HD__METHOD: + req.method(std::string(value, value + valuelen)); + break; + case http2::HD__SCHEME: + req.scheme(std::string(value, value + valuelen)); + break; + case http2::HD__PATH: + req.path(std::string(value, value + valuelen)); + break; + case http2::HD__AUTHORITY: + req.authority(std::string(value, value + valuelen)); + // host defaults to authority value + req.host(std::string(value, value + valuelen)); + break; + case http2::HD_HOST: + req.host(std::string(value, value + valuelen)); + // fall through + default: + req.header().add(std::string(name, name + namelen), + std::string(value, value + valuelen), + flags & NGHTTP2_NV_FLAG_NO_INDEX); + } + + 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(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(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(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(nghttp2_strerror(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) { + nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE, strm.stream_id(), + NGHTTP2_CANCEL); +} + +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 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 session_impl::create_stream() { + auto strm = make_unique(this); + + auto &req = strm->request().impl(); + req.stream(strm.get()); + + return strm; +} + +request *session_impl::submit(boost::system::error_code &ec, + const std::string &method, const std::string &uri, + read_cb cb, http_header h) { + ec.clear(); + + auto nva = std::vector(); + nva.reserve(3 + h.size()); + nva.push_back(http2::make_nv_ls(":method", method)); + nva.push_back(http2::make_nv_ll(":scheme", "https")); + nva.push_back(http2::make_nv_ll(":path", "/")); + nva.push_back(http2::make_nv_ll(":authority", "localhost:3000")); + for (auto &kv : h.items()) { + nva.push_back( + http2::make_nv(kv.first, kv.second.value, kv.second.sensitive)); + } + + auto strm = create_stream(); + auto &req = strm->request().impl(); + 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(source->ptr); + auto rv = strm->request().impl().call_on_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; + }; + 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(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(); +} + +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(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 diff --git a/src/asio_client_session_impl.h b/src/asio_client_session_impl.h new file mode 100644 index 00000000..e4a3e163 --- /dev/null +++ b/src/asio_client_session_impl.h @@ -0,0 +1,114 @@ +/* + * 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 + +#include + +#include + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +class stream; + +using boost::asio::ip::tcp; + +class session_impl { +public: + session_impl(); + virtual ~session_impl(); + + void connected(); + void not_connected(const boost::system::error_code &ec); + + void on_connect(void_cb cb); + void on_error(error_cb cb); + + const void_cb &on_connect() const; + const error_cb &on_error() const; + + void cancel(stream &strm); + + std::unique_ptr create_stream(); + std::unique_ptr pop_stream(int32_t stream_id); + stream *create_push_stream(int32_t stream_id); + stream *find_stream(int32_t stream_id); + + request *submit(boost::system::error_code &ec, const std::string &method, + const std::string &uri, read_cb cb, http_header h); + + 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(); + + void signal_write(); + + void enter_callback(); + void leave_callback(); + + void do_read(); + void do_write(); + +protected: + boost::array rb_; + boost::array wb_; + std::size_t wblen_; + +private: + bool should_stop() const; + bool setup_session(); + + std::map> streams_; + + void_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 diff --git a/src/asio_client_session_tcp_impl.cc b/src/asio_client_session_tcp_impl.cc new file mode 100644 index 00000000..49faa01d --- /dev/null +++ b/src/asio_client_session_tcp_impl.cc @@ -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. + */ +#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, + tcp::resolver::iterator endpoint_it) + : socket_(io_service) { + + boost::asio::async_connect(socket_, endpoint_it, + [this](boost::system::error_code ec, + tcp::resolver::iterator endpoint_it) { + if (!ec) { + connected(); + return; + } + not_connected(ec); + }); +} + +session_tcp_impl::~session_tcp_impl() {} + +tcp::socket &session_tcp_impl::socket() { return socket_; } + +void session_tcp_impl::read_socket( + std::function h) { + socket_.async_read_some(boost::asio::buffer(rb_), h); +} + +void session_tcp_impl::write_socket( + std::function 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 diff --git a/src/asio_client_session_tcp_impl.h b/src/asio_client_session_tcp_impl.h new file mode 100644 index 00000000..83ee7ef7 --- /dev/null +++ b/src/asio_client_session_tcp_impl.h @@ -0,0 +1,61 @@ +/* + * 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 + +#include + +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, + tcp::resolver::iterator endpoint_it); + virtual ~session_tcp_impl(); + + 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 diff --git a/src/asio_client_session_tls_impl.cc b/src/asio_client_session_tls_impl.cc new file mode 100644 index 00000000..1fdcaf69 --- /dev/null +++ b/src/asio_client_session_tls_impl.cc @@ -0,0 +1,75 @@ +/* + * 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, + tcp::resolver::iterator endpoint_it) + : socket_(io_service, tls_ctx) { + + boost::asio::async_connect(socket(), endpoint_it, + [this](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](const boost::system::error_code &ec) { + if (ec) { + not_connected(ec); + return; + } + connected(); + }); + }); +} + +session_tls_impl::~session_tls_impl() {} + +tcp::socket &session_tls_impl::socket() { return socket_.next_layer(); } + +void session_tls_impl::read_socket( + std::function h) { + socket_.async_read_some(boost::asio::buffer(rb_), h); +} + +void session_tls_impl::write_socket( + std::function 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 diff --git a/src/asio_client_session_tls_impl.h b/src/asio_client_session_tls_impl.h new file mode 100644 index 00000000..b7a60c5d --- /dev/null +++ b/src/asio_client_session_tls_impl.h @@ -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_CLIENT_SESSION_TLS_IMPL_H +#define ASIO_CLIENT_SESSION_TLS_IMPL_H + +#include "asio_client_session_impl.h" + +#include + +#include + +namespace nghttp2 { +namespace asio_http2 { +namespace client { + +using boost::asio::ip::tcp; + +using ssl_socket = boost::asio::ssl::stream; + +class session_tls_impl : public session_impl { +public: + session_tls_impl(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_ctx, + tcp::resolver::iterator endpoint_it); + virtual ~session_tls_impl(); + + 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 diff --git a/src/asio_client_stream.cc b/src/asio_client_stream.cc new file mode 100644 index 00000000..60f5572b --- /dev/null +++ b/src/asio_client_stream.cc @@ -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 "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) {} + +void stream::cancel() { sess_->cancel(*this); } + +void stream::stream_id(int32_t stream_id) { stream_id_ = stream_id; } + +int32_t stream::stream_id() const { return stream_id_; } + +request &stream::request() { return request_; } + +response &stream::response() { return response_; } + +bool stream::expect_final_response() const { + return response_.status_code() / 100 == 1; +} + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_stream.h b/src/asio_client_stream.h new file mode 100644 index 00000000..3c25f211 --- /dev/null +++ b/src/asio_client_stream.h @@ -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 + +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 cancel(); + + void stream_id(int32_t stream_id); + int32_t stream_id() const; + + request &request(); + response &response(); + + 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 diff --git a/src/asio_client_tls_context.cc b/src/asio_client_tls_context.cc new file mode 100644 index 00000000..95e3c063 --- /dev/null +++ b/src/asio_client_tls_context.cc @@ -0,0 +1,67 @@ +/* + * 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 + +#include + +#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(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_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS); + + SSL_CTX_set_cipher_list(ctx, ssl::DEFAULT_CIPHER_LIST); + + SSL_CTX_set_next_proto_select_cb(ctx, client_select_next_proto_cb, nullptr); +} + +} // namespace client +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_client_tls_context.h b/src/asio_client_tls_context.h new file mode 100644 index 00000000..7b0e97de --- /dev/null +++ b/src/asio_client_tls_context.h @@ -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 + +#endif // ASIO_CLIENT_TLS_CONTEXT_H diff --git a/src/asio_common.cc b/src/asio_common.cc new file mode 100644 index 00000000..ee77a630 --- /dev/null +++ b/src/asio_common.cc @@ -0,0 +1,102 @@ +/* + * 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 + +#include "util.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(ev), nghttp2_category()); +} + +read_cb string_reader(std::string data) { + auto strio = std::make_shared>(std::move(data), + data.size()); + return [strio](uint8_t *buf, size_t len) { + auto n = std::min(len, strio->second); + std::copy_n(strio->first.c_str(), n, buf); + strio->second -= n; + return std::make_pair(n, strio->second == 0); + }; +} + +http_header::http_header() {} + +http_header::http_header( + std::initializer_list> ilist) { + for (auto &kv : ilist) { + auto name = kv.first; + util::inp_strlower(name); + items_.emplace(std::move(name), kv.second); + } +} + +http_header &http_header:: +operator=(std::initializer_list> ilist) { + items_.clear(); + for (auto &kv : ilist) { + auto name = kv.first; + util::inp_strlower(name); + items_.emplace(std::move(name), kv.second); + } + return *this; +} + +const header_map &http_header::items() const { return items_; } + +void http_header::add(std::string name, std::string value, bool sensitive) { + util::inp_strlower(name); + items_.emplace(name, header_value(value, sensitive)); +} + +const header_value *http_header::get(const std::string &name) const { + auto it = items_.find(name); + if (it == std::end(items_)) { + return nullptr; + } + return &(*it).second; +} + +std::size_t http_header::size() const { return items_.size(); } + +bool http_header::empty() const { return items_.empty(); } + +} // namespace asio_http2 +} // namespace nghttp2 diff --git a/src/asio_common.h b/src/asio_common.h new file mode 100644 index 00000000..dbf2230e --- /dev/null +++ b/src/asio_common.h @@ -0,0 +1,44 @@ +/* + * 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 + +#include + +namespace nghttp2 { +namespace asio_http2 { + +read_cb string_reader(std::string data); + +boost::system::error_code make_error_code(nghttp2_error ev); + +} // namespace asio_http2 +} // namespace nghttp2 + +#endif // ASIO_COMMON_H diff --git a/src/asio_http2_handler.cc b/src/asio_http2_handler.cc index fb8c6a2b..f3302ec8 100644 --- a/src/asio_http2_handler.cc +++ b/src/asio_http2_handler.cc @@ -26,6 +26,7 @@ #include +#include "asio_common.h" #include "http2.h" #include "util.h" #include "template.h" @@ -206,20 +207,7 @@ void response_impl::end(std::string data) { return; } - auto strio = std::make_shared>(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)); + end(string_reader(std::move(data))); } void response_impl::end(read_cb cb) { diff --git a/src/asio_http2_impl.cc b/src/asio_http2_impl.cc index 051e4da8..ab575ac0 100644 --- a/src/asio_http2_impl.cc +++ b/src/asio_http2_impl.cc @@ -92,7 +92,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); diff --git a/src/includes/nghttp2/asio_http2.h b/src/includes/nghttp2/asio_http2.h index c09b7acd..10155cef 100644 --- a/src/includes/nghttp2/asio_http2.h +++ b/src/includes/nghttp2/asio_http2.h @@ -30,18 +30,79 @@ #include #include #include +#include + +#include +#include +#include + +#include + +namespace boost { +namespace system { + +template <> struct is_error_code_enum { + BOOST_STATIC_CONSTANT(bool, value = true); +}; + +} // namespace system +} // namespace boost namespace nghttp2 { namespace asio_http2 { struct header { + header() : sensitive(false) {} + header(std::string name, std::string value, bool sensitive = false) + : name(std::move(name)), value(std::move(value)), sensitive(sensitive) {} + std::string name; std::string value; + bool sensitive; }; +struct header_value { + header_value(std::string value, bool sensitive = false) + : value(std::move(value)), sensitive(sensitive) {} + + std::string value; + bool sensitive; +}; + +using header_map = std::multimap; + +class http_header { +public: + http_header(); + http_header(const http_header &other) = default; + http_header(http_header &&other) = default; + http_header( + std::initializer_list> ilist); + + http_header &operator=(const http_header &other) = default; + http_header &operator=(http_header &&other) = default; + http_header & + operator=(std::initializer_list> ilist); + + const header_map &items() const; + + void add(std::string name, std::string value, bool sensitive); + const header_value *get(const std::string &name) const; + + std::size_t size() const; + bool empty() const; + +private: + header_map items_; +}; + +const boost::system::error_category &nghttp2_category() noexcept; + typedef std::function data_cb; typedef std::function void_cb; +typedef std::function error_cb; +typedef std::function close_cb; // Callback function to generate response body. The implementation of // this callback must fill at most |len| bytes data to |buf|. The @@ -103,9 +164,9 @@ public: // 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). @@ -242,6 +303,92 @@ std::string percent_decode(const std::string &s); // Returns HTTP date representation of current posix time |t|. std::string http_date(int64_t t); +namespace client { + +class response_impl; + +class response { +public: + response(); + ~response(); + + void on_data(data_cb cb); + + int status_code() const; + + int64_t content_length() const; + + const http_header &header() const; + + response_impl &impl(); + +private: + std::unique_ptr impl_; +}; + +class request; + +using response_cb = std::function; +using request_cb = std::function; + +class request_impl; + +class request { +public: + request(); + ~request(); + + void on_response(response_cb cb); + void on_push(request_cb cb); + void on_close(close_cb cb); + + void cancel(); + + const std::string &method() const; + const std::string &scheme() const; + const std::string &path() const; + const std::string &authority() const; + const std::string &host() const; + + const http_header &header() const; + + request_impl &impl(); + +private: + std::unique_ptr impl_; +}; + +class session_impl; + +class session { +public: + session(boost::asio::io_service &io_service, + boost::asio::ip::tcp::resolver::iterator endpoint_it); + session(boost::asio::io_service &io_service, + boost::asio::ssl::context &tls_context, + boost::asio::ip::tcp::resolver::iterator endpoint_it); + ~session(); + + void on_connect(void_cb cb); + void on_error(error_cb cb); + + void shutdown(); + + request *submit(boost::system::error_code &ec, const std::string &method, + const std::string &uri, http_header h = {}); + request *submit(boost::system::error_code &ec, const std::string &method, + const std::string &uri, std::string data, http_header h = {}); + request *submit(boost::system::error_code &ec, const std::string &method, + const std::string &uri, read_cb cb, http_header h = {}); + +private: + std::unique_ptr impl_; +}; + +void configure_tls_context(boost::asio::ssl::context &tls_ctx); + +} // namespace client + } // namespace asio_http2 } // namespace nghttp2