• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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