diff --git a/examples/Makefile.am b/examples/Makefile.am index ab44912c..ea817276 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -63,6 +63,7 @@ noinst_PROGRAMS += asio-sv asio-sv2 asio-sv3 asio-cl ASIOCPPFLAGS = ${BOOST_CPPFLAGS} ${AM_CPPFLAGS} 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} \ diff --git a/examples/asio-cl.cc b/examples/asio-cl.cc index a96de1c2..e130a55a 100644 --- a/examples/asio-cl.cc +++ b/examples/asio-cl.cc @@ -49,9 +49,13 @@ void print_header(const response &res) { } void print_header(const request &req) { - std::cerr << req.method() << " " << req.scheme() << "://" << req.authority() - << req.path() << " " - << "HTTP/2\n"; + 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()); } diff --git a/src/asio_client_request.cc b/src/asio_client_request.cc index 4aa83866..8997a23b 100644 --- a/src/asio_client_request.cc +++ b/src/asio_client_request.cc @@ -46,16 +46,10 @@ 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 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 header_map &request::header() const { return impl_->header(); } request_impl &request::impl() { return *impl_; } diff --git a/src/asio_client_request_impl.cc b/src/asio_client_request_impl.cc index 47eb943f..4be1cd10 100644 --- a/src/asio_client_request_impl.cc +++ b/src/asio_client_request_impl.cc @@ -76,6 +76,10 @@ 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_; } + void request_impl::method(std::string s) { method_ = std::move(s); } const std::string &request_impl::method() const { return method_; } diff --git a/src/asio_client_request_impl.h b/src/asio_client_request_impl.h index be8cc220..e3769a16 100644 --- a/src/asio_client_request_impl.h +++ b/src/asio_client_request_impl.h @@ -63,6 +63,9 @@ public: void stream(class stream *strm); + void uri(uri_ref uri); + const uri_ref &uri() const; + void method(std::string s); const std::string &method() const; @@ -85,6 +88,7 @@ private: close_cb close_cb_; read_cb read_cb_; class stream *strm_; + uri_ref uri_; std::string method_; std::string scheme_; std::string path_; diff --git a/src/asio_client_session_impl.cc b/src/asio_client_session_impl.cc index cf7c049d..3292d51e 100644 --- a/src/asio_client_session_impl.cc +++ b/src/asio_client_session_impl.cc @@ -246,6 +246,11 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, return 0; } + auto &req = push_strm->request().impl(); + req.uri(make_uri_ref(req.scheme(), + req.authority().empty() ? req.host() : req.authority(), + req.path())); + strm->request().impl().call_on_push(push_strm->request()); break; @@ -364,6 +369,19 @@ const request *session_impl::submit(boost::system::error_code &ec, 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 nva = std::vector(); nva.reserve(3 + h.size()); nva.push_back(http2::make_nv_ls(":method", method)); @@ -379,6 +397,17 @@ const request *session_impl::submit(boost::system::error_code &ec, auto &req = strm->request().impl(); req.header(std::move(h)); + { + std::string scheme, host, raw_path, raw_query; + http2::copy_url_component(scheme, &u, UF_SCHEMA, uri.c_str()); + http2::copy_url_component(host, &u, UF_HOST, uri.c_str()); + http2::copy_url_component(raw_path, &u, UF_PATH, uri.c_str()); + http2::copy_url_component(raw_query, &u, UF_QUERY, uri.c_str()); + + req.uri(make_uri_ref(std::move(scheme), std::move(host), + std::move(raw_path), std::move(raw_query))); + } + nghttp2_data_provider *prdptr = nullptr; nghttp2_data_provider prd; diff --git a/src/asio_common.cc b/src/asio_common.cc index 34efefa5..fdad1915 100644 --- a/src/asio_common.cc +++ b/src/asio_common.cc @@ -57,5 +57,28 @@ read_cb string_reader(std::string data) { }; } +uri_ref make_uri_ref(std::string scheme, std::string host, std::string raw_path, + std::string raw_query) { + return uri_ref{ + std::move(scheme), std::move(host), percent_decode(raw_path), + std::move(raw_path), + }; +} + +uri_ref make_uri_ref(std::string scheme, std::string host, + const std::string &raw_path_query) { + auto path_end = raw_path_query.find('?'); + std::size_t query_pos; + if (path_end == std::string::npos) { + query_pos = path_end = raw_path_query.size(); + } else { + query_pos = path_end + 1; + } + return uri_ref{std::move(scheme), std::move(host), + util::percentDecode(std::begin(raw_path_query), + std::begin(raw_path_query) + path_end), + raw_path_query.substr(query_pos)}; +} + } // namespace asio_http2 } // namespace nghttp2 diff --git a/src/asio_common.h b/src/asio_common.h index dbf2230e..ca6a63f0 100644 --- a/src/asio_common.h +++ b/src/asio_common.h @@ -38,6 +38,12 @@ read_cb string_reader(std::string data); boost::system::error_code make_error_code(nghttp2_error ev); +uri_ref make_uri_ref(std::string scheme, std::string host, std::string raw_path, + std::string raw_query); + +uri_ref make_uri_ref(std::string scheme, std::string host, + const std::string &raw_path_query); + } // namespace asio_http2 } // namespace nghttp2 diff --git a/src/includes/nghttp2/asio_http2.h b/src/includes/nghttp2/asio_http2.h index 6a2d7d82..ac08aa65 100644 --- a/src/includes/nghttp2/asio_http2.h +++ b/src/includes/nghttp2/asio_http2.h @@ -74,6 +74,15 @@ using header_map = std::multimap; const boost::system::error_category &nghttp2_category() noexcept; +struct uri_ref { + std::string scheme; + std::string host; + // percent-decoded form + std::string path; + std::string raw_query; + std::string fragment; +}; + typedef std::function data_cb; typedef std::function void_cb; typedef std::function error_cb; @@ -320,10 +329,8 @@ public: void cancel() const; 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 uri_ref &uri() const; const header_map &header() const;