nghttp2/src/asio_http2_handler.cc

563 lines
16 KiB
C++
Raw Normal View History

/*
* nghttp2 - HTTP/2 C Library
*
* Copyright (c) 2014 Tatsuhiro Tsujikawa
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "asio_http2_handler.h"
#include <iostream>
2015-03-01 02:02:49 +01:00
#include "asio_common.h"
#include "http2.h"
#include "util.h"
#include "template.h"
namespace nghttp2 {
namespace asio_http2 {
namespace server {
extern std::shared_ptr<std::string> cached_date;
request::request() : impl_(make_unique<request_impl>()) {}
2015-03-03 19:05:57 +01:00
const header_map &request::header() const { return impl_->header(); }
2014-11-27 15:39:04 +01:00
const std::string &request::method() const { return impl_->method(); }
2015-03-04 13:49:53 +01:00
const uri_ref &request::uri() const { return impl_->uri(); }
2015-03-03 19:05:57 +01:00
bool request::push(std::string method, std::string path, header_map h) const {
return impl_->push(std::move(method), std::move(path), std::move(h));
}
2014-11-27 15:39:04 +01:00
bool request::pushed() const { return impl_->pushed(); }
void request::on_data(data_cb cb) const {
return impl_->on_data(std::move(cb));
}
2015-03-03 18:22:40 +01:00
request_impl &request::impl() const { return *impl_; }
response::response() : impl_(make_unique<response_impl>()) {}
2015-03-03 19:05:57 +01:00
void response::write_head(unsigned int status_code, header_map h) const {
impl_->write_head(status_code, std::move(h));
}
void response::end(std::string data) const { impl_->end(std::move(data)); }
void response::end(read_cb cb) const { impl_->end(std::move(cb)); }
void response::resume() const { impl_->resume(); }
2014-11-27 15:39:04 +01:00
unsigned int response::status_code() const { return impl_->status_code(); }
2014-11-27 15:39:04 +01:00
bool response::started() const { return impl_->started(); }
2015-03-03 18:22:40 +01:00
response_impl &response::impl() const { return *impl_; }
request_impl::request_impl() : stream_(nullptr), pushed_(false) {}
2015-03-03 19:05:57 +01:00
const header_map &request_impl::header() const { return header_; }
2014-11-27 15:39:04 +01:00
const std::string &request_impl::method() const { return method_; }
2015-03-04 13:49:53 +01:00
const uri_ref &request_impl::uri() const { return uri_; }
2015-03-04 13:49:53 +01:00
uri_ref &request_impl::uri() { return uri_; }
2015-03-03 19:05:57 +01:00
void request_impl::header(header_map h) { header_ = std::move(h); }
2015-03-03 19:05:57 +01:00
header_map &request_impl::header() { return header_; }
2014-11-27 15:39:04 +01:00
void request_impl::method(std::string arg) { method_ = std::move(arg); }
2015-03-03 19:05:57 +01:00
bool request_impl::push(std::string method, std::string path, header_map h) {
auto handler = stream_->handler();
auto rv = handler->push_promise(*stream_, std::move(method), std::move(path),
2015-03-03 19:05:57 +01:00
std::move(h));
return rv == 0;
}
2014-11-27 15:39:04 +01:00
bool request_impl::pushed() const { return pushed_; }
2014-11-27 15:39:04 +01:00
void request_impl::pushed(bool f) { pushed_ = f; }
2014-11-27 15:39:04 +01:00
void request_impl::on_data(data_cb cb) { on_data_cb_ = std::move(cb); }
void request_impl::stream(http2_stream *s) { stream_ = s; }
2014-11-27 15:39:04 +01:00
void request_impl::call_on_data(const uint8_t *data, std::size_t len) {
if (on_data_cb_) {
on_data_cb_(data, len);
}
}
response_impl::response_impl()
: stream_(nullptr), status_code_(200), started_(false) {}
2014-11-27 15:39:04 +01:00
unsigned int response_impl::status_code() const { return status_code_; }
2015-03-03 19:05:57 +01:00
void response_impl::write_head(unsigned int status_code, header_map h) {
status_code_ = status_code;
2015-03-03 19:05:57 +01:00
header_ = std::move(h);
}
2014-11-27 15:39:04 +01:00
void response_impl::end(std::string data) {
if (started_) {
return;
}
2015-03-01 02:02:49 +01:00
end(string_reader(std::move(data)));
}
2014-11-27 15:39:04 +01:00
void response_impl::end(read_cb cb) {
if (started_) {
return;
}
read_cb_ = std::move(cb);
started_ = true;
auto handler = stream_->handler();
if (handler->start_response(*stream_) != 0) {
handler->stream_error(stream_->get_stream_id(), NGHTTP2_INTERNAL_ERROR);
return;
}
2014-11-27 15:39:04 +01:00
if (!handler->inside_callback()) {
handler->initiate_write();
}
}
2014-11-27 15:39:04 +01:00
void response_impl::resume() {
auto handler = stream_->handler();
handler->resume(*stream_);
2014-11-27 15:39:04 +01:00
if (!handler->inside_callback()) {
handler->initiate_write();
}
}
2014-11-27 15:39:04 +01:00
bool response_impl::started() const { return started_; }
2015-03-03 19:05:57 +01:00
const header_map &response_impl::header() const { return header_; }
void response_impl::stream(http2_stream *s) { stream_ = s; }
read_cb::result_type response_impl::call_read(uint8_t *data, std::size_t len,
uint32_t *data_flags) {
2014-11-27 15:39:04 +01:00
if (read_cb_) {
return read_cb_(data, len, data_flags);
}
*data_flags |= NGHTTP2_DATA_FLAG_EOF;
return 0;
}
http2_stream::http2_stream(http2_handler *h, int32_t stream_id)
: handler_(h), stream_id_(stream_id) {
request_.impl().stream(this);
response_.impl().stream(this);
}
2014-11-27 15:39:04 +01:00
int32_t http2_stream::get_stream_id() const { return stream_id_; }
request &http2_stream::request() { return request_; }
response &http2_stream::response() { return response_; }
http2_handler *http2_stream::handler() const { return handler_; }
namespace {
int stream_error(nghttp2_session *session, int32_t stream_id,
2014-11-27 15:39:04 +01:00
uint32_t error_code) {
return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
error_code);
}
} // namespace
namespace {
int on_begin_headers_callback(nghttp2_session *session,
2014-11-27 15:39:04 +01:00
const nghttp2_frame *frame, void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
2014-11-27 15:39:04 +01:00
if (frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
handler->create_stream(frame->hd.stream_id);
return 0;
}
} // namespace
namespace {
2014-11-27 15:39:04 +01:00
int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
2014-11-27 15:39:04 +01:00
const uint8_t *value, size_t valuelen, uint8_t flags,
void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
auto stream_id = frame->hd.stream_id;
2014-11-27 15:39:04 +01:00
if (frame->hd.type != NGHTTP2_HEADERS ||
frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
return 0;
}
2014-11-27 15:39:04 +01:00
auto stream = handler->find_stream(stream_id);
if (!stream) {
return 0;
}
auto &req = stream->request().impl();
2015-03-04 13:49:53 +01:00
auto &uref = req.uri();
switch (nghttp2::http2::lookup_token(name, namelen)) {
case nghttp2::http2::HD__METHOD:
req.method(std::string(value, value + valuelen));
break;
case nghttp2::http2::HD__SCHEME:
2015-03-04 13:49:53 +01:00
uref.scheme.assign(value, value + valuelen);
break;
case nghttp2::http2::HD__AUTHORITY:
2015-03-04 13:49:53 +01:00
uref.host.assign(value, value + valuelen);
break;
case nghttp2::http2::HD__PATH:
2015-03-04 13:49:53 +01:00
split_path(uref, value, value + valuelen);
break;
case nghttp2::http2::HD_HOST:
2015-03-04 13:49:53 +01:00
if (uref.host.empty()) {
uref.host.assign(value, value + valuelen);
}
// fall through
default:
2015-03-03 19:05:57 +01:00
req.header().emplace(std::string(name, name + namelen),
header_value{std::string(value, value + valuelen),
(flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
}
return 0;
}
} // namespace
namespace {
2014-11-27 15:39:04 +01:00
int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
auto stream = handler->find_stream(frame->hd.stream_id);
2014-11-27 15:39:04 +01:00
switch (frame->hd.type) {
case NGHTTP2_DATA:
2014-11-27 15:39:04 +01:00
if (!stream) {
break;
}
2014-11-27 15:39:04 +01:00
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
stream->request().impl().call_on_data(nullptr, 0);
}
break;
case NGHTTP2_HEADERS: {
2014-11-27 15:39:04 +01:00
if (!stream || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
break;
}
handler->call_on_request(*stream);
2014-11-27 15:39:04 +01:00
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
stream->request().impl().call_on_data(nullptr, 0);
}
break;
}
}
return 0;
}
} // namespace
namespace {
int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
2014-11-27 15:39:04 +01:00
int32_t stream_id, const uint8_t *data,
size_t len, void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
auto stream = handler->find_stream(stream_id);
2014-11-27 15:39:04 +01:00
if (!stream) {
return 0;
}
stream->request().impl().call_on_data(data, len);
return 0;
}
} // namespace
namespace {
2014-11-27 15:39:04 +01:00
int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
uint32_t error_code, void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
handler->close_stream(stream_id);
return 0;
}
} // namespace
namespace {
2014-11-27 15:39:04 +01:00
int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
void *user_data) {
auto handler = static_cast<http2_handler *>(user_data);
2014-11-27 15:39:04 +01:00
if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
return 0;
}
auto stream = handler->find_stream(frame->push_promise.promised_stream_id);
2014-11-27 15:39:04 +01:00
if (!stream) {
return 0;
}
handler->call_on_request(*stream);
return 0;
}
} // namespace
namespace {
2014-11-27 15:39:04 +01:00
int on_frame_not_send_callback(nghttp2_session *session,
const nghttp2_frame *frame, int lib_error_code,
void *user_data) {
if (frame->hd.type != NGHTTP2_HEADERS) {
return 0;
}
// Issue RST_STREAM so that stream does not hang around.
nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
NGHTTP2_INTERNAL_ERROR);
return 0;
}
} // namespace
2014-11-27 15:39:04 +01:00
http2_handler::http2_handler(boost::asio::io_service &io_service,
connection_write writefun, request_cb cb)
: writefun_(writefun), request_cb_(std::move(cb)), io_service_(io_service),
session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false) {}
http2_handler::~http2_handler() { nghttp2_session_del(session_); }
int http2_handler::start() {
int rv;
nghttp2_session_callbacks *callbacks;
rv = nghttp2_session_callbacks_new(&callbacks);
2014-11-27 15:39:04 +01:00
if (rv != 0) {
return -1;
}
2015-02-06 15:27:15 +01:00
auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
2014-11-27 15:39:04 +01:00
nghttp2_session_callbacks_set_on_begin_headers_callback(
callbacks, on_begin_headers_callback);
nghttp2_session_callbacks_set_on_header_callback(callbacks,
on_header_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
on_frame_recv_callback);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
callbacks, on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_stream_close_callback(
callbacks, on_stream_close_callback);
nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
on_frame_send_callback);
nghttp2_session_callbacks_set_on_frame_not_send_callback(
callbacks, on_frame_not_send_callback);
nghttp2_option *option;
rv = nghttp2_option_new(&option);
2014-11-27 15:39:04 +01:00
if (rv != 0) {
return -1;
}
2015-02-06 15:27:15 +01:00
auto opt_del = defer(nghttp2_option_del, option);
nghttp2_option_set_recv_client_preface(option, 1);
rv = nghttp2_session_server_new2(&session_, callbacks, this, option);
2014-11-27 15:39:04 +01:00
if (rv != 0) {
return -1;
}
2014-11-27 15:39:04 +01:00
nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
return 0;
}
http2_stream *http2_handler::create_stream(int32_t stream_id) {
auto p =
streams_.emplace(stream_id, make_unique<http2_stream>(this, stream_id));
assert(p.second);
return (*p.first).second.get();
}
2014-11-27 15:39:04 +01:00
void http2_handler::close_stream(int32_t stream_id) {
streams_.erase(stream_id);
}
http2_stream *http2_handler::find_stream(int32_t stream_id) {
auto i = streams_.find(stream_id);
2014-11-27 15:39:04 +01:00
if (i == std::end(streams_)) {
return nullptr;
}
return (*i).second.get();
}
2014-11-27 15:39:04 +01:00
void http2_handler::call_on_request(http2_stream &stream) {
request_cb_(stream.request(), stream.response());
}
2014-11-27 15:39:04 +01:00
bool http2_handler::should_stop() const {
return !nghttp2_session_want_read(session_) &&
!nghttp2_session_want_write(session_);
}
2014-11-27 15:39:04 +01:00
int http2_handler::start_response(http2_stream &stream) {
int rv;
auto &res = stream.response().impl();
2015-03-03 19:05:57 +01:00
auto &header = res.header();
auto nva = std::vector<nghttp2_nv>();
2015-03-03 19:05:57 +01:00
nva.reserve(2 + header.size());
auto status = util::utos(res.status_code());
auto date = cached_date;
nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
nva.push_back(nghttp2::http2::make_nv_ls("date", *date));
2015-03-03 19:05:57 +01:00
for (auto &hd : header) {
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
hd.second.sensitive));
}
nghttp2_data_provider prd;
prd.source.ptr = &stream;
prd.read_callback =
2014-11-27 15:39:04 +01:00
[](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
size_t length, uint32_t *data_flags, nghttp2_data_source *source,
void *user_data) -> ssize_t {
auto &stream = *static_cast<http2_stream *>(source->ptr);
return stream.response().impl().call_read(buf, length, data_flags);
2014-11-27 15:39:04 +01:00
};
rv = nghttp2_submit_response(session_, stream.get_stream_id(), nva.data(),
nva.size(), &prd);
if (rv != 0) {
return -1;
}
return 0;
}
2014-11-27 15:39:04 +01:00
void http2_handler::enter_callback() {
assert(!inside_callback_);
inside_callback_ = true;
}
2014-11-27 15:39:04 +01:00
void http2_handler::leave_callback() {
assert(inside_callback_);
inside_callback_ = false;
}
2014-11-27 15:39:04 +01:00
bool http2_handler::inside_callback() const { return inside_callback_; }
2014-11-27 15:39:04 +01:00
void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
}
2014-11-27 15:39:04 +01:00
void http2_handler::initiate_write() { writefun_(); }
2014-11-27 15:39:04 +01:00
void http2_handler::resume(http2_stream &stream) {
nghttp2_session_resume_data(session_, stream.get_stream_id());
}
2014-11-27 15:39:04 +01:00
int http2_handler::push_promise(http2_stream &stream, std::string method,
2015-03-04 13:49:53 +01:00
std::string raw_path_query, header_map h) {
int rv;
auto &req = stream.request().impl();
auto nva = std::vector<nghttp2_nv>();
2015-03-04 13:49:53 +01:00
nva.reserve(4 + h.size());
nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
2015-03-04 13:49:53 +01:00
nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.uri().scheme));
nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.uri().host));
nva.push_back(nghttp2::http2::make_nv_ls(":path", raw_path_query));
2015-03-03 19:05:57 +01:00
for (auto &hd : h) {
nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
hd.second.sensitive));
}
rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
2014-11-27 15:39:04 +01:00
stream.get_stream_id(), nva.data(),
nva.size(), nullptr);
2014-11-27 15:39:04 +01:00
if (rv < 0) {
return -1;
}
auto promised_stream = create_stream(rv);
auto &promised_req = promised_stream->request().impl();
promised_req.pushed(true);
2015-03-03 19:05:57 +01:00
promised_req.header(std::move(h));
2015-03-04 13:49:53 +01:00
promised_req.method(std::move(method));
auto &uref = promised_req.uri();
uref.scheme = req.uri().scheme;
uref.host = req.uri().host;
split_path(uref, std::begin(raw_path_query), std::end(raw_path_query));
return 0;
}
2014-11-27 15:39:04 +01:00
boost::asio::io_service &http2_handler::io_service() { return io_service_; }
2014-09-23 17:57:43 +02:00
2014-11-27 15:39:04 +01:00
callback_guard::callback_guard(http2_handler &h) : handler(h) {
handler.enter_callback();
}
2014-11-27 15:39:04 +01:00
callback_guard::~callback_guard() { handler.leave_callback(); }
} // namespace server
} // namespace asio_http2
} // namespace nghttp2