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_server_http2_handler.h"
29 #include "asio_common.h"
30 #include "asio_server_serve_mux.h"
31 #include "asio_server_stream.h"
32 #include "asio_server_request_impl.h"
33 #include "asio_server_response_impl.h"
40 namespace asio_http2 {
45 int stream_error(nghttp2_session *session, int32_t stream_id,
46 uint32_t error_code) {
47 return nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, stream_id,
53 int on_begin_headers_callback(nghttp2_session *session,
54 const nghttp2_frame *frame, void *user_data) {
55 auto handler = static_cast<http2_handler *>(user_data);
57 if (frame->hd.type != NGHTTP2_HEADERS ||
58 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
62 handler->create_stream(frame->hd.stream_id);
69 int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
70 const uint8_t *name, size_t namelen,
71 const uint8_t *value, size_t valuelen, uint8_t flags,
73 auto handler = static_cast<http2_handler *>(user_data);
74 auto stream_id = frame->hd.stream_id;
76 if (frame->hd.type != NGHTTP2_HEADERS ||
77 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
81 auto strm = handler->find_stream(stream_id);
86 auto &req = strm->request().impl();
87 auto &uref = req.uri();
89 switch (nghttp2::http2::lookup_token(name, namelen)) {
90 case nghttp2::http2::HD__METHOD:
91 req.method(std::string(value, value + valuelen));
93 case nghttp2::http2::HD__SCHEME:
94 uref.scheme.assign(value, value + valuelen);
96 case nghttp2::http2::HD__AUTHORITY:
97 uref.host.assign(value, value + valuelen);
99 case nghttp2::http2::HD__PATH:
100 split_path(uref, value, value + valuelen);
102 case nghttp2::http2::HD_HOST:
103 if (uref.host.empty()) {
104 uref.host.assign(value, value + valuelen);
108 req.header().emplace(std::string(name, name + namelen),
109 header_value{std::string(value, value + valuelen),
110 (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
118 int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
120 auto handler = static_cast<http2_handler *>(user_data);
121 auto strm = handler->find_stream(frame->hd.stream_id);
123 switch (frame->hd.type) {
129 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
130 strm->request().impl().call_on_data(nullptr, 0);
134 case NGHTTP2_HEADERS: {
135 if (!strm || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
139 handler->call_on_request(*strm);
141 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
142 strm->request().impl().call_on_data(nullptr, 0);
154 int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
155 int32_t stream_id, const uint8_t *data,
156 size_t len, void *user_data) {
157 auto handler = static_cast<http2_handler *>(user_data);
158 auto strm = handler->find_stream(stream_id);
164 strm->request().impl().call_on_data(data, len);
172 int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
173 uint32_t error_code, void *user_data) {
174 auto handler = static_cast<http2_handler *>(user_data);
176 auto strm = handler->find_stream(stream_id);
181 strm->response().impl().call_on_close(error_code);
183 handler->close_stream(stream_id);
190 int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
192 auto handler = static_cast<http2_handler *>(user_data);
194 if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
198 auto strm = handler->find_stream(frame->push_promise.promised_stream_id);
204 auto &res = strm->response().impl();
205 res.push_promise_sent();
212 int on_frame_not_send_callback(nghttp2_session *session,
213 const nghttp2_frame *frame, int lib_error_code,
215 if (frame->hd.type != NGHTTP2_HEADERS) {
219 // Issue RST_STREAM so that stream does not hang around.
220 nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
221 NGHTTP2_INTERNAL_ERROR);
227 http2_handler::http2_handler(boost::asio::io_service &io_service,
228 connection_write writefun, serve_mux &mux)
229 : writefun_(writefun), mux_(mux), io_service_(io_service),
230 session_(nullptr), buf_(nullptr), buflen_(0), inside_callback_(false),
231 tstamp_cached_(time(nullptr)),
232 formatted_date_(util::http_date(tstamp_cached_)) {}
234 http2_handler::~http2_handler() { nghttp2_session_del(session_); }
236 const std::string &http2_handler::http_date() {
237 auto t = time(nullptr);
238 if (t != tstamp_cached_) {
240 formatted_date_ = util::http_date(t);
242 return formatted_date_;
245 int http2_handler::start() {
248 nghttp2_session_callbacks *callbacks;
249 rv = nghttp2_session_callbacks_new(&callbacks);
254 auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
256 nghttp2_session_callbacks_set_on_begin_headers_callback(
257 callbacks, on_begin_headers_callback);
258 nghttp2_session_callbacks_set_on_header_callback(callbacks,
260 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
261 on_frame_recv_callback);
262 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
263 callbacks, on_data_chunk_recv_callback);
264 nghttp2_session_callbacks_set_on_stream_close_callback(
265 callbacks, on_stream_close_callback);
266 nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
267 on_frame_send_callback);
268 nghttp2_session_callbacks_set_on_frame_not_send_callback(
269 callbacks, on_frame_not_send_callback);
271 rv = nghttp2_session_server_new(&session_, callbacks, this);
276 nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
277 nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
282 stream *http2_handler::create_stream(int32_t stream_id) {
283 auto p = streams_.emplace(stream_id, make_unique<stream>(this, stream_id));
285 return (*p.first).second.get();
288 void http2_handler::close_stream(int32_t stream_id) {
289 streams_.erase(stream_id);
292 stream *http2_handler::find_stream(int32_t stream_id) {
293 auto i = streams_.find(stream_id);
294 if (i == std::end(streams_)) {
298 return (*i).second.get();
301 void http2_handler::call_on_request(stream &strm) {
302 auto cb = mux_.handler(strm.request().impl());
303 cb(strm.request(), strm.response());
306 bool http2_handler::should_stop() const {
307 return !nghttp2_session_want_read(session_) &&
308 !nghttp2_session_want_write(session_);
311 int http2_handler::start_response(stream &strm) {
314 auto &res = strm.response().impl();
315 auto &header = res.header();
316 auto nva = std::vector<nghttp2_nv>();
317 nva.reserve(2 + header.size());
318 auto status = util::utos(res.status_code());
319 auto date = http_date();
320 nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
321 nva.push_back(nghttp2::http2::make_nv_ls("date", date));
322 for (auto &hd : header) {
323 nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
324 hd.second.sensitive));
327 nghttp2_data_provider *prd_ptr = nullptr, prd;
328 auto &req = strm.request().impl();
329 if (::nghttp2::http2::expect_response_body(req.method(), res.status_code())) {
330 prd.source.ptr = &strm;
332 [](nghttp2_session *session, int32_t stream_id, uint8_t *buf,
333 size_t length, uint32_t *data_flags, nghttp2_data_source *source,
334 void *user_data) -> ssize_t {
335 auto &strm = *static_cast<stream *>(source->ptr);
336 return strm.response().impl().call_read(buf, length, data_flags);
340 rv = nghttp2_submit_response(session_, strm.get_stream_id(), nva.data(),
341 nva.size(), prd_ptr);
352 int http2_handler::submit_trailer(stream &strm, header_map h) {
354 auto nva = std::vector<nghttp2_nv>();
355 nva.reserve(h.size());
357 nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
358 hd.second.sensitive));
361 rv = nghttp2_submit_trailer(session_, strm.get_stream_id(), nva.data(),
373 void http2_handler::enter_callback() {
374 assert(!inside_callback_);
375 inside_callback_ = true;
378 void http2_handler::leave_callback() {
379 assert(inside_callback_);
380 inside_callback_ = false;
383 void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
384 ::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
388 void http2_handler::signal_write() {
389 if (!inside_callback_) {
394 void http2_handler::initiate_write() { writefun_(); }
396 void http2_handler::resume(stream &strm) {
397 nghttp2_session_resume_data(session_, strm.get_stream_id());
401 response *http2_handler::push_promise(boost::system::error_code &ec,
402 stream &strm, std::string method,
403 std::string raw_path_query,
409 auto &req = strm.request().impl();
411 auto nva = std::vector<nghttp2_nv>();
412 nva.reserve(4 + h.size());
413 nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
414 nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.uri().scheme));
415 nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.uri().host));
416 nva.push_back(nghttp2::http2::make_nv_ls(":path", raw_path_query));
419 nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
420 hd.second.sensitive));
423 rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
424 strm.get_stream_id(), nva.data(), nva.size(),
428 ec = make_error_code(static_cast<nghttp2_error>(rv));
432 auto promised_strm = create_stream(rv);
433 auto &promised_req = promised_strm->request().impl();
434 promised_req.header(std::move(h));
435 promised_req.method(std::move(method));
437 auto &uref = promised_req.uri();
438 uref.scheme = req.uri().scheme;
439 uref.host = req.uri().host;
440 split_path(uref, std::begin(raw_path_query), std::end(raw_path_query));
442 auto &promised_res = promised_strm->response().impl();
443 promised_res.pushed(true);
447 return &promised_strm->response();
450 boost::asio::io_service &http2_handler::io_service() { return io_service_; }
452 callback_guard::callback_guard(http2_handler &h) : handler(h) {
453 handler.enter_callback();
456 callback_guard::~callback_guard() { handler.leave_callback(); }
458 } // namespace server
460 } // namespace asio_http2
462 } // namespace nghttp2