2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2014 Tatsuhiro Tsujikawa
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 #include "asio_http2_handler.h"
35 namespace asio_http2 {
37 channel::channel() : impl_(make_unique<channel_impl>()) {}
39 void channel::post(void_cb cb) { impl_->post(std::move(cb)); }
41 channel_impl &channel::impl() { return *impl_; }
43 channel_impl::channel_impl() : strand_(nullptr) {}
45 void channel_impl::post(void_cb cb) { strand_->post(std::move(cb)); }
47 void channel_impl::strand(boost::asio::io_service::strand *strand) {
53 extern std::shared_ptr<std::string> cached_date;
55 request::request() : impl_(make_unique<request_impl>()) {}
57 const std::vector<header> &request::headers() const { return impl_->headers(); }
59 const std::string &request::method() const { return impl_->method(); }
61 const std::string &request::scheme() const { return impl_->scheme(); }
63 const std::string &request::authority() const { return impl_->authority(); }
65 const std::string &request::host() const { return impl_->host(); }
67 const std::string &request::path() const { return impl_->path(); }
69 bool request::push(std::string method, std::string path,
70 std::vector<header> headers) {
71 return impl_->push(std::move(method), std::move(path), std::move(headers));
74 bool request::pushed() const { return impl_->pushed(); }
76 bool request::closed() const { return impl_->closed(); }
78 void request::on_data(data_cb cb) { return impl_->on_data(std::move(cb)); }
80 void request::on_end(void_cb cb) { return impl_->on_end(std::move(cb)); }
82 bool request::run_task(thread_cb start) {
83 return impl_->run_task(std::move(start));
86 request_impl &request::impl() { return *impl_; }
88 response::response() : impl_(make_unique<response_impl>()) {}
90 void response::write_head(unsigned int status_code,
91 std::vector<header> headers) {
92 impl_->write_head(status_code, std::move(headers));
95 void response::end(std::string data) { impl_->end(std::move(data)); }
97 void response::end(read_cb cb) { impl_->end(std::move(cb)); }
99 void response::resume() { impl_->resume(); }
101 unsigned int response::status_code() const { return impl_->status_code(); }
103 bool response::started() const { return impl_->started(); }
105 response_impl &response::impl() { return *impl_; }
107 request_impl::request_impl() : pushed_(false) {}
109 const std::vector<header> &request_impl::headers() const { return headers_; }
111 const std::string &request_impl::method() const { return method_; }
113 const std::string &request_impl::scheme() const { return scheme_; }
115 const std::string &request_impl::authority() const { return authority_; }
117 const std::string &request_impl::host() const { return host_; }
119 const std::string &request_impl::path() const { return path_; }
121 void request_impl::set_header(std::vector<header> headers) {
122 headers_ = std::move(headers);
125 void request_impl::add_header(std::string name, std::string value) {
126 headers_.push_back(header{std::move(name), std::move(value)});
129 void request_impl::method(std::string arg) { method_ = std::move(arg); }
131 void request_impl::scheme(std::string arg) { scheme_ = std::move(arg); }
133 void request_impl::authority(std::string arg) { authority_ = std::move(arg); }
135 void request_impl::host(std::string arg) { host_ = std::move(arg); }
137 void request_impl::path(std::string arg) { path_ = std::move(arg); }
139 bool request_impl::push(std::string method, std::string path,
140 std::vector<header> headers) {
145 auto handler = handler_.lock();
146 auto stream = stream_.lock();
147 auto rv = handler->push_promise(*stream, std::move(method), std::move(path),
152 bool request_impl::pushed() const { return pushed_; }
154 void request_impl::pushed(bool f) { pushed_ = f; }
156 bool request_impl::closed() const {
157 return handler_.expired() || stream_.expired();
160 void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); }
162 void request_impl::on_end(void_cb cb) { on_end_cb_ = std::move(cb); }
164 bool request_impl::run_task(thread_cb start) {
169 auto handler = handler_.lock();
171 return handler->run_task(std::move(start));
174 void request_impl::handler(std::weak_ptr<http2_handler> h) {
175 handler_ = std::move(h);
178 void request_impl::stream(std::weak_ptr<http2_stream> s) {
179 stream_ = std::move(s);
182 void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
184 on_data_cb_(data, len);
188 void request_impl::call_on_end() {
194 response_impl::response_impl() : status_code_(200), started_(false) {}
196 unsigned int response_impl::status_code() const { return status_code_; }
198 void response_impl::write_head(unsigned int status_code,
199 std::vector<header> headers) {
200 status_code_ = status_code;
201 headers_ = std::move(headers);
204 void response_impl::end(std::string data) {
209 auto strio = std::make_shared<std::pair<std::string, size_t>>(std::move(data),
211 auto read_cb = [strio](uint8_t *buf, size_t len) {
212 auto nread = std::min(len, strio->second);
213 memcpy(buf, strio->first.c_str(), nread);
214 strio->second -= nread;
215 if (strio->second == 0) {
216 return std::make_pair(nread, true);
219 return std::make_pair(nread, false);
222 end(std::move(read_cb));
225 void response_impl::end(read_cb cb) {
226 if (started_ || closed()) {
230 read_cb_ = std::move(cb);
233 auto handler = handler_.lock();
234 auto stream = stream_.lock();
236 if (handler->start_response(*stream) != 0) {
237 handler->stream_error(stream->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
241 if (!handler->inside_callback()) {
242 handler->initiate_write();
246 bool response_impl::closed() const {
247 return handler_.expired() || stream_.expired();
250 void response_impl::resume() {
255 auto handler = handler_.lock();
256 auto stream = stream_.lock();
257 handler->resume(*stream);
259 if (!handler->inside_callback()) {
260 handler->initiate_write();
264 bool response_impl::started() const { return started_; }
266 const std::vector<header> &response_impl::headers() const { return headers_; }
268 void response_impl::handler(std::weak_ptr<http2_handler> h) {
269 handler_ = std::move(h);
272 void response_impl::stream(std::weak_ptr<http2_stream> s) {
273 stream_ = std::move(s);
276 std::pair<ssize_t, bool> response_impl::call_read(uint8_t *data,
279 return read_cb_(data, len);
282 return std::make_pair(0, true);
285 http2_stream::http2_stream(int32_t stream_id)
286 : request_(std::make_shared<request>()),
287 response_(std::make_shared<response>()), stream_id_(stream_id) {}
289 int32_t http2_stream::get_stream_id() const { return stream_id_; }
291 const std::shared_ptr<request> &http2_stream::get_request() { return request_; }
293 const std::shared_ptr<response> &http2_stream::get_response() {
298 int stream_error(nghttp2_session *session, int32_t stream_id,
299 uint32_t error_code) {
300 return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
306 int on_begin_headers_callback(nghttp2_session *session,
307 const nghttp2_frame *frame, void *user_data) {
308 auto handler = static_cast<http2_handler *>(user_data);
310 if (frame->hd.type != NGHTTP2_HEADERS ||
311 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
315 handler->create_stream(frame->hd.stream_id);
322 int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
323 const uint8_t *name, size_t namelen,
324 const uint8_t *value, size_t valuelen, uint8_t flags,
326 auto handler = static_cast<http2_handler *>(user_data);
327 auto stream_id = frame->hd.stream_id;
329 if (frame->hd.type != NGHTTP2_HEADERS ||
330 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
334 auto stream = handler->find_stream(stream_id);
339 if (!nghttp2_check_header_name(name, namelen) ||
340 !nghttp2_check_header_value(value, valuelen)) {
341 stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
343 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
346 auto &req = stream->get_request()->impl();
348 if (name[0] == ':' && !req.headers().empty()) {
349 stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
350 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
353 if (util::streq(":method", name, namelen)) {
354 if (!req.method().empty()) {
355 stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
356 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
358 req.method(std::string(value, value + valuelen));
359 } else if (util::streq(":scheme", name, namelen)) {
360 if (!req.scheme().empty()) {
361 stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
362 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
364 req.scheme(std::string(value, value + valuelen));
365 } else if (util::streq(":authority", name, namelen)) {
366 if (!req.authority().empty()) {
367 stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
368 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
370 req.authority(std::string(value, value + valuelen));
371 } else if (util::streq(":path", name, namelen)) {
372 if (!req.path().empty()) {
373 stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
374 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
376 req.path(std::string(value, value + valuelen));
378 if (name[0] == ':') {
379 stream_error(session, stream_id, NGHTTP2_PROTOCOL_ERROR);
380 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
383 if (util::streq("host", name, namelen)) {
384 req.host(std::string(value, value + valuelen));
387 req.add_header(std::string(name, name + namelen),
388 std::string(value, value + valuelen));
396 int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
398 auto handler = static_cast<http2_handler *>(user_data);
399 auto stream = handler->find_stream(frame->hd.stream_id);
401 switch (frame->hd.type) {
407 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
408 stream->get_request()->impl().call_on_end();
412 case NGHTTP2_HEADERS: {
413 if (!stream || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
417 auto &req = stream->get_request()->impl();
419 if (req.method().empty() || req.scheme().empty() || req.path().empty() ||
420 (req.authority().empty() && req.host().empty())) {
421 stream_error(session, frame->hd.stream_id, NGHTTP2_PROTOCOL_ERROR);
425 if (req.host().empty()) {
426 req.host(req.authority());
429 handler->call_on_request(*stream);
431 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
432 stream->get_request()->impl().call_on_end();
444 int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
445 int32_t stream_id, const uint8_t *data,
446 size_t len, void *user_data) {
447 auto handler = static_cast<http2_handler *>(user_data);
448 auto stream = handler->find_stream(stream_id);
454 stream->get_request()->impl().call_on_data(data, len);
462 int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
463 uint32_t error_code, void *user_data) {
464 auto handler = static_cast<http2_handler *>(user_data);
466 handler->close_stream(stream_id);
473 int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
475 auto handler = static_cast<http2_handler *>(user_data);
477 if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
481 auto stream = handler->find_stream(frame->push_promise.promised_stream_id);
487 handler->call_on_request(*stream);
494 int on_frame_not_send_callback(nghttp2_session *session,
495 const nghttp2_frame *frame, int lib_error_code,
497 if (frame->hd.type != NGHTTP2_HEADERS) {
501 // Issue RST_STREAM so that stream does not hang around.
502 nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
503 NGHTTP2_INTERNAL_ERROR);
509 http2_handler::http2_handler(boost::asio::io_service &io_service,
510 boost::asio::io_service &task_io_service_,
511 connection_write writefun, request_cb cb)
512 : writefun_(writefun), request_cb_(std::move(cb)), io_service_(io_service),
513 task_io_service_(task_io_service_),
514 strand_(std::make_shared<boost::asio::io_service::strand>(io_service_)),
515 session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false) {}
517 http2_handler::~http2_handler() { nghttp2_session_del(session_); }
519 int http2_handler::start() {
522 nghttp2_session_callbacks *callbacks;
523 rv = nghttp2_session_callbacks_new(&callbacks);
528 auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
530 nghttp2_session_callbacks_set_on_begin_headers_callback(
531 callbacks, on_begin_headers_callback);
532 nghttp2_session_callbacks_set_on_header_callback(callbacks,
534 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
535 on_frame_recv_callback);
536 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
537 callbacks, on_data_chunk_recv_callback);
538 nghttp2_session_callbacks_set_on_stream_close_callback(
539 callbacks, on_stream_close_callback);
540 nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
541 on_frame_send_callback);
542 nghttp2_session_callbacks_set_on_frame_not_send_callback(
543 callbacks, on_frame_not_send_callback);
545 nghttp2_option *option;
546 rv = nghttp2_option_new(&option);
551 auto opt_del = defer(nghttp2_option_del, option);
553 nghttp2_option_set_recv_client_preface(option, 1);
555 rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
560 nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
561 nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
566 std::shared_ptr<http2_stream> http2_handler::create_stream(int32_t stream_id) {
567 auto stream = std::make_shared<http2_stream>(stream_id);
568 streams_.emplace(stream_id, stream);
570 auto self = shared_from_this();
571 auto &req = stream->get_request()->impl();
572 auto &res = stream->get_response()->impl();
581 void http2_handler::close_stream(int32_t stream_id) {
582 streams_.erase(stream_id);
585 std::shared_ptr<http2_stream> http2_handler::find_stream(int32_t stream_id) {
586 auto i = streams_.find(stream_id);
587 if (i == std::end(streams_)) {
594 void http2_handler::call_on_request(http2_stream &stream) {
595 request_cb_(stream.get_request(), stream.get_response());
598 bool http2_handler::should_stop() const {
599 return !nghttp2_session_want_read(session_) &&
600 !nghttp2_session_want_write(session_);
603 int http2_handler::start_response(http2_stream &stream) {
606 auto &res = stream.get_response()->impl();
607 auto &headers = res.headers();
608 auto nva = std::vector<nghttp2_nv>();
609 nva.reserve(2 + headers.size());
610 auto status = util::utos(res.status_code());
611 auto date = cached_date;
612 nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
613 nva.push_back(nghttp2::http2::make_nv_ls("date", *date));
614 for (auto &hd : headers) {
615 nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
618 nghttp2_data_provider prd;
619 prd.source.ptr = &stream;
621 [](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
622 size_t length, uint32_t *data_flags, nghttp2_data_source *source,
623 void *user_data) -> ssize_t {
624 auto &stream = *static_cast<http2_stream *>(source->ptr);
625 auto rv = stream.get_response()->impl().call_read(buf, length);
627 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
631 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
632 } else if (rv.first == 0) {
633 return NGHTTP2_ERR_DEFERRED;
639 rv = nghttp2_submit_response(session_, stream.get_stream_id(), nva.data(),
649 void http2_handler::enter_callback() {
650 assert(!inside_callback_);
651 inside_callback_ = true;
654 void http2_handler::leave_callback() {
655 assert(inside_callback_);
656 inside_callback_ = false;
659 bool http2_handler::inside_callback() const { return inside_callback_; }
661 void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
662 ::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
665 void http2_handler::initiate_write() { writefun_(); }
667 void http2_handler::resume(http2_stream &stream) {
668 nghttp2_session_resume_data(session_, stream.get_stream_id());
671 int http2_handler::push_promise(http2_stream &stream, std::string method,
672 std::string path, std::vector<header> headers) {
675 auto &req = stream.get_request()->impl();
677 auto nva = std::vector<nghttp2_nv>();
678 nva.reserve(5 + headers.size());
679 nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
680 nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.scheme()));
681 if (!req.authority().empty()) {
682 nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.authority()));
684 nva.push_back(nghttp2::http2::make_nv_ls(":path", path));
685 if (!req.host().empty()) {
686 nva.push_back(nghttp2::http2::make_nv_ls("host", req.host()));
689 for (auto &hd : headers) {
690 nva.push_back(nghttp2::http2::make_nv(hd.name, hd.value));
693 rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
694 stream.get_stream_id(), nva.data(),
695 nva.size(), nullptr);
701 auto promised_stream = create_stream(rv);
702 auto &promised_req = promised_stream->get_request()->impl();
703 promised_req.pushed(true);
704 promised_req.method(std::move(method));
705 promised_req.scheme(req.scheme());
706 promised_req.authority(req.authority());
707 promised_req.path(std::move(path));
708 promised_req.host(req.host());
709 promised_req.set_header(std::move(headers));
710 if (!req.host().empty()) {
711 promised_req.add_header("host", req.host());
717 bool http2_handler::run_task(thread_cb start) {
718 auto strand = strand_;
721 task_io_service_.post([start, strand]() {
723 chan.impl().strand(strand.get());
729 } catch (std::exception &ex) {
734 boost::asio::io_service &http2_handler::io_service() { return io_service_; }
736 callback_guard::callback_guard(http2_handler &h) : handler(h) {
737 handler.enter_callback();
740 callback_guard::~callback_guard() { handler.leave_callback(); }
742 } // namespace server
744 } // namespace asio_http2
746 } // namespace nghttp2