diff --git a/examples/asio-sv.cc b/examples/asio-sv.cc index 8d88ddcb..47a1d847 100644 --- a/examples/asio-sv.cc +++ b/examples/asio-sv.cc @@ -101,6 +101,30 @@ int main(int argc, char *argv[]) { res.end("finally!\n"); }); }); + server.handle("/trailer", [](const request &req, const response &res) { + // send trailer part. + res.write_head(200, {{"trailers", {"digest"}}}); + + std::string body = "nghttp2 FTW!\n"; + auto left = std::make_shared(body.size()); + + res.end([&res, body, left](uint8_t *dst, std::size_t len, + uint32_t *data_flags) { + auto n = std::min(len, *left); + std::copy_n(body.c_str() + (body.size() - *left), n, dst); + *left -= n; + if (*left == 0) { + *data_flags |= + NGHTTP2_DATA_FLAG_EOF | NGHTTP2_DATA_FLAG_NO_END_STREAM; + // RFC 3230 Instance Digests in HTTP. The digest value is + // SHA-256 message digest of body. + res.write_trailer( + {{"digest", + {"SHA-256=qqXqskW7F3ueBSvmZRCiSwl2ym4HRO0M/pvQCBlSDis="}}}); + } + return n; + }); + }); if (argc >= 6) { boost::asio::ssl::context tls(boost::asio::ssl::context::sslv23); diff --git a/src/asio_server_http2_handler.cc b/src/asio_server_http2_handler.cc index 76436aec..7e2304ae 100644 --- a/src/asio_server_http2_handler.cc +++ b/src/asio_server_http2_handler.cc @@ -359,6 +359,27 @@ int http2_handler::start_response(stream &strm) { return 0; } +int http2_handler::submit_trailer(stream &strm, header_map h) { + int rv; + auto nva = std::vector(); + nva.reserve(h.size()); + for (auto &hd : h) { + nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value, + hd.second.sensitive)); + } + + rv = nghttp2_submit_trailer(session_, strm.get_stream_id(), nva.data(), + nva.size()); + + if (rv != 0) { + return -1; + } + + signal_write(); + + return 0; +} + void http2_handler::enter_callback() { assert(!inside_callback_); inside_callback_ = true; diff --git a/src/asio_server_http2_handler.h b/src/asio_server_http2_handler.h index 2f5d69b9..068d4527 100644 --- a/src/asio_server_http2_handler.h +++ b/src/asio_server_http2_handler.h @@ -70,6 +70,8 @@ public: int start_response(stream &s); + int submit_trailer(stream &s, header_map h); + void stream_error(int32_t stream_id, uint32_t error_code); void initiate_write(); diff --git a/src/asio_server_response.cc b/src/asio_server_response.cc index 6952b586..f4206930 100644 --- a/src/asio_server_response.cc +++ b/src/asio_server_response.cc @@ -46,6 +46,10 @@ 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::write_trailer(header_map h) const { + impl_->write_trailer(std::move(h)); +} + 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); } diff --git a/src/asio_server_response_impl.cc b/src/asio_server_response_impl.cc index e9122dca..1cdbc591 100644 --- a/src/asio_server_response_impl.cc +++ b/src/asio_server_response_impl.cc @@ -81,6 +81,11 @@ void response_impl::end(generator_cb cb) { state_ = response_state::BODY_STARTED; } +void response_impl::write_trailer(header_map h) { + auto handler = strm_->handler(); + handler->submit_trailer(*strm_, std::move(h)); +} + void response_impl::start_response() { auto handler = strm_->handler(); diff --git a/src/asio_server_response_impl.h b/src/asio_server_response_impl.h index c85101a0..8955968a 100644 --- a/src/asio_server_response_impl.h +++ b/src/asio_server_response_impl.h @@ -49,6 +49,7 @@ public: void write_head(unsigned int status_code, header_map h = {}); void end(std::string data = ""); void end(generator_cb cb); + void write_trailer(header_map h); void on_close(close_cb cb); void resume(); diff --git a/src/includes/nghttp2/asio_http2_server.h b/src/includes/nghttp2/asio_http2_server.h index 267d1dfc..9f5a4b31 100644 --- a/src/includes/nghttp2/asio_http2_server.h +++ b/src/includes/nghttp2/asio_http2_server.h @@ -81,6 +81,11 @@ public: // call of end() is allowed. void end(generator_cb cb) const; + // Write trailer part. This must be called after setting both + // NGHTTP2_DATA_FLAG_EOF and NGHTTP2_DATA_FLAG_NO_END_STREAM set in + // *data_flag parameter in generator_cb passed to end() function. + void write_trailer(header_map h) 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. diff --git a/src/template.h b/src/template.h index 41295fc7..df5565bf 100644 --- a/src/template.h +++ b/src/template.h @@ -76,6 +76,10 @@ template Defer defer(F &&f, T &&... t) { return Defer(std::forward(f), std::forward(t)...); } +template bool test_flags(T t, F flags) { + return (t & flags) == flags; +} + } // namespace nghttp2 #endif // TEMPLATE_H