Imported Upstream version 1.0.0
[platform/upstream/nghttp2.git] / src / asio_server_http2_handler.cc
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 {
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 {
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 {
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     req.header().emplace(std::string(name, name + namelen),
109                          header_value{std::string(value, value + valuelen),
110                                       (flags & NGHTTP2_NV_FLAG_NO_INDEX) != 0});
111   }
112
113   return 0;
114 }
115 } // namespace
116
117 namespace {
118 int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
119                            void *user_data) {
120   auto handler = static_cast<http2_handler *>(user_data);
121   auto strm = handler->find_stream(frame->hd.stream_id);
122
123   switch (frame->hd.type) {
124   case NGHTTP2_DATA:
125     if (!strm) {
126       break;
127     }
128
129     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
130       strm->request().impl().call_on_data(nullptr, 0);
131     }
132
133     break;
134   case NGHTTP2_HEADERS: {
135     if (!strm || frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
136       break;
137     }
138
139     handler->call_on_request(*strm);
140
141     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
142       strm->request().impl().call_on_data(nullptr, 0);
143     }
144
145     break;
146   }
147   }
148
149   return 0;
150 }
151 } // namespace
152
153 namespace {
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);
159
160   if (!strm) {
161     return 0;
162   }
163
164   strm->request().impl().call_on_data(data, len);
165
166   return 0;
167 }
168
169 } // namespace
170
171 namespace {
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);
175
176   auto strm = handler->find_stream(stream_id);
177   if (!strm) {
178     return 0;
179   }
180
181   strm->response().impl().call_on_close(error_code);
182
183   handler->close_stream(stream_id);
184
185   return 0;
186 }
187 } // namespace
188
189 namespace {
190 int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
191                            void *user_data) {
192   auto handler = static_cast<http2_handler *>(user_data);
193
194   if (frame->hd.type != NGHTTP2_PUSH_PROMISE) {
195     return 0;
196   }
197
198   auto strm = handler->find_stream(frame->push_promise.promised_stream_id);
199
200   if (!strm) {
201     return 0;
202   }
203
204   auto &res = strm->response().impl();
205   res.push_promise_sent();
206
207   return 0;
208 }
209 } // namespace
210
211 namespace {
212 int on_frame_not_send_callback(nghttp2_session *session,
213                                const nghttp2_frame *frame, int lib_error_code,
214                                void *user_data) {
215   if (frame->hd.type != NGHTTP2_HEADERS) {
216     return 0;
217   }
218
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);
222
223   return 0;
224 }
225 } // namespace
226
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_)) {}
233
234 http2_handler::~http2_handler() { nghttp2_session_del(session_); }
235
236 const std::string &http2_handler::http_date() {
237   auto t = time(nullptr);
238   if (t != tstamp_cached_) {
239     tstamp_cached_ = t;
240     formatted_date_ = util::http_date(t);
241   }
242   return formatted_date_;
243 }
244
245 int http2_handler::start() {
246   int rv;
247
248   nghttp2_session_callbacks *callbacks;
249   rv = nghttp2_session_callbacks_new(&callbacks);
250   if (rv != 0) {
251     return -1;
252   }
253
254   auto cb_del = defer(nghttp2_session_callbacks_del, callbacks);
255
256   nghttp2_session_callbacks_set_on_begin_headers_callback(
257       callbacks, on_begin_headers_callback);
258   nghttp2_session_callbacks_set_on_header_callback(callbacks,
259                                                    on_header_callback);
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);
270
271   rv = nghttp2_session_server_new(&session_, callbacks, this);
272   if (rv != 0) {
273     return -1;
274   }
275
276   nghttp2_settings_entry ent{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100};
277   nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, &ent, 1);
278
279   return 0;
280 }
281
282 stream *http2_handler::create_stream(int32_t stream_id) {
283   auto p = streams_.emplace(stream_id, make_unique<stream>(this, stream_id));
284   assert(p.second);
285   return (*p.first).second.get();
286 }
287
288 void http2_handler::close_stream(int32_t stream_id) {
289   streams_.erase(stream_id);
290 }
291
292 stream *http2_handler::find_stream(int32_t stream_id) {
293   auto i = streams_.find(stream_id);
294   if (i == std::end(streams_)) {
295     return nullptr;
296   }
297
298   return (*i).second.get();
299 }
300
301 void http2_handler::call_on_request(stream &strm) {
302   auto cb = mux_.handler(strm.request().impl());
303   cb(strm.request(), strm.response());
304 }
305
306 bool http2_handler::should_stop() const {
307   return !nghttp2_session_want_read(session_) &&
308          !nghttp2_session_want_write(session_);
309 }
310
311 int http2_handler::start_response(stream &strm) {
312   int rv;
313
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));
325   }
326
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;
331     prd.read_callback =
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);
337     };
338     prd_ptr = &prd;
339   }
340   rv = nghttp2_submit_response(session_, strm.get_stream_id(), nva.data(),
341                                nva.size(), prd_ptr);
342
343   if (rv != 0) {
344     return -1;
345   }
346
347   signal_write();
348
349   return 0;
350 }
351
352 int http2_handler::submit_trailer(stream &strm, header_map h) {
353   int rv;
354   auto nva = std::vector<nghttp2_nv>();
355   nva.reserve(h.size());
356   for (auto &hd : h) {
357     nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
358                                           hd.second.sensitive));
359   }
360
361   rv = nghttp2_submit_trailer(session_, strm.get_stream_id(), nva.data(),
362                               nva.size());
363
364   if (rv != 0) {
365     return -1;
366   }
367
368   signal_write();
369
370   return 0;
371 }
372
373 void http2_handler::enter_callback() {
374   assert(!inside_callback_);
375   inside_callback_ = true;
376 }
377
378 void http2_handler::leave_callback() {
379   assert(inside_callback_);
380   inside_callback_ = false;
381 }
382
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);
385   signal_write();
386 }
387
388 void http2_handler::signal_write() {
389   if (!inside_callback_) {
390     initiate_write();
391   }
392 }
393
394 void http2_handler::initiate_write() { writefun_(); }
395
396 void http2_handler::resume(stream &strm) {
397   nghttp2_session_resume_data(session_, strm.get_stream_id());
398   signal_write();
399 }
400
401 response *http2_handler::push_promise(boost::system::error_code &ec,
402                                       stream &strm, std::string method,
403                                       std::string raw_path_query,
404                                       header_map h) {
405   int rv;
406
407   ec.clear();
408
409   auto &req = strm.request().impl();
410
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));
417
418   for (auto &hd : h) {
419     nva.push_back(nghttp2::http2::make_nv(hd.first, hd.second.value,
420                                           hd.second.sensitive));
421   }
422
423   rv = nghttp2_submit_push_promise(session_, NGHTTP2_FLAG_NONE,
424                                    strm.get_stream_id(), nva.data(), nva.size(),
425                                    nullptr);
426
427   if (rv < 0) {
428     ec = make_error_code(static_cast<nghttp2_error>(rv));
429     return nullptr;
430   }
431
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));
436
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));
441
442   auto &promised_res = promised_strm->response().impl();
443   promised_res.pushed(true);
444
445   signal_write();
446
447   return &promised_strm->response();
448 }
449
450 boost::asio::io_service &http2_handler::io_service() { return io_service_; }
451
452 callback_guard::callback_guard(http2_handler &h) : handler(h) {
453   handler.enter_callback();
454 }
455
456 callback_guard::~callback_guard() { handler.leave_callback(); }
457
458 } // namespace server
459
460 } // namespace asio_http2
461
462 } // namespace nghttp2