• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2016 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 "shrpx_live_check.h"
26 #include "shrpx_worker.h"
27 #include "shrpx_connect_blocker.h"
28 #include "shrpx_tls.h"
29 #include "shrpx_log.h"
30 
31 namespace shrpx {
32 
33 namespace {
34 constexpr size_t MAX_BUFFER_SIZE = 4_k;
35 } // namespace
36 
37 namespace {
readcb(struct ev_loop * loop,ev_io * w,int revents)38 void readcb(struct ev_loop *loop, ev_io *w, int revents) {
39   int rv;
40   auto conn = static_cast<Connection *>(w->data);
41   auto live_check = static_cast<LiveCheck *>(conn->data);
42 
43   rv = live_check->do_read();
44   if (rv != 0) {
45     live_check->on_failure();
46     return;
47   }
48 }
49 } // namespace
50 
51 namespace {
writecb(struct ev_loop * loop,ev_io * w,int revents)52 void writecb(struct ev_loop *loop, ev_io *w, int revents) {
53   int rv;
54   auto conn = static_cast<Connection *>(w->data);
55   auto live_check = static_cast<LiveCheck *>(conn->data);
56 
57   rv = live_check->do_write();
58   if (rv != 0) {
59     live_check->on_failure();
60     return;
61   }
62 }
63 } // namespace
64 
65 namespace {
timeoutcb(struct ev_loop * loop,ev_timer * w,int revents)66 void timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
67   auto conn = static_cast<Connection *>(w->data);
68   auto live_check = static_cast<LiveCheck *>(conn->data);
69 
70   if (w == &conn->rt && !conn->expired_rt()) {
71     return;
72   }
73 
74   live_check->on_failure();
75 }
76 } // namespace
77 
78 namespace {
backoff_timeoutcb(struct ev_loop * loop,ev_timer * w,int revents)79 void backoff_timeoutcb(struct ev_loop *loop, ev_timer *w, int revents) {
80   int rv;
81   auto live_check = static_cast<LiveCheck *>(w->data);
82 
83   rv = live_check->initiate_connection();
84   if (rv != 0) {
85     live_check->on_failure();
86     return;
87   }
88 }
89 } // namespace
90 
91 namespace {
settings_timeout_cb(struct ev_loop * loop,ev_timer * w,int revents)92 void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
93   auto live_check = static_cast<LiveCheck *>(w->data);
94 
95   if (LOG_ENABLED(INFO)) {
96     LOG(INFO) << "SETTINGS timeout";
97   }
98 
99   live_check->on_failure();
100 }
101 } // namespace
102 
LiveCheck(struct ev_loop * loop,SSL_CTX * ssl_ctx,Worker * worker,DownstreamAddr * addr,std::mt19937 & gen)103 LiveCheck::LiveCheck(struct ev_loop *loop, SSL_CTX *ssl_ctx, Worker *worker,
104                      DownstreamAddr *addr, std::mt19937 &gen)
105     : conn_(loop, -1, nullptr, worker->get_mcpool(),
106             worker->get_downstream_config()->timeout.write,
107             worker->get_downstream_config()->timeout.read, {}, {}, writecb,
108             readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold,
109             get_config()->tls.dyn_rec.idle_timeout, Proto::NONE),
110       wb_(worker->get_mcpool()),
111       gen_(gen),
112       read_(&LiveCheck::noop),
113       write_(&LiveCheck::noop),
114       worker_(worker),
115       ssl_ctx_(ssl_ctx),
116       addr_(addr),
117       session_(nullptr),
118       raddr_(nullptr),
119       success_count_(0),
120       fail_count_(0),
121       settings_ack_received_(false),
122       session_closing_(false) {
123   ev_timer_init(&backoff_timer_, backoff_timeoutcb, 0., 0.);
124   backoff_timer_.data = this;
125 
126   // SETTINGS ACK must be received in a short timeout.  Otherwise, we
127   // assume that connection is broken.
128   ev_timer_init(&settings_timer_, settings_timeout_cb, 0., 0.);
129   settings_timer_.data = this;
130 }
131 
~LiveCheck()132 LiveCheck::~LiveCheck() {
133   disconnect();
134 
135   ev_timer_stop(conn_.loop, &backoff_timer_);
136 }
137 
disconnect()138 void LiveCheck::disconnect() {
139   if (dns_query_) {
140     auto dns_tracker = worker_->get_dns_tracker();
141 
142     dns_tracker->cancel(dns_query_.get());
143   }
144 
145   dns_query_.reset();
146   // We can reuse resolved_addr_
147   raddr_ = nullptr;
148 
149   conn_.rlimit.stopw();
150   conn_.wlimit.stopw();
151 
152   ev_timer_stop(conn_.loop, &settings_timer_);
153 
154   read_ = write_ = &LiveCheck::noop;
155 
156   conn_.disconnect();
157 
158   nghttp2_session_del(session_);
159   session_ = nullptr;
160 
161   settings_ack_received_ = false;
162   session_closing_ = false;
163 
164   wb_.reset();
165 }
166 
167 // Use the similar backoff algorithm described in
168 // https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md
169 namespace {
170 constexpr size_t MAX_BACKOFF_EXP = 10;
171 constexpr auto MULTIPLIER = 1.6;
172 constexpr auto JITTER = 0.2;
173 } // namespace
174 
schedule()175 void LiveCheck::schedule() {
176   auto base_backoff =
177       util::int_pow(MULTIPLIER, std::min(fail_count_, MAX_BACKOFF_EXP));
178   auto dist = std::uniform_real_distribution<>(-JITTER * base_backoff,
179                                                JITTER * base_backoff);
180 
181   auto &downstreamconf = *get_config()->conn.downstream;
182 
183   auto backoff =
184       std::min(downstreamconf.timeout.max_backoff, base_backoff + dist(gen_));
185 
186   ev_timer_set(&backoff_timer_, backoff, 0.);
187   ev_timer_start(conn_.loop, &backoff_timer_);
188 }
189 
do_read()190 int LiveCheck::do_read() { return read_(*this); }
191 
do_write()192 int LiveCheck::do_write() { return write_(*this); }
193 
initiate_connection()194 int LiveCheck::initiate_connection() {
195   int rv;
196 
197   auto worker_blocker = worker_->get_connect_blocker();
198   if (worker_blocker->blocked()) {
199     if (LOG_ENABLED(INFO)) {
200       LOG(INFO) << "Worker wide backend connection was blocked temporarily";
201     }
202     return -1;
203   }
204 
205   if (!dns_query_ && addr_->tls) {
206     assert(ssl_ctx_);
207 
208     auto ssl = tls::create_ssl(ssl_ctx_);
209     if (!ssl) {
210       return -1;
211     }
212 
213     switch (addr_->proto) {
214     case Proto::HTTP1:
215       tls::setup_downstream_http1_alpn(ssl);
216       break;
217     case Proto::HTTP2:
218       tls::setup_downstream_http2_alpn(ssl);
219       break;
220     default:
221       assert(0);
222     }
223 
224     conn_.set_ssl(ssl);
225     conn_.tls.client_session_cache = &addr_->tls_session_cache;
226   }
227 
228   if (addr_->dns) {
229     if (!dns_query_) {
230       auto dns_query = std::make_unique<DNSQuery>(
231           addr_->host, [this](DNSResolverStatus status, const Address *result) {
232             int rv;
233 
234             if (status == DNSResolverStatus::OK) {
235               *this->resolved_addr_ = *result;
236             }
237             rv = this->initiate_connection();
238             if (rv != 0) {
239               this->on_failure();
240             }
241           });
242       auto dns_tracker = worker_->get_dns_tracker();
243 
244       if (!resolved_addr_) {
245         resolved_addr_ = std::make_unique<Address>();
246       }
247 
248       switch (dns_tracker->resolve(resolved_addr_.get(), dns_query.get())) {
249       case DNSResolverStatus::ERROR:
250         return -1;
251       case DNSResolverStatus::RUNNING:
252         dns_query_ = std::move(dns_query);
253         return 0;
254       case DNSResolverStatus::OK:
255         break;
256       default:
257         assert(0);
258       }
259     } else {
260       switch (dns_query_->status) {
261       case DNSResolverStatus::ERROR:
262         dns_query_.reset();
263         return -1;
264       case DNSResolverStatus::OK:
265         dns_query_.reset();
266         break;
267       default:
268         assert(0);
269       }
270     }
271 
272     util::set_port(*resolved_addr_, addr_->port);
273     raddr_ = resolved_addr_.get();
274   } else {
275     raddr_ = &addr_->addr;
276   }
277 
278   conn_.fd = util::create_nonblock_socket(raddr_->su.storage.ss_family);
279 
280   if (conn_.fd == -1) {
281     auto error = errno;
282     LOG(WARN) << "socket() failed; addr=" << util::to_numeric_addr(raddr_)
283               << ", errno=" << error;
284     return -1;
285   }
286 
287   rv = connect(conn_.fd, &raddr_->su.sa, raddr_->len);
288   if (rv != 0 && errno != EINPROGRESS) {
289     auto error = errno;
290     LOG(WARN) << "connect() failed; addr=" << util::to_numeric_addr(raddr_)
291               << ", errno=" << error;
292 
293     close(conn_.fd);
294     conn_.fd = -1;
295 
296     return -1;
297   }
298 
299   if (addr_->tls) {
300     auto sni_name =
301         addr_->sni.empty() ? StringRef{addr_->host} : StringRef{addr_->sni};
302     if (!util::numeric_host(sni_name.c_str())) {
303       SSL_set_tlsext_host_name(conn_.tls.ssl, sni_name.c_str());
304     }
305 
306     auto session = tls::reuse_tls_session(addr_->tls_session_cache);
307     if (session) {
308       SSL_set_session(conn_.tls.ssl, session);
309       SSL_SESSION_free(session);
310     }
311 
312     conn_.prepare_client_handshake();
313   }
314 
315   write_ = &LiveCheck::connected;
316 
317   ev_io_set(&conn_.wev, conn_.fd, EV_WRITE);
318   ev_io_set(&conn_.rev, conn_.fd, EV_READ);
319 
320   conn_.wlimit.startw();
321 
322   auto &downstreamconf = *get_config()->conn.downstream;
323 
324   conn_.wt.repeat = downstreamconf.timeout.connect;
325   ev_timer_again(conn_.loop, &conn_.wt);
326 
327   return 0;
328 }
329 
connected()330 int LiveCheck::connected() {
331   auto sock_error = util::get_socket_error(conn_.fd);
332   if (sock_error != 0) {
333     if (LOG_ENABLED(INFO)) {
334       LOG(INFO) << "Backend connect failed; addr="
335                 << util::to_numeric_addr(raddr_) << ": errno=" << sock_error;
336     }
337 
338     return -1;
339   }
340 
341   if (LOG_ENABLED(INFO)) {
342     LOG(INFO) << "Connection established";
343   }
344 
345   auto &downstreamconf = *get_config()->conn.downstream;
346 
347   // Reset timeout for write.  Previously, we set timeout for connect.
348   conn_.wt.repeat = downstreamconf.timeout.write;
349   ev_timer_again(conn_.loop, &conn_.wt);
350 
351   conn_.rlimit.startw();
352   conn_.again_rt();
353 
354   if (conn_.tls.ssl) {
355     read_ = &LiveCheck::tls_handshake;
356     write_ = &LiveCheck::tls_handshake;
357 
358     return do_write();
359   }
360 
361   if (addr_->proto == Proto::HTTP2) {
362     // For HTTP/2, we try to read SETTINGS ACK from server to make
363     // sure it is really alive, and serving HTTP/2.
364     read_ = &LiveCheck::read_clear;
365     write_ = &LiveCheck::write_clear;
366 
367     if (connection_made() != 0) {
368       return -1;
369     }
370 
371     return 0;
372   }
373 
374   on_success();
375 
376   return 0;
377 }
378 
tls_handshake()379 int LiveCheck::tls_handshake() {
380   conn_.last_read = std::chrono::steady_clock::now();
381 
382   ERR_clear_error();
383 
384   auto rv = conn_.tls_handshake();
385 
386   if (rv == SHRPX_ERR_INPROGRESS) {
387     return 0;
388   }
389 
390   if (rv < 0) {
391     return rv;
392   }
393 
394   if (LOG_ENABLED(INFO)) {
395     LOG(INFO) << "SSL/TLS handshake completed";
396   }
397 
398   if (!get_config()->tls.insecure &&
399       tls::check_cert(conn_.tls.ssl, addr_, raddr_) != 0) {
400     return -1;
401   }
402 
403   // Check negotiated ALPN
404 
405   const unsigned char *next_proto = nullptr;
406   unsigned int next_proto_len = 0;
407 
408 #ifndef OPENSSL_NO_NEXTPROTONEG
409   SSL_get0_next_proto_negotiated(conn_.tls.ssl, &next_proto, &next_proto_len);
410 #endif // !OPENSSL_NO_NEXTPROTONEG
411 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
412   if (next_proto == nullptr) {
413     SSL_get0_alpn_selected(conn_.tls.ssl, &next_proto, &next_proto_len);
414   }
415 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
416 
417   auto proto = StringRef{next_proto, next_proto_len};
418 
419   switch (addr_->proto) {
420   case Proto::HTTP1:
421     if (proto.empty() || proto == StringRef::from_lit("http/1.1")) {
422       break;
423     }
424     return -1;
425   case Proto::HTTP2:
426     if (util::check_h2_is_selected(proto)) {
427       // For HTTP/2, we try to read SETTINGS ACK from server to make
428       // sure it is really alive, and serving HTTP/2.
429       read_ = &LiveCheck::read_tls;
430       write_ = &LiveCheck::write_tls;
431 
432       if (connection_made() != 0) {
433         return -1;
434       }
435 
436       return 0;
437     }
438     return -1;
439   default:
440     break;
441   }
442 
443   on_success();
444 
445   return 0;
446 }
447 
read_tls()448 int LiveCheck::read_tls() {
449   conn_.last_read = std::chrono::steady_clock::now();
450 
451   std::array<uint8_t, 4_k> buf;
452 
453   ERR_clear_error();
454 
455   for (;;) {
456     auto nread = conn_.read_tls(buf.data(), buf.size());
457 
458     if (nread == 0) {
459       return 0;
460     }
461 
462     if (nread < 0) {
463       return nread;
464     }
465 
466     if (on_read(buf.data(), nread) != 0) {
467       return -1;
468     }
469   }
470 }
471 
write_tls()472 int LiveCheck::write_tls() {
473   conn_.last_read = std::chrono::steady_clock::now();
474 
475   ERR_clear_error();
476 
477   struct iovec iov;
478 
479   for (;;) {
480     if (wb_.rleft() > 0) {
481       auto iovcnt = wb_.riovec(&iov, 1);
482       if (iovcnt != 1) {
483         assert(0);
484         return -1;
485       }
486       auto nwrite = conn_.write_tls(iov.iov_base, iov.iov_len);
487 
488       if (nwrite == 0) {
489         return 0;
490       }
491 
492       if (nwrite < 0) {
493         return nwrite;
494       }
495 
496       wb_.drain(nwrite);
497 
498       continue;
499     }
500 
501     if (on_write() != 0) {
502       return -1;
503     }
504 
505     if (wb_.rleft() == 0) {
506       conn_.start_tls_write_idle();
507       break;
508     }
509   }
510 
511   conn_.wlimit.stopw();
512   ev_timer_stop(conn_.loop, &conn_.wt);
513 
514   if (settings_ack_received_) {
515     on_success();
516   }
517 
518   return 0;
519 }
520 
read_clear()521 int LiveCheck::read_clear() {
522   conn_.last_read = std::chrono::steady_clock::now();
523 
524   std::array<uint8_t, 4_k> buf;
525 
526   for (;;) {
527     auto nread = conn_.read_clear(buf.data(), buf.size());
528 
529     if (nread == 0) {
530       return 0;
531     }
532 
533     if (nread < 0) {
534       return nread;
535     }
536 
537     if (on_read(buf.data(), nread) != 0) {
538       return -1;
539     }
540   }
541 }
542 
write_clear()543 int LiveCheck::write_clear() {
544   conn_.last_read = std::chrono::steady_clock::now();
545 
546   struct iovec iov;
547 
548   for (;;) {
549     if (wb_.rleft() > 0) {
550       auto iovcnt = wb_.riovec(&iov, 1);
551       if (iovcnt != 1) {
552         assert(0);
553         return -1;
554       }
555       auto nwrite = conn_.write_clear(iov.iov_base, iov.iov_len);
556 
557       if (nwrite == 0) {
558         return 0;
559       }
560 
561       if (nwrite < 0) {
562         return nwrite;
563       }
564 
565       wb_.drain(nwrite);
566 
567       continue;
568     }
569 
570     if (on_write() != 0) {
571       return -1;
572     }
573 
574     if (wb_.rleft() == 0) {
575       break;
576     }
577   }
578 
579   conn_.wlimit.stopw();
580   ev_timer_stop(conn_.loop, &conn_.wt);
581 
582   if (settings_ack_received_) {
583     on_success();
584   }
585 
586   return 0;
587 }
588 
on_read(const uint8_t * data,size_t len)589 int LiveCheck::on_read(const uint8_t *data, size_t len) {
590   ssize_t rv;
591 
592   rv = nghttp2_session_mem_recv(session_, data, len);
593   if (rv < 0) {
594     LOG(ERROR) << "nghttp2_session_mem_recv() returned error: "
595                << nghttp2_strerror(rv);
596     return -1;
597   }
598 
599   if (settings_ack_received_ && !session_closing_) {
600     session_closing_ = true;
601     rv = nghttp2_session_terminate_session(session_, NGHTTP2_NO_ERROR);
602     if (rv != 0) {
603       return -1;
604     }
605   }
606 
607   if (nghttp2_session_want_read(session_) == 0 &&
608       nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
609     if (LOG_ENABLED(INFO)) {
610       LOG(INFO) << "No more read/write for this session";
611     }
612 
613     // If we have SETTINGS ACK already, we treat this success.
614     if (settings_ack_received_) {
615       return 0;
616     }
617 
618     return -1;
619   }
620 
621   signal_write();
622 
623   return 0;
624 }
625 
on_write()626 int LiveCheck::on_write() {
627   for (;;) {
628     const uint8_t *data;
629     auto datalen = nghttp2_session_mem_send(session_, &data);
630 
631     if (datalen < 0) {
632       LOG(ERROR) << "nghttp2_session_mem_send() returned error: "
633                  << nghttp2_strerror(datalen);
634       return -1;
635     }
636     if (datalen == 0) {
637       break;
638     }
639     wb_.append(data, datalen);
640 
641     if (wb_.rleft() >= MAX_BUFFER_SIZE) {
642       break;
643     }
644   }
645 
646   if (nghttp2_session_want_read(session_) == 0 &&
647       nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
648     if (LOG_ENABLED(INFO)) {
649       LOG(INFO) << "No more read/write for this session";
650     }
651 
652     if (settings_ack_received_) {
653       return 0;
654     }
655 
656     return -1;
657   }
658 
659   return 0;
660 }
661 
on_failure()662 void LiveCheck::on_failure() {
663   ++fail_count_;
664 
665   if (LOG_ENABLED(INFO)) {
666     LOG(INFO) << "Liveness check for " << addr_->host << ":" << addr_->port
667               << " failed " << fail_count_ << " time(s) in a row";
668   }
669 
670   disconnect();
671 
672   schedule();
673 }
674 
on_success()675 void LiveCheck::on_success() {
676   ++success_count_;
677   fail_count_ = 0;
678 
679   if (LOG_ENABLED(INFO)) {
680     LOG(INFO) << "Liveness check for " << addr_->host << ":" << addr_->port
681               << " succeeded " << success_count_ << " time(s) in a row";
682   }
683 
684   if (success_count_ < addr_->rise) {
685     disconnect();
686 
687     schedule();
688 
689     return;
690   }
691 
692   LOG(NOTICE) << util::to_numeric_addr(&addr_->addr) << " is considered online";
693 
694   addr_->connect_blocker->online();
695 
696   success_count_ = 0;
697   fail_count_ = 0;
698 
699   disconnect();
700 }
701 
noop()702 int LiveCheck::noop() { return 0; }
703 
start_settings_timer()704 void LiveCheck::start_settings_timer() {
705   auto &downstreamconf = get_config()->http2.downstream;
706 
707   ev_timer_set(&settings_timer_, downstreamconf.timeout.settings, 0.);
708   ev_timer_start(conn_.loop, &settings_timer_);
709 }
710 
stop_settings_timer()711 void LiveCheck::stop_settings_timer() {
712   ev_timer_stop(conn_.loop, &settings_timer_);
713 }
714 
settings_ack_received()715 void LiveCheck::settings_ack_received() { settings_ack_received_ = true; }
716 
717 namespace {
on_frame_send_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)718 int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame,
719                            void *user_data) {
720   auto live_check = static_cast<LiveCheck *>(user_data);
721 
722   if (frame->hd.type != NGHTTP2_SETTINGS ||
723       (frame->hd.flags & NGHTTP2_FLAG_ACK)) {
724     return 0;
725   }
726 
727   live_check->start_settings_timer();
728 
729   return 0;
730 }
731 } // namespace
732 
733 namespace {
on_frame_recv_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)734 int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
735                            void *user_data) {
736   auto live_check = static_cast<LiveCheck *>(user_data);
737 
738   if (frame->hd.type != NGHTTP2_SETTINGS ||
739       (frame->hd.flags & NGHTTP2_FLAG_ACK) == 0) {
740     return 0;
741   }
742 
743   live_check->stop_settings_timer();
744   live_check->settings_ack_received();
745 
746   return 0;
747 }
748 } // namespace
749 
connection_made()750 int LiveCheck::connection_made() {
751   int rv;
752 
753   nghttp2_session_callbacks *callbacks;
754   rv = nghttp2_session_callbacks_new(&callbacks);
755   if (rv != 0) {
756     return -1;
757   }
758 
759   nghttp2_session_callbacks_set_on_frame_send_callback(callbacks,
760                                                        on_frame_send_callback);
761   nghttp2_session_callbacks_set_on_frame_recv_callback(callbacks,
762                                                        on_frame_recv_callback);
763 
764   rv = nghttp2_session_client_new(&session_, callbacks, this);
765 
766   nghttp2_session_callbacks_del(callbacks);
767 
768   if (rv != 0) {
769     return -1;
770   }
771 
772   rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, nullptr, 0);
773   if (rv != 0) {
774     return -1;
775   }
776 
777   auto must_terminate =
778       addr_->tls && !nghttp2::tls::check_http2_requirement(conn_.tls.ssl);
779 
780   if (must_terminate) {
781     if (LOG_ENABLED(INFO)) {
782       LOG(INFO) << "TLSv1.2 was not negotiated. HTTP/2 must not be negotiated.";
783     }
784 
785     rv = nghttp2_session_terminate_session(session_,
786                                            NGHTTP2_INADEQUATE_SECURITY);
787     if (rv != 0) {
788       return -1;
789     }
790   }
791 
792   signal_write();
793 
794   return 0;
795 }
796 
signal_write()797 void LiveCheck::signal_write() { conn_.wlimit.startw(); }
798 
799 } // namespace shrpx
800