1 /*
2 * nghttp2 - HTTP/2 C Library
3 *
4 * Copyright (c) 2014 Tatsuhiro Tsujikawa
5 *
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:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
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.
24 */
25 #include "asio_server_http2_handler.h"
26
27 #include <iostream>
28
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"
34 #include "http2.h"
35 #include "util.h"
36 #include "template.h"
37
38 namespace nghttp2 {
39
40 namespace asio_http2 {
41
42 namespace server {
43
44 namespace {
stream_error(nghttp2_session * session,int32_t stream_id,uint32_t error_code)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,
48 error_code);
49 }
50 } // namespace
51
52 namespace {
on_begin_headers_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)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);
56
57 if (frame->hd.type != NGHTTP2_HEADERS ||
58 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
59 return 0;
60 }
61
62 handler->create_stream(frame->hd.stream_id);
63
64 return 0;
65 }
66 } // namespace
67
68 namespace {
on_header_callback(nghttp2_session * session,const nghttp2_frame * frame,const uint8_t * name,size_t namelen,const uint8_t * value,size_t valuelen,uint8_t flags,void * user_data)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,
72 void *user_data) {
73 auto handler = static_cast<http2_handler *>(user_data);
74 auto stream_id = frame->hd.stream_id;
75
76 if (frame->hd.type != NGHTTP2_HEADERS ||
77 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
78 return 0;
79 }
80
81 auto strm = handler->find_stream(stream_id);
82 if (!strm) {
83 return 0;
84 }
85
86 auto &req = strm->request().impl();
87 auto &uref = req.uri();
88
89 switch (nghttp2::http2::lookup_token(name, namelen)) {
90 case nghttp2::http2::HD__METHOD:
91 req.method(std::string(value, value + valuelen));
92 break;
93 case nghttp2::http2::HD__SCHEME:
94 uref.scheme.assign(value, value + valuelen);
95 break;
96 case nghttp2::http2::HD__AUTHORITY:
97 uref.host.assign(value, value + valuelen);
98 break;
99 case nghttp2::http2::HD__PATH:
100 split_path(uref, value, value + valuelen);
101 break;
102 case nghttp2::http2::HD_HOST:
103 if (uref.host.empty()) {
104 uref.host.assign(value, value + valuelen);
105 }
106 // fall through
107 default:
108 if (req.header_buffer_size() + namelen + valuelen > 64_k) {
109 nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
110 NGHTTP2_INTERNAL_ERROR);
111 break;
112 }
113 req.update_header_buffer_size(namelen + valuelen);
114
115 req.header().emplace(std::string(name, name + namelen),
116 header_value{std::string(value, value + valuelen),
117 (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
118 }
119
120 return 0;
121 }
122 } // namespace
123
124 namespace {
on_frame_recv_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)125 int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
126 void *user_data) {
127 auto handler = static_cast<http2_handler *>(user_data);
128 auto strm = handler->find_stream(frame->hd.stream_id);
129
130 switch (frame->hd.type) {
131 case NGHTTP2_DATA:
132 if (!strm) {
133 break;
134 }
135
136 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
137 strm->request().impl().call_on_data(nullptr, 0);
138 }
139
140 break;
141 case NGHTTP2_HEADERS: {
142 if (!strm || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
143 break;
144 }
145
146 auto &req = strm->request().impl();
147 req.remote_endpoint(handler->remote_endpoint());
148
149 handler->call_on_request(*strm);
150
151 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
152 strm->request().impl().call_on_data(nullptr, 0);
153 }
154
155 break;
156 }
157 }
158
159 return 0;
160 }
161 } // namespace
162
163 namespace {
on_data_chunk_recv_callback(nghttp2_session * session,uint8_t flags,int32_t stream_id,const uint8_t * data,size_t len,void * user_data)164 int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
165 int32_t stream_id, const uint8_t *data,
166 size_t len, void *user_data) {
167 auto handler = static_cast<http2_handler *>(user_data);
168 auto strm = handler->find_stream(stream_id);
169
170 if (!strm) {
171 return 0;
172 }
173
174 strm->request().impl().call_on_data(data, len);
175
176 return 0;
177 }
178
179 } // namespace
180
181 namespace {
on_stream_close_callback(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * user_data)182 int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
183 uint32_t error_code, void *user_data) {
184 auto handler = static_cast<http2_handler *>(user_data);
185
186 auto strm = handler->find_stream(stream_id);
187 if (!strm) {
188 return 0;
189 }
190
191 strm->response().impl().call_on_close(error_code);
192
193 handler->close_stream(stream_id);
194
195 return 0;
196 }
197 } // namespace
198
199 namespace {
on_frame_send_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)200 int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
201 void *user_data) {
202 auto handler = static_cast<http2_handler *>(user_data);
203
204 if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
205 return 0;
206 }
207
208 auto strm = handler->find_stream(frame->push_promise.promised_stream_id);
209
210 if (!strm) {
211 return 0;
212 }
213
214 auto &res = strm->response().impl();
215 res.push_promise_sent();
216
217 return 0;
218 }
219 } // namespace
220
221 namespace {
on_frame_not_send_callback(nghttp2_session * session,const nghttp2_frame * frame,int lib_error_code,void * user_data)222 int on_frame_not_send_callback(nghttp2_session *session,
223 const nghttp2_frame *frame, int lib_error_code,
224 void *user_data) {
225 if (frame->hd.type != NGHTTP2_HEADERS) {
226 return 0;
227 }
228
229 // Issue RST_STREAM so that stream does not hang around.
230 nghttp2_submit_rst_stream(session, NGHTTP2_FLAG_NONE, frame->hd.stream_id,
231 NGHTTP2_INTERNAL_ERROR);
232
233 return 0;
234 }
235 } // namespace
236
http2_handler(boost::asio::io_service & io_service,boost::asio::ip::tcp::endpoint ep,connection_write writefun,serve_mux & mux)237 http2_handler::http2_handler(boost::asio::io_service &io_service,
238 boost::asio::ip::tcp::endpoint ep,
239 connection_write writefun, serve_mux &mux)
240 : writefun_(writefun),
241 mux_(mux),
242 io_service_(io_service),
243 remote_ep_(ep),
244 session_(nullptr),
245 buf_(nullptr),
246 buflen_(0),
247 inside_callback_(false),
248 write_signaled_(false),
249 tstamp_cached_(time(nullptr)),
250 formatted_date_(util::http_date(tstamp_cached_)) {}
251
~http2_handler()252 http2_handler::~http2_handler() {
253 for (auto &p : streams_) {
254 auto &strm = p.second;
255 strm->response().impl().call_on_close(NGHTTP2_INTERNAL_ERROR);
256 }
257
258 nghttp2_session_del(session_);
259 }
260
http_date()261 const std::string &http2_handler::http_date() {
262 auto t = time(nullptr);
263 if (t != tstamp_cached_) {
264 tstamp_cached_ = t;
265 formatted_date_ = util::http_date(t);
266 }
267 return formatted_date_;
268 }
269
start()270 int http2_handler::start() {
271 int rv;
272
273 nghttp2_session_callbacks *callbacks;
274 rv = nghttp2_session_callbacks_new(&callbacks);
275 if (rv != 0) {
276 return -1;
277 }
278
279 auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
280
281 nghttp2_session_callbacks_set_on_begin_headers_callback(
282 callbacks, on_begin_headers_callback);
283 nghttp2_session_callbacks_set_on_header_callback(callbacks,
284 on_header_callback);
285 nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
286 on_frame_recv_callback);
287 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
288 callbacks, on_data_chunk_recv_callback);
289 nghttp2_session_callbacks_set_on_stream_close_callback(
290 callbacks, on_stream_close_callback);
291 nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
292 on_frame_send_callback);
293 nghttp2_session_callbacks_set_on_frame_not_send_callback(
294 callbacks, on_frame_not_send_callback);
295
296 rv = nghttp2_session_server_new(&session_, callbacks, this);
297 if (rv != 0) {
298 return -1;
299 }
300
301 nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
302 nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
303
304 return 0;
305 }
306
create_stream(int32_t stream_id)307 stream *http2_handler::create_stream(int32_t stream_id) {
308 auto p =
309 streams_.emplace(stream_id, std::make_unique<stream>(this, stream_id));
310 assert(p.second);
311 return (*p.first).second.get();
312 }
313
close_stream(int32_t stream_id)314 void http2_handler::close_stream(int32_t stream_id) {
315 streams_.erase(stream_id);
316 }
317
find_stream(int32_t stream_id)318 stream *http2_handler::find_stream(int32_t stream_id) {
319 auto i = streams_.find(stream_id);
320 if (i == std::end(streams_)) {
321 return nullptr;
322 }
323
324 return (*i).second.get();
325 }
326
call_on_request(stream & strm)327 void http2_handler::call_on_request(stream &strm) {
328 auto cb = mux_.handler(strm.request().impl());
329 cb(strm.request(), strm.response());
330 }
331
should_stop() const332 bool http2_handler::should_stop() const {
333 return !nghttp2_session_want_read(session_) &&
334 !nghttp2_session_want_write(session_);
335 }
336
start_response(stream & strm)337 int http2_handler::start_response(stream &strm) {
338 int rv;
339
340 auto &res = strm.response().impl();
341 auto &header = res.header();
342 auto nva = std::vector<nghttp2_nv>();
343 nva.reserve(2 + header.size());
344 auto status = util::utos(res.status_code());
345 auto date = http_date();
346 nva.push_back(nghttp2::http2::make_nv_ls(":status", status));
347 nva.push_back(nghttp2::http2::make_nv_ls("date", date));
348 for (auto &hd : header) {
349 nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
350 hd.second.sensitive));
351 }
352
353 nghttp2_data_provider *prd_ptr = nullptr, prd;
354 auto &req = strm.request().impl();
355 if (::nghttp2::http2::expect_response_body(req.method(), res.status_code())) {
356 prd.source.ptr = &strm;
357 prd.read_callback = [](nghttp2_session *session, int32_t stream_id,
358 uint8_t *buf, size_t length, uint32_t *data_flags,
359 nghttp2_data_source *source,
360 void *user_data) -> ssize_t {
361 auto &strm = *static_cast<stream *>(source->ptr);
362 return strm.response().impl().call_read(buf, length, data_flags);
363 };
364 prd_ptr = &prd;
365 }
366 rv = nghttp2_submit_response(session_, strm.get_stream_id(), nva.data(),
367 nva.size(), prd_ptr);
368
369 if (rv != 0) {
370 return -1;
371 }
372
373 signal_write();
374
375 return 0;
376 }
377
submit_trailer(stream & strm,header_map h)378 int http2_handler::submit_trailer(stream &strm, header_map h) {
379 int rv;
380 auto nva = std::vector<nghttp2_nv>();
381 nva.reserve(h.size());
382 for (auto &hd : h) {
383 nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
384 hd.second.sensitive));
385 }
386
387 rv = nghttp2_submit_trailer(session_, strm.get_stream_id(), nva.data(),
388 nva.size());
389
390 if (rv != 0) {
391 return -1;
392 }
393
394 signal_write();
395
396 return 0;
397 }
398
enter_callback()399 void http2_handler::enter_callback() {
400 assert(!inside_callback_);
401 inside_callback_ = true;
402 }
403
leave_callback()404 void http2_handler::leave_callback() {
405 assert(inside_callback_);
406 inside_callback_ = false;
407 }
408
stream_error(int32_t stream_id,uint32_t error_code)409 void http2_handler::stream_error(int32_t stream_id, uint32_t error_code) {
410 ::nghttp2::asio_http2::server::stream_error(session_, stream_id, error_code);
411 signal_write();
412 }
413
signal_write()414 void http2_handler::signal_write() {
415 if (!inside_callback_ && !write_signaled_) {
416 write_signaled_ = true;
417 auto self = shared_from_this();
418 io_service_.post([self]() { self->initiate_write(); });
419 }
420 }
421
initiate_write()422 void http2_handler::initiate_write() {
423 write_signaled_ = false;
424 writefun_();
425 }
426
resume(stream & strm)427 void http2_handler::resume(stream &strm) {
428 nghttp2_session_resume_data(session_, strm.get_stream_id());
429 signal_write();
430 }
431
push_promise(boost::system::error_code & ec,stream & strm,std::string method,std::string raw_path_query,header_map h)432 response *http2_handler::push_promise(boost::system::error_code &ec,
433 stream &strm, std::string method,
434 std::string raw_path_query,
435 header_map h) {
436 int rv;
437
438 ec.clear();
439
440 auto &req = strm.request().impl();
441
442 auto nva = std::vector<nghttp2_nv>();
443 nva.reserve(4 + h.size());
444 nva.push_back(nghttp2::http2::make_nv_ls(":method", method));
445 nva.push_back(nghttp2::http2::make_nv_ls(":scheme", req.uri().scheme));
446 nva.push_back(nghttp2::http2::make_nv_ls(":authority", req.uri().host));
447 nva.push_back(nghttp2::http2::make_nv_ls(":path", raw_path_query));
448
449 for (auto &hd : h) {
450 nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
451 hd.second.sensitive));
452 }
453
454 rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
455 strm.get_stream_id(), nva.data(), nva.size(),
456 nullptr);
457
458 if (rv < 0) {
459 ec = make_error_code(static_cast<nghttp2_error>(rv));
460 return nullptr;
461 }
462
463 auto promised_strm = create_stream(rv);
464 auto &promised_req = promised_strm->request().impl();
465 promised_req.header(std::move(h));
466 promised_req.method(std::move(method));
467
468 auto &uref = promised_req.uri();
469 uref.scheme = req.uri().scheme;
470 uref.host = req.uri().host;
471 split_path(uref, std::begin(raw_path_query), std::end(raw_path_query));
472
473 auto &promised_res = promised_strm->response().impl();
474 promised_res.pushed(true);
475
476 signal_write();
477
478 return &promised_strm->response();
479 }
480
io_service()481 boost::asio::io_service &http2_handler::io_service() { return io_service_; }
482
remote_endpoint()483 const boost::asio::ip::tcp::endpoint &http2_handler::remote_endpoint() {
484 return remote_ep_;
485 }
486
callback_guard(http2_handler & h)487 callback_guard::callback_guard(http2_handler &h) : handler(h) {
488 handler.enter_callback();
489 }
490
~callback_guard()491 callback_guard::~callback_guard() { handler.leave_callback(); }
492
493 } // namespace server
494
495 } // namespace asio_http2
496
497 } // namespace nghttp2
498