• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * nghttp2 - HTTP/2 C Library
3  *
4  * Copyright (c) 2013 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 "HttpServer.h"
26 
27 #include <sys/stat.h>
28 #ifdef HAVE_SYS_SOCKET_H
29 #  include <sys/socket.h>
30 #endif // HAVE_SYS_SOCKET_H
31 #ifdef HAVE_NETDB_H
32 #  include <netdb.h>
33 #endif // HAVE_NETDB_H
34 #ifdef HAVE_UNISTD_H
35 #  include <unistd.h>
36 #endif // HAVE_UNISTD_H
37 #ifdef HAVE_FCNTL_H
38 #  include <fcntl.h>
39 #endif // HAVE_FCNTL_H
40 #ifdef HAVE_NETINET_IN_H
41 #  include <netinet/in.h>
42 #endif // HAVE_NETINET_IN_H
43 #include <netinet/tcp.h>
44 #ifdef HAVE_ARPA_INET_H
45 #  include <arpa/inet.h>
46 #endif // HAVE_ARPA_INET_H
47 
48 #include <cassert>
49 #include <set>
50 #include <iostream>
51 #include <thread>
52 #include <mutex>
53 #include <deque>
54 
55 #include "ssl_compat.h"
56 
57 #include <openssl/err.h>
58 #include <openssl/dh.h>
59 #if OPENSSL_3_0_0_API
60 #  include <openssl/decoder.h>
61 #endif // OPENSSL_3_0_0_API
62 
63 #include <zlib.h>
64 
65 #include "app_helper.h"
66 #include "http2.h"
67 #include "util.h"
68 #include "tls.h"
69 #include "template.h"
70 
71 #ifndef O_BINARY
72 #  define O_BINARY (0)
73 #endif // O_BINARY
74 
75 using namespace std::chrono_literals;
76 
77 namespace nghttp2 {
78 
79 namespace {
80 // TODO could be constexpr
81 constexpr auto DEFAULT_HTML = "index.html"_sr;
82 constexpr auto NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION ""_sr;
83 } // namespace
84 
85 namespace {
delete_handler(Http2Handler * handler)86 void delete_handler(Http2Handler *handler) {
87   handler->remove_self();
88   delete handler;
89 }
90 } // namespace
91 
92 namespace {
print_session_id(int64_t id)93 void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; }
94 } // namespace
95 
Config()96 Config::Config()
97     : mime_types_file("/etc/mime.types"),
98       stream_read_timeout(1_min),
99       stream_write_timeout(1_min),
100       data_ptr(nullptr),
101       padding(0),
102       num_worker(1),
103       max_concurrent_streams(100),
104       header_table_size(-1),
105       encoder_header_table_size(-1),
106       window_bits(-1),
107       connection_window_bits(-1),
108       port(0),
109       verbose(false),
110       daemon(false),
111       verify_client(false),
112       no_tls(false),
113       error_gzip(false),
114       early_response(false),
115       hexdump(false),
116       echo_upload(false),
117       no_content_length(false),
118       ktls(false),
119       no_rfc7540_pri(false) {}
120 
~Config()121 Config::~Config() {}
122 
123 namespace {
stream_timeout_cb(struct ev_loop * loop,ev_timer * w,int revents)124 void stream_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
125   int rv;
126   auto stream = static_cast<Stream *>(w->data);
127   auto hd = stream->handler;
128   auto config = hd->get_config();
129 
130   ev_timer_stop(hd->get_loop(), &stream->rtimer);
131   ev_timer_stop(hd->get_loop(), &stream->wtimer);
132 
133   if (config->verbose) {
134     print_session_id(hd->session_id());
135     print_timer();
136     std::cout << " timeout stream_id=" << stream->stream_id << std::endl;
137   }
138 
139   hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
140 
141   rv = hd->on_write();
142   if (rv == -1) {
143     delete_handler(hd);
144   }
145 }
146 } // namespace
147 
148 namespace {
add_stream_read_timeout(Stream * stream)149 void add_stream_read_timeout(Stream *stream) {
150   auto hd = stream->handler;
151   ev_timer_again(hd->get_loop(), &stream->rtimer);
152 }
153 } // namespace
154 
155 namespace {
add_stream_read_timeout_if_pending(Stream * stream)156 void add_stream_read_timeout_if_pending(Stream *stream) {
157   auto hd = stream->handler;
158   if (ev_is_active(&stream->rtimer)) {
159     ev_timer_again(hd->get_loop(), &stream->rtimer);
160   }
161 }
162 } // namespace
163 
164 namespace {
add_stream_write_timeout(Stream * stream)165 void add_stream_write_timeout(Stream *stream) {
166   auto hd = stream->handler;
167   ev_timer_again(hd->get_loop(), &stream->wtimer);
168 }
169 } // namespace
170 
171 namespace {
remove_stream_read_timeout(Stream * stream)172 void remove_stream_read_timeout(Stream *stream) {
173   auto hd = stream->handler;
174   ev_timer_stop(hd->get_loop(), &stream->rtimer);
175 }
176 } // namespace
177 
178 namespace {
remove_stream_write_timeout(Stream * stream)179 void remove_stream_write_timeout(Stream *stream) {
180   auto hd = stream->handler;
181   ev_timer_stop(hd->get_loop(), &stream->wtimer);
182 }
183 } // namespace
184 
185 namespace {
186 void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config);
187 } // namespace
188 
189 namespace {
190 constexpr ev_tstamp RELEASE_FD_TIMEOUT = 2.;
191 } // namespace
192 
193 namespace {
194 void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents);
195 } // namespace
196 
197 namespace {
198 constexpr auto FILE_ENTRY_MAX_AGE = 10s;
199 } // namespace
200 
201 namespace {
202 constexpr size_t FILE_ENTRY_EVICT_THRES = 2048;
203 } // namespace
204 
205 namespace {
need_validation_file_entry(const FileEntry * ent,const std::chrono::steady_clock::time_point & now)206 bool need_validation_file_entry(
207     const FileEntry *ent, const std::chrono::steady_clock::time_point &now) {
208   return ent->last_valid + FILE_ENTRY_MAX_AGE < now;
209 }
210 } // namespace
211 
212 namespace {
validate_file_entry(FileEntry * ent,const std::chrono::steady_clock::time_point & now)213 bool validate_file_entry(FileEntry *ent,
214                          const std::chrono::steady_clock::time_point &now) {
215   struct stat stbuf;
216   int rv;
217 
218   rv = fstat(ent->fd, &stbuf);
219   if (rv != 0) {
220     ent->stale = true;
221     return false;
222   }
223 
224   if (stbuf.st_nlink == 0 || ent->mtime != stbuf.st_mtime) {
225     ent->stale = true;
226     return false;
227   }
228 
229   ent->mtime = stbuf.st_mtime;
230   ent->last_valid = now;
231 
232   return true;
233 }
234 } // namespace
235 
236 class Sessions {
237 public:
Sessions(HttpServer * sv,struct ev_loop * loop,const Config * config,SSL_CTX * ssl_ctx)238   Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config,
239            SSL_CTX *ssl_ctx)
240       : sv_(sv),
241         loop_(loop),
242         config_(config),
243         ssl_ctx_(ssl_ctx),
244         callbacks_(nullptr),
245         option_(nullptr),
246         next_session_id_(1),
247         tstamp_cached_(ev_now(loop)),
248         cached_date_(util::http_date(tstamp_cached_)) {
249     nghttp2_session_callbacks_new(&callbacks_);
250 
251     fill_callback(callbacks_, config_);
252 
253     nghttp2_option_new(&option_);
254 
255     if (config_->encoder_header_table_size != -1) {
256       nghttp2_option_set_max_deflate_dynamic_table_size(
257           option_, config_->encoder_header_table_size);
258     }
259 
260     ev_timer_init(&release_fd_timer_, release_fd_cb, 0., RELEASE_FD_TIMEOUT);
261     release_fd_timer_.data = this;
262   }
~Sessions()263   ~Sessions() {
264     ev_timer_stop(loop_, &release_fd_timer_);
265     for (auto handler : handlers_) {
266       delete handler;
267     }
268     nghttp2_option_del(option_);
269     nghttp2_session_callbacks_del(callbacks_);
270   }
add_handler(Http2Handler * handler)271   void add_handler(Http2Handler *handler) { handlers_.insert(handler); }
remove_handler(Http2Handler * handler)272   void remove_handler(Http2Handler *handler) {
273     handlers_.erase(handler);
274     if (handlers_.empty() && !fd_cache_.empty()) {
275       ev_timer_again(loop_, &release_fd_timer_);
276     }
277   }
get_ssl_ctx() const278   SSL_CTX *get_ssl_ctx() const { return ssl_ctx_; }
ssl_session_new(int fd)279   SSL *ssl_session_new(int fd) {
280     SSL *ssl = SSL_new(ssl_ctx_);
281     if (!ssl) {
282       std::cerr << "SSL_new() failed" << std::endl;
283       return nullptr;
284     }
285     if (SSL_set_fd(ssl, fd) == 0) {
286       std::cerr << "SSL_set_fd() failed" << std::endl;
287       SSL_free(ssl);
288       return nullptr;
289     }
290     return ssl;
291   }
get_config() const292   const Config *get_config() const { return config_; }
get_loop() const293   struct ev_loop *get_loop() const { return loop_; }
get_next_session_id()294   int64_t get_next_session_id() {
295     auto session_id = next_session_id_;
296     if (next_session_id_ == std::numeric_limits<int64_t>::max()) {
297       next_session_id_ = 1;
298     } else {
299       ++next_session_id_;
300     }
301     return session_id;
302   }
get_callbacks() const303   const nghttp2_session_callbacks *get_callbacks() const { return callbacks_; }
get_option() const304   const nghttp2_option *get_option() const { return option_; }
accept_connection(int fd)305   void accept_connection(int fd) {
306     util::make_socket_nodelay(fd);
307     SSL *ssl = nullptr;
308     if (ssl_ctx_) {
309       ssl = ssl_session_new(fd);
310       if (!ssl) {
311         close(fd);
312         return;
313       }
314     }
315     auto handler =
316         std::make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
317     if (!ssl) {
318       if (handler->connection_made() != 0) {
319         return;
320       }
321     }
322     add_handler(handler.release());
323   }
update_cached_date()324   void update_cached_date() { cached_date_ = util::http_date(tstamp_cached_); }
get_cached_date()325   const std::string &get_cached_date() {
326     auto t = ev_now(loop_);
327     if (t != tstamp_cached_) {
328       tstamp_cached_ = t;
329       update_cached_date();
330     }
331     return cached_date_;
332   }
get_cached_fd(const std::string & path)333   FileEntry *get_cached_fd(const std::string &path) {
334     auto range = fd_cache_.equal_range(path);
335     if (range.first == range.second) {
336       return nullptr;
337     }
338 
339     auto now = std::chrono::steady_clock::now();
340 
341     for (auto it = range.first; it != range.second;) {
342       auto &ent = (*it).second;
343       if (ent->stale) {
344         ++it;
345         continue;
346       }
347       if (need_validation_file_entry(ent.get(), now) &&
348           !validate_file_entry(ent.get(), now)) {
349         if (ent->usecount == 0) {
350           fd_cache_lru_.remove(ent.get());
351           close(ent->fd);
352           it = fd_cache_.erase(it);
353           continue;
354         }
355         ++it;
356         continue;
357       }
358 
359       fd_cache_lru_.remove(ent.get());
360       fd_cache_lru_.append(ent.get());
361 
362       ++ent->usecount;
363       return ent.get();
364     }
365     return nullptr;
366   }
cache_fd(const std::string & path,const FileEntry & ent)367   FileEntry *cache_fd(const std::string &path, const FileEntry &ent) {
368 #ifdef HAVE_STD_MAP_EMPLACE
369     auto rv = fd_cache_.emplace(path, std::make_unique<FileEntry>(ent));
370 #else  // !HAVE_STD_MAP_EMPLACE
371     // for gcc-4.7
372     auto rv = fd_cache_.insert(
373         std::make_pair(path, std::make_unique<FileEntry>(ent)));
374 #endif // !HAVE_STD_MAP_EMPLACE
375     auto &res = (*rv).second;
376     res->it = rv;
377     fd_cache_lru_.append(res.get());
378 
379     while (fd_cache_.size() > FILE_ENTRY_EVICT_THRES) {
380       auto ent = fd_cache_lru_.head;
381       if (ent->usecount) {
382         break;
383       }
384       fd_cache_lru_.remove(ent);
385       close(ent->fd);
386       fd_cache_.erase(ent->it);
387     }
388 
389     return res.get();
390   }
release_fd(FileEntry * target)391   void release_fd(FileEntry *target) {
392     --target->usecount;
393 
394     if (target->usecount == 0 && target->stale) {
395       fd_cache_lru_.remove(target);
396       close(target->fd);
397       fd_cache_.erase(target->it);
398       return;
399     }
400 
401     // We use timer to close file descriptor and delete the entry from
402     // cache.  The timer will be started when there is no handler.
403   }
release_unused_fd()404   void release_unused_fd() {
405     for (auto i = std::begin(fd_cache_); i != std::end(fd_cache_);) {
406       auto &ent = (*i).second;
407       if (ent->usecount != 0) {
408         ++i;
409         continue;
410       }
411 
412       fd_cache_lru_.remove(ent.get());
413       close(ent->fd);
414       i = fd_cache_.erase(i);
415     }
416   }
get_server() const417   const HttpServer *get_server() const { return sv_; }
handlers_empty() const418   bool handlers_empty() const { return handlers_.empty(); }
419 
420 private:
421   std::set<Http2Handler *> handlers_;
422   // cache for file descriptors to read file.
423   std::multimap<std::string, std::unique_ptr<FileEntry>> fd_cache_;
424   DList<FileEntry> fd_cache_lru_;
425   HttpServer *sv_;
426   struct ev_loop *loop_;
427   const Config *config_;
428   SSL_CTX *ssl_ctx_;
429   nghttp2_session_callbacks *callbacks_;
430   nghttp2_option *option_;
431   ev_timer release_fd_timer_;
432   int64_t next_session_id_;
433   ev_tstamp tstamp_cached_;
434   std::string cached_date_;
435 };
436 
437 namespace {
release_fd_cb(struct ev_loop * loop,ev_timer * w,int revents)438 void release_fd_cb(struct ev_loop *loop, ev_timer *w, int revents) {
439   auto sessions = static_cast<Sessions *>(w->data);
440 
441   ev_timer_stop(loop, w);
442 
443   if (!sessions->handlers_empty()) {
444     return;
445   }
446 
447   sessions->release_unused_fd();
448 }
449 } // namespace
450 
Stream(Http2Handler * handler,int32_t stream_id)451 Stream::Stream(Http2Handler *handler, int32_t stream_id)
452     : balloc(1024, 1024),
453       header{},
454       handler(handler),
455       file_ent(nullptr),
456       body_length(0),
457       body_offset(0),
458       header_buffer_size(0),
459       stream_id(stream_id),
460       echo_upload(false) {
461   auto config = handler->get_config();
462   ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
463   ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
464   rtimer.data = this;
465   wtimer.data = this;
466 }
467 
~Stream()468 Stream::~Stream() {
469   if (file_ent != nullptr) {
470     auto sessions = handler->get_sessions();
471     sessions->release_fd(file_ent);
472   }
473 
474   auto &rcbuf = header.rcbuf;
475   nghttp2_rcbuf_decref(rcbuf.method);
476   nghttp2_rcbuf_decref(rcbuf.scheme);
477   nghttp2_rcbuf_decref(rcbuf.authority);
478   nghttp2_rcbuf_decref(rcbuf.host);
479   nghttp2_rcbuf_decref(rcbuf.path);
480   nghttp2_rcbuf_decref(rcbuf.ims);
481   nghttp2_rcbuf_decref(rcbuf.expect);
482 
483   auto loop = handler->get_loop();
484   ev_timer_stop(loop, &rtimer);
485   ev_timer_stop(loop, &wtimer);
486 }
487 
488 namespace {
on_session_closed(Http2Handler * hd,int64_t session_id)489 void on_session_closed(Http2Handler *hd, int64_t session_id) {
490   if (hd->get_config()->verbose) {
491     print_session_id(session_id);
492     print_timer();
493     std::cout << " closed" << std::endl;
494   }
495 }
496 } // namespace
497 
498 namespace {
settings_timeout_cb(struct ev_loop * loop,ev_timer * w,int revents)499 void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
500   int rv;
501   auto hd = static_cast<Http2Handler *>(w->data);
502   hd->terminate_session(NGHTTP2_SETTINGS_TIMEOUT);
503   rv = hd->on_write();
504   if (rv == -1) {
505     delete_handler(hd);
506   }
507 }
508 } // namespace
509 
510 namespace {
readcb(struct ev_loop * loop,ev_io * w,int revents)511 void readcb(struct ev_loop *loop, ev_io *w, int revents) {
512   int rv;
513   auto handler = static_cast<Http2Handler *>(w->data);
514 
515   rv = handler->on_read();
516   if (rv == -1) {
517     delete_handler(handler);
518   }
519 }
520 } // namespace
521 
522 namespace {
writecb(struct ev_loop * loop,ev_io * w,int revents)523 void writecb(struct ev_loop *loop, ev_io *w, int revents) {
524   int rv;
525   auto handler = static_cast<Http2Handler *>(w->data);
526 
527   rv = handler->on_write();
528   if (rv == -1) {
529     delete_handler(handler);
530   }
531 }
532 } // namespace
533 
Http2Handler(Sessions * sessions,int fd,SSL * ssl,int64_t session_id)534 Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl,
535                            int64_t session_id)
536     : session_id_(session_id),
537       session_(nullptr),
538       sessions_(sessions),
539       ssl_(ssl),
540       data_pending_(nullptr),
541       data_pendinglen_(0),
542       fd_(fd) {
543   ev_timer_init(&settings_timerev_, settings_timeout_cb, 10., 0.);
544   ev_io_init(&wev_, writecb, fd, EV_WRITE);
545   ev_io_init(&rev_, readcb, fd, EV_READ);
546 
547   settings_timerev_.data = this;
548   wev_.data = this;
549   rev_.data = this;
550 
551   auto loop = sessions_->get_loop();
552   ev_io_start(loop, &rev_);
553 
554   if (ssl) {
555     SSL_set_accept_state(ssl);
556     read_ = &Http2Handler::tls_handshake;
557     write_ = &Http2Handler::tls_handshake;
558   } else {
559     read_ = &Http2Handler::read_clear;
560     write_ = &Http2Handler::write_clear;
561   }
562 }
563 
~Http2Handler()564 Http2Handler::~Http2Handler() {
565   on_session_closed(this, session_id_);
566   nghttp2_session_del(session_);
567   if (ssl_) {
568     SSL_set_shutdown(ssl_, SSL_get_shutdown(ssl_) | SSL_RECEIVED_SHUTDOWN);
569     ERR_clear_error();
570     SSL_shutdown(ssl_);
571   }
572   auto loop = sessions_->get_loop();
573   ev_timer_stop(loop, &settings_timerev_);
574   ev_io_stop(loop, &rev_);
575   ev_io_stop(loop, &wev_);
576   if (ssl_) {
577     SSL_free(ssl_);
578   }
579   shutdown(fd_, SHUT_WR);
580   close(fd_);
581 }
582 
remove_self()583 void Http2Handler::remove_self() { sessions_->remove_handler(this); }
584 
get_loop() const585 struct ev_loop *Http2Handler::get_loop() const { return sessions_->get_loop(); }
586 
get_wb()587 Http2Handler::WriteBuf *Http2Handler::get_wb() { return &wb_; }
588 
start_settings_timer()589 void Http2Handler::start_settings_timer() {
590   ev_timer_start(sessions_->get_loop(), &settings_timerev_);
591 }
592 
fill_wb()593 int Http2Handler::fill_wb() {
594   if (data_pending_) {
595     auto n = std::min(wb_.wleft(), data_pendinglen_);
596     wb_.write(data_pending_, n);
597     if (n < data_pendinglen_) {
598       data_pending_ += n;
599       data_pendinglen_ -= n;
600       return 0;
601     }
602 
603     data_pending_ = nullptr;
604     data_pendinglen_ = 0;
605   }
606 
607   for (;;) {
608     const uint8_t *data;
609     auto datalen = nghttp2_session_mem_send2(session_, &data);
610 
611     if (datalen < 0) {
612       std::cerr << "nghttp2_session_mem_send2() returned error: "
613                 << nghttp2_strerror(datalen) << std::endl;
614       return -1;
615     }
616     if (datalen == 0) {
617       break;
618     }
619     auto n = wb_.write(data, datalen);
620     if (n < static_cast<decltype(n)>(datalen)) {
621       data_pending_ = data + n;
622       data_pendinglen_ = datalen - n;
623       break;
624     }
625   }
626   return 0;
627 }
628 
read_clear()629 int Http2Handler::read_clear() {
630   int rv;
631   std::array<uint8_t, 8_k> buf;
632 
633   ssize_t nread;
634   while ((nread = read(fd_, buf.data(), buf.size())) == -1 && errno == EINTR)
635     ;
636   if (nread == -1) {
637     if (errno == EAGAIN || errno == EWOULDBLOCK) {
638       return write_(*this);
639     }
640     return -1;
641   }
642   if (nread == 0) {
643     return -1;
644   }
645 
646   if (get_config()->hexdump) {
647     util::hexdump(stdout, buf.data(), nread);
648   }
649 
650   rv = nghttp2_session_mem_recv2(session_, buf.data(), nread);
651   if (rv < 0) {
652     if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
653       std::cerr << "nghttp2_session_mem_recv2() returned error: "
654                 << nghttp2_strerror(rv) << std::endl;
655     }
656     return -1;
657   }
658 
659   return write_(*this);
660 }
661 
write_clear()662 int Http2Handler::write_clear() {
663   auto loop = sessions_->get_loop();
664   for (;;) {
665     if (wb_.rleft() > 0) {
666       ssize_t nwrite;
667       while ((nwrite = write(fd_, wb_.pos, wb_.rleft())) == -1 &&
668              errno == EINTR)
669         ;
670       if (nwrite == -1) {
671         if (errno == EAGAIN || errno == EWOULDBLOCK) {
672           ev_io_start(loop, &wev_);
673           return 0;
674         }
675         return -1;
676       }
677       wb_.drain(nwrite);
678       continue;
679     }
680     wb_.reset();
681     if (fill_wb() != 0) {
682       return -1;
683     }
684     if (wb_.rleft() == 0) {
685       break;
686     }
687   }
688 
689   if (wb_.rleft() == 0) {
690     ev_io_stop(loop, &wev_);
691   } else {
692     ev_io_start(loop, &wev_);
693   }
694 
695   if (nghttp2_session_want_read(session_) == 0 &&
696       nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
697     return -1;
698   }
699 
700   return 0;
701 }
702 
tls_handshake()703 int Http2Handler::tls_handshake() {
704   ev_io_stop(sessions_->get_loop(), &wev_);
705 
706   ERR_clear_error();
707 
708   auto rv = SSL_do_handshake(ssl_);
709 
710   if (rv <= 0) {
711     auto err = SSL_get_error(ssl_, rv);
712     switch (err) {
713     case SSL_ERROR_WANT_READ:
714       return 0;
715     case SSL_ERROR_WANT_WRITE:
716       ev_io_start(sessions_->get_loop(), &wev_);
717       return 0;
718     default:
719       return -1;
720     }
721   }
722 
723   if (sessions_->get_config()->verbose) {
724     std::cerr << "SSL/TLS handshake completed" << std::endl;
725   }
726 
727   if (verify_alpn_result() != 0) {
728     return -1;
729   }
730 
731   read_ = &Http2Handler::read_tls;
732   write_ = &Http2Handler::write_tls;
733 
734   if (connection_made() != 0) {
735     return -1;
736   }
737 
738   if (sessions_->get_config()->verbose) {
739     if (SSL_session_reused(ssl_)) {
740       std::cerr << "SSL/TLS session reused" << std::endl;
741     }
742   }
743 
744   return 0;
745 }
746 
read_tls()747 int Http2Handler::read_tls() {
748   std::array<uint8_t, 8_k> buf;
749 
750   ERR_clear_error();
751 
752   for (;;) {
753     auto rv = SSL_read(ssl_, buf.data(), buf.size());
754 
755     if (rv <= 0) {
756       auto err = SSL_get_error(ssl_, rv);
757       switch (err) {
758       case SSL_ERROR_WANT_READ:
759         return write_(*this);
760       case SSL_ERROR_WANT_WRITE:
761         // renegotiation started
762         return -1;
763       default:
764         return -1;
765       }
766     }
767 
768     auto nread = rv;
769 
770     if (get_config()->hexdump) {
771       util::hexdump(stdout, buf.data(), nread);
772     }
773 
774     rv = nghttp2_session_mem_recv2(session_, buf.data(), nread);
775     if (rv < 0) {
776       if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
777         std::cerr << "nghttp2_session_mem_recv2() returned error: "
778                   << nghttp2_strerror(rv) << std::endl;
779       }
780       return -1;
781     }
782 
783     if (SSL_pending(ssl_) == 0) {
784       break;
785     }
786   }
787 
788   return write_(*this);
789 }
790 
write_tls()791 int Http2Handler::write_tls() {
792   auto loop = sessions_->get_loop();
793 
794   ERR_clear_error();
795 
796   for (;;) {
797     if (wb_.rleft() > 0) {
798       auto rv = SSL_write(ssl_, wb_.pos, wb_.rleft());
799 
800       if (rv <= 0) {
801         auto err = SSL_get_error(ssl_, rv);
802         switch (err) {
803         case SSL_ERROR_WANT_READ:
804           // renegotiation started
805           return -1;
806         case SSL_ERROR_WANT_WRITE:
807           ev_io_start(sessions_->get_loop(), &wev_);
808           return 0;
809         default:
810           return -1;
811         }
812       }
813 
814       wb_.drain(rv);
815       continue;
816     }
817     wb_.reset();
818     if (fill_wb() != 0) {
819       return -1;
820     }
821     if (wb_.rleft() == 0) {
822       break;
823     }
824   }
825 
826   if (wb_.rleft() == 0) {
827     ev_io_stop(loop, &wev_);
828   } else {
829     ev_io_start(loop, &wev_);
830   }
831 
832   if (nghttp2_session_want_read(session_) == 0 &&
833       nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
834     return -1;
835   }
836 
837   return 0;
838 }
839 
on_read()840 int Http2Handler::on_read() { return read_(*this); }
841 
on_write()842 int Http2Handler::on_write() { return write_(*this); }
843 
connection_made()844 int Http2Handler::connection_made() {
845   int r;
846 
847   r = nghttp2_session_server_new2(&session_, sessions_->get_callbacks(), this,
848                                   sessions_->get_option());
849 
850   if (r != 0) {
851     return r;
852   }
853 
854   auto config = sessions_->get_config();
855   std::array<nghttp2_settings_entry, 4> entry;
856   size_t niv = 1;
857 
858   entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
859   entry[0].value = config->max_concurrent_streams;
860 
861   if (config->header_table_size >= 0) {
862     entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
863     entry[niv].value = config->header_table_size;
864     ++niv;
865   }
866 
867   if (config->window_bits != -1) {
868     entry[niv].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
869     entry[niv].value = (1 << config->window_bits) - 1;
870     ++niv;
871   }
872 
873   if (config->no_rfc7540_pri) {
874     entry[niv].settings_id = NGHTTP2_SETTINGS_NO_RFC7540_PRIORITIES;
875     entry[niv].value = 1;
876     ++niv;
877   }
878 
879   r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
880   if (r != 0) {
881     return r;
882   }
883 
884   if (config->connection_window_bits != -1) {
885     r = nghttp2_session_set_local_window_size(
886         session_, NGHTTP2_FLAG_NONE, 0,
887         (1 << config->connection_window_bits) - 1);
888     if (r != 0) {
889       return r;
890     }
891   }
892 
893   if (ssl_ && !nghttp2::tls::check_http2_requirement(ssl_)) {
894     terminate_session(NGHTTP2_INADEQUATE_SECURITY);
895   }
896 
897   return on_write();
898 }
899 
verify_alpn_result()900 int Http2Handler::verify_alpn_result() {
901   const unsigned char *next_proto = nullptr;
902   unsigned int next_proto_len;
903   // Check the negotiated protocol in ALPN
904   SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len);
905   if (next_proto) {
906     auto proto = StringRef{next_proto, next_proto_len};
907     if (sessions_->get_config()->verbose) {
908       std::cout << "The negotiated protocol: " << proto << std::endl;
909     }
910     if (util::check_h2_is_selected(proto)) {
911       return 0;
912     }
913   }
914   if (sessions_->get_config()->verbose) {
915     std::cerr << "Client did not advertise HTTP/2 protocol."
916               << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")"
917               << std::endl;
918   }
919   return -1;
920 }
921 
submit_file_response(const StringRef & status,Stream * stream,time_t last_modified,off_t file_length,const std::string * content_type,nghttp2_data_provider2 * data_prd)922 int Http2Handler::submit_file_response(const StringRef &status, Stream *stream,
923                                        time_t last_modified, off_t file_length,
924                                        const std::string *content_type,
925                                        nghttp2_data_provider2 *data_prd) {
926   std::string last_modified_str;
927   auto nva = std::to_array({
928       http2::make_field(":status"_sr, status),
929       http2::make_field("server"_sr, NGHTTPD_SERVER),
930       http2::make_field("cache-control"_sr, "max-age=3600"_sr),
931       http2::make_field_v("date"_sr, sessions_->get_cached_date()),
932       {},
933       {},
934       {},
935       {},
936   });
937   size_t nvlen = 4;
938   if (!get_config()->no_content_length) {
939     nva[nvlen++] = http2::make_field(
940         "content-length"_sr,
941         util::make_string_ref_uint(stream->balloc, file_length));
942   }
943   if (last_modified != 0) {
944     last_modified_str = util::http_date(last_modified);
945     nva[nvlen++] = http2::make_field_v("last-modified"_sr, last_modified_str);
946   }
947   if (content_type) {
948     nva[nvlen++] = http2::make_field_v("content-type"_sr, *content_type);
949   }
950   auto &trailer_names = get_config()->trailer_names;
951   if (!trailer_names.empty()) {
952     nva[nvlen++] = http2::make_field("trailer"_sr, trailer_names);
953   }
954   return nghttp2_submit_response2(session_, stream->stream_id, nva.data(),
955                                   nvlen, data_prd);
956 }
957 
submit_response(const StringRef & status,int32_t stream_id,const HeaderRefs & headers,nghttp2_data_provider2 * data_prd)958 int Http2Handler::submit_response(const StringRef &status, int32_t stream_id,
959                                   const HeaderRefs &headers,
960                                   nghttp2_data_provider2 *data_prd) {
961   auto nva = std::vector<nghttp2_nv>();
962   nva.reserve(4 + headers.size());
963   nva.push_back(http2::make_field(":status"_sr, status));
964   nva.push_back(http2::make_field("server"_sr, NGHTTPD_SERVER));
965   nva.push_back(http2::make_field_v("date"_sr, sessions_->get_cached_date()));
966 
967   if (data_prd) {
968     auto &trailer_names = get_config()->trailer_names;
969     if (!trailer_names.empty()) {
970       nva.push_back(http2::make_field("trailer"_sr, trailer_names));
971     }
972   }
973 
974   for (auto &nv : headers) {
975     nva.push_back(
976         http2::make_field(nv.name, nv.value, http2::no_index(nv.no_index)));
977   }
978   int r = nghttp2_submit_response2(session_, stream_id, nva.data(), nva.size(),
979                                    data_prd);
980   return r;
981 }
982 
submit_response(const StringRef & status,int32_t stream_id,nghttp2_data_provider2 * data_prd)983 int Http2Handler::submit_response(const StringRef &status, int32_t stream_id,
984                                   nghttp2_data_provider2 *data_prd) {
985   auto nva = std::to_array({
986       http2::make_field(":status"_sr, status),
987       http2::make_field("server"_sr, NGHTTPD_SERVER),
988       http2::make_field_v("date"_sr, sessions_->get_cached_date()),
989       {},
990   });
991   size_t nvlen = 3;
992 
993   if (data_prd) {
994     auto &trailer_names = get_config()->trailer_names;
995     if (!trailer_names.empty()) {
996       nva[nvlen++] = http2::make_field("trailer"_sr, trailer_names);
997     }
998   }
999 
1000   return nghttp2_submit_response2(session_, stream_id, nva.data(), nvlen,
1001                                   data_prd);
1002 }
1003 
submit_non_final_response(const std::string & status,int32_t stream_id)1004 int Http2Handler::submit_non_final_response(const std::string &status,
1005                                             int32_t stream_id) {
1006   auto nva = std::to_array({http2::make_field_v(":status"_sr, status)});
1007   return nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, stream_id, nullptr,
1008                                 nva.data(), nva.size(), nullptr);
1009 }
1010 
submit_push_promise(Stream * stream,const StringRef & push_path)1011 int Http2Handler::submit_push_promise(Stream *stream,
1012                                       const StringRef &push_path) {
1013   auto authority = stream->header.authority;
1014 
1015   if (authority.empty()) {
1016     authority = stream->header.host;
1017   }
1018 
1019   auto scheme = get_config()->no_tls ? "http"_sr : "https"_sr;
1020 
1021   auto nva = std::to_array({http2::make_field(":method"_sr, "GET"_sr),
1022                             http2::make_field(":path"_sr, push_path),
1023                             http2::make_field(":scheme"_sr, scheme),
1024                             http2::make_field(":authority"_sr, authority)});
1025 
1026   auto promised_stream_id = nghttp2_submit_push_promise(
1027       session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(),
1028       nva.size(), nullptr);
1029 
1030   if (promised_stream_id < 0) {
1031     return promised_stream_id;
1032   }
1033 
1034   auto promised_stream = std::make_unique<Stream>(this, promised_stream_id);
1035 
1036   auto &promised_header = promised_stream->header;
1037   promised_header.method = "GET"_sr;
1038   promised_header.path = push_path;
1039   promised_header.scheme = scheme;
1040   promised_header.authority =
1041       make_string_ref(promised_stream->balloc, authority);
1042 
1043   add_stream(promised_stream_id, std::move(promised_stream));
1044 
1045   return 0;
1046 }
1047 
submit_rst_stream(Stream * stream,uint32_t error_code)1048 int Http2Handler::submit_rst_stream(Stream *stream, uint32_t error_code) {
1049   remove_stream_read_timeout(stream);
1050   remove_stream_write_timeout(stream);
1051 
1052   return nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE,
1053                                    stream->stream_id, error_code);
1054 }
1055 
add_stream(int32_t stream_id,std::unique_ptr<Stream> stream)1056 void Http2Handler::add_stream(int32_t stream_id,
1057                               std::unique_ptr<Stream> stream) {
1058   id2stream_[stream_id] = std::move(stream);
1059 }
1060 
remove_stream(int32_t stream_id)1061 void Http2Handler::remove_stream(int32_t stream_id) {
1062   id2stream_.erase(stream_id);
1063 }
1064 
get_stream(int32_t stream_id)1065 Stream *Http2Handler::get_stream(int32_t stream_id) {
1066   auto itr = id2stream_.find(stream_id);
1067   if (itr == std::end(id2stream_)) {
1068     return nullptr;
1069   } else {
1070     return (*itr).second.get();
1071   }
1072 }
1073 
session_id() const1074 int64_t Http2Handler::session_id() const { return session_id_; }
1075 
get_sessions() const1076 Sessions *Http2Handler::get_sessions() const { return sessions_; }
1077 
get_config() const1078 const Config *Http2Handler::get_config() const {
1079   return sessions_->get_config();
1080 }
1081 
remove_settings_timer()1082 void Http2Handler::remove_settings_timer() {
1083   ev_timer_stop(sessions_->get_loop(), &settings_timerev_);
1084 }
1085 
terminate_session(uint32_t error_code)1086 void Http2Handler::terminate_session(uint32_t error_code) {
1087   nghttp2_session_terminate_session(session_, error_code);
1088 }
1089 
file_read_callback(nghttp2_session * session,int32_t stream_id,uint8_t * buf,size_t length,uint32_t * data_flags,nghttp2_data_source * source,void * user_data)1090 nghttp2_ssize file_read_callback(nghttp2_session *session, int32_t stream_id,
1091                                  uint8_t *buf, size_t length,
1092                                  uint32_t *data_flags,
1093                                  nghttp2_data_source *source, void *user_data) {
1094   int rv;
1095   auto hd = static_cast<Http2Handler *>(user_data);
1096   auto stream = hd->get_stream(stream_id);
1097 
1098   auto nread = std::min(stream->body_length - stream->body_offset,
1099                         static_cast<int64_t>(length));
1100 
1101   *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
1102 
1103   if (nread == 0 || stream->body_length == stream->body_offset + nread) {
1104     *data_flags |= NGHTTP2_DATA_FLAG_EOF;
1105 
1106     auto config = hd->get_config();
1107     if (!config->trailer.empty()) {
1108       std::vector<nghttp2_nv> nva;
1109       nva.reserve(config->trailer.size());
1110       for (auto &kv : config->trailer) {
1111         nva.push_back(http2::make_field_nv(kv.name, kv.value,
1112                                            http2::no_index(kv.no_index)));
1113       }
1114       rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
1115       if (rv != 0) {
1116         if (nghttp2_is_fatal(rv)) {
1117           return NGHTTP2_ERR_CALLBACK_FAILURE;
1118         }
1119       } else {
1120         *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
1121       }
1122     }
1123 
1124     if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
1125       remove_stream_read_timeout(stream);
1126       remove_stream_write_timeout(stream);
1127 
1128       hd->submit_rst_stream(stream, NGHTTP2_NO_ERROR);
1129     }
1130   }
1131 
1132   return nread;
1133 }
1134 
1135 namespace {
prepare_status_response(Stream * stream,Http2Handler * hd,int status)1136 void prepare_status_response(Stream *stream, Http2Handler *hd, int status) {
1137   auto sessions = hd->get_sessions();
1138   auto status_page = sessions->get_server()->get_status_page(status);
1139   auto file_ent = &status_page->file_ent;
1140 
1141   // we don't set stream->file_ent since we don't want to expire it.
1142   stream->body_length = file_ent->length;
1143   nghttp2_data_provider2 data_prd;
1144   data_prd.source.fd = file_ent->fd;
1145   data_prd.read_callback = file_read_callback;
1146 
1147   HeaderRefs headers;
1148   headers.reserve(2);
1149   headers.emplace_back("content-type"_sr, "text/html; charset=UTF-8"_sr);
1150   headers.emplace_back(
1151       "content-length"_sr,
1152       util::make_string_ref_uint(stream->balloc, file_ent->length));
1153   hd->submit_response(StringRef{status_page->status}, stream->stream_id,
1154                       headers, &data_prd);
1155 }
1156 } // namespace
1157 
1158 namespace {
prepare_echo_response(Stream * stream,Http2Handler * hd)1159 void prepare_echo_response(Stream *stream, Http2Handler *hd) {
1160   auto length = lseek(stream->file_ent->fd, 0, SEEK_END);
1161   if (length == -1) {
1162     hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
1163     return;
1164   }
1165   stream->body_length = length;
1166   if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) {
1167     hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
1168     return;
1169   }
1170   nghttp2_data_provider2 data_prd;
1171   data_prd.source.fd = stream->file_ent->fd;
1172   data_prd.read_callback = file_read_callback;
1173 
1174   HeaderRefs headers;
1175   headers.emplace_back("nghttpd-response"_sr, "echo"_sr);
1176   if (!hd->get_config()->no_content_length) {
1177     headers.emplace_back("content-length"_sr,
1178                          util::make_string_ref_uint(stream->balloc, length));
1179   }
1180 
1181   hd->submit_response("200"_sr, stream->stream_id, headers, &data_prd);
1182 }
1183 } // namespace
1184 
1185 namespace {
prepare_upload_temp_store(Stream * stream,Http2Handler * hd)1186 bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
1187   auto sessions = hd->get_sessions();
1188 
1189   char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
1190   auto fd = mkstemp(tempfn);
1191   if (fd == -1) {
1192     return false;
1193   }
1194   unlink(tempfn);
1195   // Ordinary request never start with "echo:".  The length is 0 for
1196   // now.  We will update it when we get whole request body.
1197   auto path = std::string("echo:") + tempfn;
1198   stream->file_ent =
1199       sessions->cache_fd(path, FileEntry(path, 0, 0, fd, nullptr, {}, true));
1200   stream->echo_upload = true;
1201   return true;
1202 }
1203 } // namespace
1204 
1205 namespace {
prepare_redirect_response(Stream * stream,Http2Handler * hd,const StringRef & path,int status)1206 void prepare_redirect_response(Stream *stream, Http2Handler *hd,
1207                                const StringRef &path, int status) {
1208   auto scheme = stream->header.scheme;
1209 
1210   auto authority = stream->header.authority;
1211   if (authority.empty()) {
1212     authority = stream->header.host;
1213   }
1214 
1215   auto location =
1216       concat_string_ref(stream->balloc, scheme, "://"_sr, authority, path);
1217 
1218   auto headers = HeaderRefs{{"location"_sr, location}};
1219 
1220   auto sessions = hd->get_sessions();
1221   auto status_page = sessions->get_server()->get_status_page(status);
1222 
1223   hd->submit_response(StringRef{status_page->status}, stream->stream_id,
1224                       headers, nullptr);
1225 }
1226 } // namespace
1227 
1228 namespace {
prepare_response(Stream * stream,Http2Handler * hd,bool allow_push=true)1229 void prepare_response(Stream *stream, Http2Handler *hd,
1230                       bool allow_push = true) {
1231   int rv;
1232   auto reqpath = stream->header.path;
1233   if (reqpath.empty()) {
1234     prepare_status_response(stream, hd, 405);
1235     return;
1236   }
1237 
1238   auto ims = stream->header.ims;
1239 
1240   time_t last_mod = 0;
1241   bool last_mod_found = false;
1242   if (!ims.empty()) {
1243     last_mod_found = true;
1244     last_mod = util::parse_http_date(ims);
1245   }
1246 
1247   StringRef raw_path, raw_query;
1248   auto query_pos = std::find(std::begin(reqpath), std::end(reqpath), '?');
1249   if (query_pos != std::end(reqpath)) {
1250     // Do not response to this request to allow clients to test timeouts.
1251     if ("nghttpd_do_not_respond_to_req=yes"_sr ==
1252         StringRef{query_pos, std::end(reqpath)}) {
1253       return;
1254     }
1255     raw_path = StringRef{std::begin(reqpath), query_pos};
1256     raw_query = StringRef{query_pos, std::end(reqpath)};
1257   } else {
1258     raw_path = reqpath;
1259   }
1260 
1261   auto sessions = hd->get_sessions();
1262 
1263   StringRef path;
1264   if (std::find(std::begin(raw_path), std::end(raw_path), '%') ==
1265       std::end(raw_path)) {
1266     path = raw_path;
1267   } else {
1268     path = util::percent_decode(stream->balloc, raw_path);
1269   }
1270 
1271   path = http2::path_join(stream->balloc, StringRef{}, StringRef{}, path,
1272                           StringRef{});
1273 
1274   if (std::find(std::begin(path), std::end(path), '\\') != std::end(path)) {
1275     if (stream->file_ent) {
1276       sessions->release_fd(stream->file_ent);
1277       stream->file_ent = nullptr;
1278     }
1279     prepare_status_response(stream, hd, 404);
1280     return;
1281   }
1282 
1283   if (!hd->get_config()->push.empty()) {
1284     auto push_itr = hd->get_config()->push.find(std::string{path});
1285     if (allow_push && push_itr != std::end(hd->get_config()->push)) {
1286       for (auto &push_path : (*push_itr).second) {
1287         rv = hd->submit_push_promise(stream, StringRef{push_path});
1288         if (rv != 0) {
1289           std::cerr << "nghttp2_submit_push_promise() returned error: "
1290                     << nghttp2_strerror(rv) << std::endl;
1291         }
1292       }
1293     }
1294   }
1295 
1296   std::string file_path;
1297   {
1298     auto len = hd->get_config()->htdocs.size() + path.size();
1299 
1300     auto trailing_slash = path[path.size() - 1] == '/';
1301     if (trailing_slash) {
1302       len += DEFAULT_HTML.size();
1303     }
1304 
1305     file_path.resize(len);
1306 
1307     auto p = &file_path[0];
1308 
1309     auto &htdocs = hd->get_config()->htdocs;
1310     p = std::copy(std::begin(htdocs), std::end(htdocs), p);
1311     p = std::copy(std::begin(path), std::end(path), p);
1312     if (trailing_slash) {
1313       std::copy(std::begin(DEFAULT_HTML), std::end(DEFAULT_HTML), p);
1314     }
1315   }
1316 
1317   if (stream->echo_upload) {
1318     assert(stream->file_ent);
1319     prepare_echo_response(stream, hd);
1320     return;
1321   }
1322 
1323   auto file_ent = sessions->get_cached_fd(file_path);
1324 
1325   if (file_ent == nullptr) {
1326     int file = open(file_path.c_str(), O_RDONLY | O_BINARY);
1327     if (file == -1) {
1328       prepare_status_response(stream, hd, 404);
1329 
1330       return;
1331     }
1332 
1333     struct stat buf;
1334 
1335     if (fstat(file, &buf) == -1) {
1336       close(file);
1337       prepare_status_response(stream, hd, 404);
1338 
1339       return;
1340     }
1341 
1342     if (buf.st_mode & S_IFDIR) {
1343       close(file);
1344 
1345       auto reqpath =
1346           concat_string_ref(stream->balloc, raw_path, "/"_sr, raw_query);
1347 
1348       prepare_redirect_response(stream, hd, reqpath, 301);
1349 
1350       return;
1351     }
1352 
1353     const std::string *content_type = nullptr;
1354 
1355     auto ext = file_path.c_str() + file_path.size() - 1;
1356     for (; file_path.c_str() < ext && *ext != '.' && *ext != '/'; --ext)
1357       ;
1358     if (*ext == '.') {
1359       ++ext;
1360 
1361       const auto &mime_types = hd->get_config()->mime_types;
1362       auto content_type_itr = mime_types.find(ext);
1363       if (content_type_itr != std::end(mime_types)) {
1364         content_type = &(*content_type_itr).second;
1365       }
1366     }
1367 
1368     file_ent = sessions->cache_fd(
1369         file_path, FileEntry(file_path, buf.st_size, buf.st_mtime, file,
1370                              content_type, std::chrono::steady_clock::now()));
1371   }
1372 
1373   stream->file_ent = file_ent;
1374 
1375   if (last_mod_found && file_ent->mtime <= last_mod) {
1376     hd->submit_response("304"_sr, stream->stream_id, nullptr);
1377 
1378     return;
1379   }
1380 
1381   auto method = stream->header.method;
1382   if (method == "HEAD"_sr) {
1383     hd->submit_file_response("200"_sr, stream, file_ent->mtime,
1384                              file_ent->length, file_ent->content_type, nullptr);
1385     return;
1386   }
1387 
1388   stream->body_length = file_ent->length;
1389 
1390   nghttp2_data_provider2 data_prd;
1391 
1392   data_prd.source.fd = file_ent->fd;
1393   data_prd.read_callback = file_read_callback;
1394 
1395   hd->submit_file_response("200"_sr, stream, file_ent->mtime, file_ent->length,
1396                            file_ent->content_type, &data_prd);
1397 }
1398 } // namespace
1399 
1400 namespace {
on_header_callback2(nghttp2_session * session,const nghttp2_frame * frame,nghttp2_rcbuf * name,nghttp2_rcbuf * value,uint8_t flags,void * user_data)1401 int on_header_callback2(nghttp2_session *session, const nghttp2_frame *frame,
1402                         nghttp2_rcbuf *name, nghttp2_rcbuf *value,
1403                         uint8_t flags, void *user_data) {
1404   auto hd = static_cast<Http2Handler *>(user_data);
1405 
1406   auto namebuf = nghttp2_rcbuf_get_buf(name);
1407   auto valuebuf = nghttp2_rcbuf_get_buf(value);
1408 
1409   if (hd->get_config()->verbose) {
1410     print_session_id(hd->session_id());
1411     verbose_on_header_callback(session, frame, namebuf.base, namebuf.len,
1412                                valuebuf.base, valuebuf.len, flags, user_data);
1413   }
1414   if (frame->hd.type != NGHTTP2_HEADERS ||
1415       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
1416     return 0;
1417   }
1418   auto stream = hd->get_stream(frame->hd.stream_id);
1419   if (!stream) {
1420     return 0;
1421   }
1422 
1423   if (stream->header_buffer_size + namebuf.len + valuebuf.len > 64_k) {
1424     hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
1425     return 0;
1426   }
1427 
1428   stream->header_buffer_size += namebuf.len + valuebuf.len;
1429 
1430   auto token = http2::lookup_token(StringRef{namebuf.base, namebuf.len});
1431 
1432   auto &header = stream->header;
1433 
1434   switch (token) {
1435   case http2::HD__METHOD:
1436     header.method = StringRef{valuebuf.base, valuebuf.len};
1437     header.rcbuf.method = value;
1438     nghttp2_rcbuf_incref(value);
1439     break;
1440   case http2::HD__SCHEME:
1441     header.scheme = StringRef{valuebuf.base, valuebuf.len};
1442     header.rcbuf.scheme = value;
1443     nghttp2_rcbuf_incref(value);
1444     break;
1445   case http2::HD__AUTHORITY:
1446     header.authority = StringRef{valuebuf.base, valuebuf.len};
1447     header.rcbuf.authority = value;
1448     nghttp2_rcbuf_incref(value);
1449     break;
1450   case http2::HD_HOST:
1451     header.host = StringRef{valuebuf.base, valuebuf.len};
1452     header.rcbuf.host = value;
1453     nghttp2_rcbuf_incref(value);
1454     break;
1455   case http2::HD__PATH:
1456     header.path = StringRef{valuebuf.base, valuebuf.len};
1457     header.rcbuf.path = value;
1458     nghttp2_rcbuf_incref(value);
1459     break;
1460   case http2::HD_IF_MODIFIED_SINCE:
1461     header.ims = StringRef{valuebuf.base, valuebuf.len};
1462     header.rcbuf.ims = value;
1463     nghttp2_rcbuf_incref(value);
1464     break;
1465   case http2::HD_EXPECT:
1466     header.expect = StringRef{valuebuf.base, valuebuf.len};
1467     header.rcbuf.expect = value;
1468     nghttp2_rcbuf_incref(value);
1469     break;
1470   }
1471 
1472   return 0;
1473 }
1474 } // namespace
1475 
1476 namespace {
on_begin_headers_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)1477 int on_begin_headers_callback(nghttp2_session *session,
1478                               const nghttp2_frame *frame, void *user_data) {
1479   auto hd = static_cast<Http2Handler *>(user_data);
1480 
1481   if (frame->hd.type != NGHTTP2_HEADERS ||
1482       frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
1483     return 0;
1484   }
1485 
1486   auto stream = std::make_unique<Stream>(hd, frame->hd.stream_id);
1487 
1488   add_stream_read_timeout(stream.get());
1489 
1490   hd->add_stream(frame->hd.stream_id, std::move(stream));
1491 
1492   return 0;
1493 }
1494 } // namespace
1495 
1496 namespace {
hd_on_frame_recv_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)1497 int hd_on_frame_recv_callback(nghttp2_session *session,
1498                               const nghttp2_frame *frame, void *user_data) {
1499   auto hd = static_cast<Http2Handler *>(user_data);
1500   if (hd->get_config()->verbose) {
1501     print_session_id(hd->session_id());
1502     verbose_on_frame_recv_callback(session, frame, user_data);
1503   }
1504   switch (frame->hd.type) {
1505   case NGHTTP2_DATA: {
1506     // TODO Handle POST
1507     auto stream = hd->get_stream(frame->hd.stream_id);
1508     if (!stream) {
1509       return 0;
1510     }
1511 
1512     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
1513       remove_stream_read_timeout(stream);
1514       if (stream->echo_upload || !hd->get_config()->early_response) {
1515         prepare_response(stream, hd);
1516       }
1517     } else {
1518       add_stream_read_timeout(stream);
1519     }
1520 
1521     break;
1522   }
1523   case NGHTTP2_HEADERS: {
1524     auto stream = hd->get_stream(frame->hd.stream_id);
1525     if (!stream) {
1526       return 0;
1527     }
1528 
1529     if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
1530 
1531       auto expect100 = stream->header.expect;
1532 
1533       if (util::strieq("100-continue"_sr, expect100)) {
1534         hd->submit_non_final_response("100", frame->hd.stream_id);
1535       }
1536 
1537       auto method = stream->header.method;
1538       if (hd->get_config()->echo_upload &&
1539           (method == "POST"_sr || method == "PUT"_sr)) {
1540         if (!prepare_upload_temp_store(stream, hd)) {
1541           hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
1542           return 0;
1543         }
1544       } else if (hd->get_config()->early_response) {
1545         prepare_response(stream, hd);
1546       }
1547     }
1548 
1549     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
1550       remove_stream_read_timeout(stream);
1551       if (stream->echo_upload || !hd->get_config()->early_response) {
1552         prepare_response(stream, hd);
1553       }
1554     } else {
1555       add_stream_read_timeout(stream);
1556     }
1557 
1558     break;
1559   }
1560   case NGHTTP2_SETTINGS:
1561     if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
1562       hd->remove_settings_timer();
1563     }
1564     break;
1565   default:
1566     break;
1567   }
1568   return 0;
1569 }
1570 } // namespace
1571 
1572 namespace {
hd_on_frame_send_callback(nghttp2_session * session,const nghttp2_frame * frame,void * user_data)1573 int hd_on_frame_send_callback(nghttp2_session *session,
1574                               const nghttp2_frame *frame, void *user_data) {
1575   auto hd = static_cast<Http2Handler *>(user_data);
1576 
1577   if (hd->get_config()->verbose) {
1578     print_session_id(hd->session_id());
1579     verbose_on_frame_send_callback(session, frame, user_data);
1580   }
1581 
1582   switch (frame->hd.type) {
1583   case NGHTTP2_DATA:
1584   case NGHTTP2_HEADERS: {
1585     auto stream = hd->get_stream(frame->hd.stream_id);
1586 
1587     if (!stream) {
1588       return 0;
1589     }
1590 
1591     if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
1592       remove_stream_write_timeout(stream);
1593     } else if (std::min(nghttp2_session_get_stream_remote_window_size(
1594                             session, frame->hd.stream_id),
1595                         nghttp2_session_get_remote_window_size(session)) <= 0) {
1596       // If stream is blocked by flow control, enable write timeout.
1597       add_stream_read_timeout_if_pending(stream);
1598       add_stream_write_timeout(stream);
1599     } else {
1600       add_stream_read_timeout_if_pending(stream);
1601       remove_stream_write_timeout(stream);
1602     }
1603 
1604     break;
1605   }
1606   case NGHTTP2_SETTINGS: {
1607     if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
1608       return 0;
1609     }
1610 
1611     hd->start_settings_timer();
1612 
1613     break;
1614   }
1615   case NGHTTP2_PUSH_PROMISE: {
1616     auto promised_stream_id = frame->push_promise.promised_stream_id;
1617     auto promised_stream = hd->get_stream(promised_stream_id);
1618     auto stream = hd->get_stream(frame->hd.stream_id);
1619 
1620     if (!stream || !promised_stream) {
1621       return 0;
1622     }
1623 
1624     add_stream_read_timeout_if_pending(stream);
1625     add_stream_write_timeout(stream);
1626 
1627     prepare_response(promised_stream, hd, /*allow_push */ false);
1628   }
1629   }
1630   return 0;
1631 }
1632 } // namespace
1633 
1634 namespace {
send_data_callback(nghttp2_session * session,nghttp2_frame * frame,const uint8_t * framehd,size_t length,nghttp2_data_source * source,void * user_data)1635 int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
1636                        const uint8_t *framehd, size_t length,
1637                        nghttp2_data_source *source, void *user_data) {
1638   auto hd = static_cast<Http2Handler *>(user_data);
1639   auto wb = hd->get_wb();
1640   auto padlen = frame->data.padlen;
1641   auto stream = hd->get_stream(frame->hd.stream_id);
1642 
1643   if (wb->wleft() < 9 + length + padlen) {
1644     return NGHTTP2_ERR_WOULDBLOCK;
1645   }
1646 
1647   int fd = source->fd;
1648 
1649   auto p = wb->last;
1650 
1651   p = std::copy_n(framehd, 9, p);
1652 
1653   if (padlen) {
1654     *p++ = padlen - 1;
1655   }
1656 
1657   while (length) {
1658     ssize_t nread;
1659     while ((nread = pread(fd, p, length, stream->body_offset)) == -1 &&
1660            errno == EINTR)
1661       ;
1662 
1663     if (nread == -1) {
1664       remove_stream_read_timeout(stream);
1665       remove_stream_write_timeout(stream);
1666 
1667       return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1668     }
1669 
1670     stream->body_offset += nread;
1671     length -= nread;
1672     p += nread;
1673   }
1674 
1675   if (padlen) {
1676     std::fill(p, p + padlen - 1, 0);
1677     p += padlen - 1;
1678   }
1679 
1680   wb->last = p;
1681 
1682   return 0;
1683 }
1684 } // namespace
1685 
1686 namespace {
select_padding_callback(nghttp2_session * session,const nghttp2_frame * frame,size_t max_payload,void * user_data)1687 nghttp2_ssize select_padding_callback(nghttp2_session *session,
1688                                       const nghttp2_frame *frame,
1689                                       size_t max_payload, void *user_data) {
1690   auto hd = static_cast<Http2Handler *>(user_data);
1691   return std::min(max_payload, frame->hd.length + hd->get_config()->padding);
1692 }
1693 } // namespace
1694 
1695 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)1696 int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
1697                                 int32_t stream_id, const uint8_t *data,
1698                                 size_t len, void *user_data) {
1699   auto hd = static_cast<Http2Handler *>(user_data);
1700   auto stream = hd->get_stream(stream_id);
1701 
1702   if (!stream) {
1703     return 0;
1704   }
1705 
1706   if (stream->echo_upload) {
1707     assert(stream->file_ent);
1708     while (len) {
1709       ssize_t n;
1710       while ((n = write(stream->file_ent->fd, data, len)) == -1 &&
1711              errno == EINTR)
1712         ;
1713       if (n == -1) {
1714         hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
1715         return 0;
1716       }
1717       len -= n;
1718       data += n;
1719     }
1720   }
1721   // TODO Handle POST
1722 
1723   add_stream_read_timeout(stream);
1724 
1725   return 0;
1726 }
1727 } // namespace
1728 
1729 namespace {
on_stream_close_callback(nghttp2_session * session,int32_t stream_id,uint32_t error_code,void * user_data)1730 int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
1731                              uint32_t error_code, void *user_data) {
1732   auto hd = static_cast<Http2Handler *>(user_data);
1733   hd->remove_stream(stream_id);
1734   if (hd->get_config()->verbose) {
1735     print_session_id(hd->session_id());
1736     print_timer();
1737     printf(" stream_id=%d closed\n", stream_id);
1738     fflush(stdout);
1739   }
1740   return 0;
1741 }
1742 } // namespace
1743 
1744 namespace {
fill_callback(nghttp2_session_callbacks * callbacks,const Config * config)1745 void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) {
1746   nghttp2_session_callbacks_set_on_stream_close_callback(
1747       callbacks, on_stream_close_callback);
1748 
1749   nghttp2_session_callbacks_set_on_frame_recv_callback(
1750       callbacks, hd_on_frame_recv_callback);
1751 
1752   nghttp2_session_callbacks_set_on_frame_send_callback(
1753       callbacks, hd_on_frame_send_callback);
1754 
1755   if (config->verbose) {
1756     nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
1757         callbacks, verbose_on_invalid_frame_recv_callback);
1758 
1759     nghttp2_session_callbacks_set_error_callback2(callbacks,
1760                                                   verbose_error_callback);
1761   }
1762 
1763   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
1764       callbacks, on_data_chunk_recv_callback);
1765 
1766   nghttp2_session_callbacks_set_on_header_callback2(callbacks,
1767                                                     on_header_callback2);
1768 
1769   nghttp2_session_callbacks_set_on_begin_headers_callback(
1770       callbacks, on_begin_headers_callback);
1771 
1772   nghttp2_session_callbacks_set_send_data_callback(callbacks,
1773                                                    send_data_callback);
1774 
1775   if (config->padding) {
1776     nghttp2_session_callbacks_set_select_padding_callback2(
1777         callbacks, select_padding_callback);
1778   }
1779 }
1780 } // namespace
1781 
1782 struct ClientInfo {
1783   int fd;
1784 };
1785 
1786 struct Worker {
1787   std::unique_ptr<Sessions> sessions;
1788   ev_async w;
1789   // protects q
1790   std::mutex m;
1791   std::deque<ClientInfo> q;
1792 };
1793 
1794 namespace {
worker_acceptcb(struct ev_loop * loop,ev_async * w,int revents)1795 void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) {
1796   auto worker = static_cast<Worker *>(w->data);
1797   auto &sessions = worker->sessions;
1798 
1799   std::deque<ClientInfo> q;
1800   {
1801     std::lock_guard<std::mutex> lock(worker->m);
1802     q.swap(worker->q);
1803   }
1804 
1805   for (const auto &c : q) {
1806     sessions->accept_connection(c.fd);
1807   }
1808 }
1809 } // namespace
1810 
1811 namespace {
run_worker(Worker * worker)1812 void run_worker(Worker *worker) {
1813   auto loop = worker->sessions->get_loop();
1814 
1815   ev_run(loop, 0);
1816 }
1817 } // namespace
1818 
1819 namespace {
get_ev_loop_flags()1820 int get_ev_loop_flags() {
1821   if (ev_supported_backends() & ~ev_recommended_backends() & EVBACKEND_KQUEUE) {
1822     return ev_recommended_backends() | EVBACKEND_KQUEUE;
1823   }
1824 
1825   return 0;
1826 }
1827 } // namespace
1828 
1829 class AcceptHandler {
1830 public:
AcceptHandler(HttpServer * sv,Sessions * sessions,const Config * config)1831   AcceptHandler(HttpServer *sv, Sessions *sessions, const Config *config)
1832       : sessions_(sessions), config_(config), next_worker_(0) {
1833     if (config_->num_worker == 1) {
1834       return;
1835     }
1836     for (size_t i = 0; i < config_->num_worker; ++i) {
1837       if (config_->verbose) {
1838         std::cerr << "spawning thread #" << i << std::endl;
1839       }
1840       auto worker = std::make_unique<Worker>();
1841       auto loop = ev_loop_new(get_ev_loop_flags());
1842       worker->sessions = std::make_unique<Sessions>(sv, loop, config_,
1843                                                     sessions_->get_ssl_ctx());
1844       ev_async_init(&worker->w, worker_acceptcb);
1845       worker->w.data = worker.get();
1846       ev_async_start(loop, &worker->w);
1847 
1848       auto t = std::thread(run_worker, worker.get());
1849       t.detach();
1850       workers_.push_back(std::move(worker));
1851     }
1852   }
accept_connection(int fd)1853   void accept_connection(int fd) {
1854     if (config_->num_worker == 1) {
1855       sessions_->accept_connection(fd);
1856       return;
1857     }
1858 
1859     // Dispatch client to the one of the worker threads, in a round
1860     // robin manner.
1861     auto &worker = workers_[next_worker_];
1862     if (next_worker_ == config_->num_worker - 1) {
1863       next_worker_ = 0;
1864     } else {
1865       ++next_worker_;
1866     }
1867     {
1868       std::lock_guard<std::mutex> lock(worker->m);
1869       worker->q.push_back({fd});
1870     }
1871     ev_async_send(worker->sessions->get_loop(), &worker->w);
1872   }
1873 
1874 private:
1875   std::vector<std::unique_ptr<Worker>> workers_;
1876   Sessions *sessions_;
1877   const Config *config_;
1878   // In multi threading mode, this points to the next thread that
1879   // client will be dispatched.
1880   size_t next_worker_;
1881 };
1882 
1883 namespace {
1884 void acceptcb(struct ev_loop *loop, ev_io *w, int revents);
1885 } // namespace
1886 
1887 class ListenEventHandler {
1888 public:
ListenEventHandler(Sessions * sessions,int fd,std::shared_ptr<AcceptHandler> acceptor)1889   ListenEventHandler(Sessions *sessions, int fd,
1890                      std::shared_ptr<AcceptHandler> acceptor)
1891       : acceptor_(std::move(acceptor)), sessions_(sessions), fd_(fd) {
1892     ev_io_init(&w_, acceptcb, fd, EV_READ);
1893     w_.data = this;
1894     ev_io_start(sessions_->get_loop(), &w_);
1895   }
accept_connection()1896   void accept_connection() {
1897     for (;;) {
1898 #ifdef HAVE_ACCEPT4
1899       auto fd = accept4(fd_, nullptr, nullptr, SOCK_NONBLOCK);
1900 #else  // !HAVE_ACCEPT4
1901       auto fd = accept(fd_, nullptr, nullptr);
1902 #endif // !HAVE_ACCEPT4
1903       if (fd == -1) {
1904         break;
1905       }
1906 #ifndef HAVE_ACCEPT4
1907       util::make_socket_nonblocking(fd);
1908 #endif // !HAVE_ACCEPT4
1909       acceptor_->accept_connection(fd);
1910     }
1911   }
1912 
1913 private:
1914   ev_io w_;
1915   std::shared_ptr<AcceptHandler> acceptor_;
1916   Sessions *sessions_;
1917   int fd_;
1918 };
1919 
1920 namespace {
acceptcb(struct ev_loop * loop,ev_io * w,int revents)1921 void acceptcb(struct ev_loop *loop, ev_io *w, int revents) {
1922   auto handler = static_cast<ListenEventHandler *>(w->data);
1923   handler->accept_connection();
1924 }
1925 } // namespace
1926 
1927 namespace {
make_status_body(int status,uint16_t port)1928 FileEntry make_status_body(int status, uint16_t port) {
1929   BlockAllocator balloc(1024, 1024);
1930 
1931   auto status_string = http2::stringify_status(balloc, status);
1932   auto reason_pharase = http2::get_reason_phrase(status);
1933 
1934   std::string body;
1935   body = "<html><head><title>";
1936   body += status_string;
1937   body += ' ';
1938   body += reason_pharase;
1939   body += "</title></head><body><h1>";
1940   body += status_string;
1941   body += ' ';
1942   body += reason_pharase;
1943   body += "</h1><hr><address>";
1944   body += NGHTTPD_SERVER;
1945   body += " at port ";
1946   body += util::utos(port);
1947   body += "</address>";
1948   body += "</body></html>";
1949 
1950   char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
1951   int fd = mkstemp(tempfn);
1952   if (fd == -1) {
1953     auto error = errno;
1954     std::cerr << "Could not open status response body file: errno=" << error;
1955     assert(0);
1956   }
1957   unlink(tempfn);
1958   ssize_t nwrite;
1959   while ((nwrite = write(fd, body.c_str(), body.size())) == -1 &&
1960          errno == EINTR)
1961     ;
1962   if (nwrite == -1) {
1963     auto error = errno;
1964     std::cerr << "Could not write status response body into file: errno="
1965               << error;
1966     assert(0);
1967   }
1968 
1969   return FileEntry(util::utos(status), nwrite, 0, fd, nullptr, {});
1970 }
1971 } // namespace
1972 
1973 // index into HttpServer::status_pages_
1974 enum {
1975   IDX_200,
1976   IDX_301,
1977   IDX_400,
1978   IDX_404,
1979   IDX_405,
1980 };
1981 
HttpServer(const Config * config)1982 HttpServer::HttpServer(const Config *config) : config_(config) {
1983   status_pages_ = std::vector<StatusPage>{
1984       {"200", make_status_body(200, config_->port)},
1985       {"301", make_status_body(301, config_->port)},
1986       {"400", make_status_body(400, config_->port)},
1987       {"404", make_status_body(404, config_->port)},
1988       {"405", make_status_body(405, config_->port)},
1989   };
1990 }
1991 
1992 namespace {
verify_callback(int preverify_ok,X509_STORE_CTX * ctx)1993 int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
1994   // We don't verify the client certificate. Just request it for the
1995   // testing purpose.
1996   return 1;
1997 }
1998 } // namespace
1999 
2000 namespace {
start_listen(HttpServer * sv,struct ev_loop * loop,Sessions * sessions,const Config * config)2001 int start_listen(HttpServer *sv, struct ev_loop *loop, Sessions *sessions,
2002                  const Config *config) {
2003   int r;
2004   bool ok = false;
2005   const char *addr = nullptr;
2006 
2007   std::shared_ptr<AcceptHandler> acceptor;
2008   auto service = util::utos(config->port);
2009 
2010   addrinfo hints{};
2011   hints.ai_family = AF_UNSPEC;
2012   hints.ai_socktype = SOCK_STREAM;
2013   hints.ai_flags = AI_PASSIVE;
2014 #ifdef AI_ADDRCONFIG
2015   hints.ai_flags |= AI_ADDRCONFIG;
2016 #endif // AI_ADDRCONFIG
2017 
2018   if (!config->address.empty()) {
2019     addr = config->address.c_str();
2020   }
2021 
2022   addrinfo *res, *rp;
2023   r = getaddrinfo(addr, service.c_str(), &hints, &res);
2024   if (r != 0) {
2025     std::cerr << "getaddrinfo() failed: " << gai_strerror(r) << std::endl;
2026     return -1;
2027   }
2028 
2029   for (rp = res; rp; rp = rp->ai_next) {
2030     int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
2031     if (fd == -1) {
2032       continue;
2033     }
2034     int val = 1;
2035     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
2036                    static_cast<socklen_t>(sizeof(val))) == -1) {
2037       close(fd);
2038       continue;
2039     }
2040     (void)util::make_socket_nonblocking(fd);
2041 #ifdef IPV6_V6ONLY
2042     if (rp->ai_family == AF_INET6) {
2043       if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
2044                      static_cast<socklen_t>(sizeof(val))) == -1) {
2045         close(fd);
2046         continue;
2047       }
2048     }
2049 #endif // IPV6_V6ONLY
2050     if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 && listen(fd, 1000) == 0) {
2051       if (!acceptor) {
2052         acceptor = std::make_shared<AcceptHandler>(sv, sessions, config);
2053       }
2054       new ListenEventHandler(sessions, fd, acceptor);
2055 
2056       if (config->verbose) {
2057         std::string s = util::numeric_name(rp->ai_addr, rp->ai_addrlen);
2058         std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6") << ": listen "
2059                   << s << ":" << config->port << std::endl;
2060       }
2061       ok = true;
2062       continue;
2063     } else {
2064       std::cerr << strerror(errno) << std::endl;
2065     }
2066     close(fd);
2067   }
2068   freeaddrinfo(res);
2069 
2070   if (!ok) {
2071     return -1;
2072   }
2073   return 0;
2074 }
2075 } // namespace
2076 
2077 namespace {
alpn_select_proto_cb(SSL * ssl,const unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void * arg)2078 int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
2079                          unsigned char *outlen, const unsigned char *in,
2080                          unsigned int inlen, void *arg) {
2081   auto config = static_cast<HttpServer *>(arg)->get_config();
2082   if (config->verbose) {
2083     std::cout << "[ALPN] client offers:" << std::endl;
2084   }
2085   if (config->verbose) {
2086     for (unsigned int i = 0; i < inlen; i += in[i] + 1) {
2087       std::cout << " * ";
2088       std::cout.write(reinterpret_cast<const char *>(&in[i + 1]), in[i]);
2089       std::cout << std::endl;
2090     }
2091   }
2092   if (!util::select_h2(out, outlen, in, inlen)) {
2093     return SSL_TLSEXT_ERR_NOACK;
2094   }
2095   return SSL_TLSEXT_ERR_OK;
2096 }
2097 } // namespace
2098 
run()2099 int HttpServer::run() {
2100   SSL_CTX *ssl_ctx = nullptr;
2101   std::vector<unsigned char> next_proto;
2102 
2103   if (!config_->no_tls) {
2104     ssl_ctx = SSL_CTX_new(TLS_server_method());
2105     if (!ssl_ctx) {
2106       std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2107       return -1;
2108     }
2109 
2110     auto ssl_opts = (SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS) |
2111                     SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION |
2112                     SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
2113                     SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET |
2114                     SSL_OP_CIPHER_SERVER_PREFERENCE;
2115 
2116 #ifdef SSL_OP_ENABLE_KTLS
2117     if (config_->ktls) {
2118       ssl_opts |= SSL_OP_ENABLE_KTLS;
2119     }
2120 #endif // SSL_OP_ENABLE_KTLS
2121 
2122     SSL_CTX_set_options(ssl_ctx, ssl_opts);
2123     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
2124     SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
2125 
2126     if (nghttp2::tls::ssl_ctx_set_proto_versions(
2127             ssl_ctx, nghttp2::tls::NGHTTP2_TLS_MIN_VERSION,
2128             nghttp2::tls::NGHTTP2_TLS_MAX_VERSION) != 0) {
2129       std::cerr << "Could not set TLS versions" << std::endl;
2130       return -1;
2131     }
2132 
2133     if (SSL_CTX_set_cipher_list(ssl_ctx, tls::DEFAULT_CIPHER_LIST.data()) ==
2134         0) {
2135       std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2136       return -1;
2137     }
2138 
2139     const unsigned char sid_ctx[] = "nghttpd";
2140     SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1);
2141     SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
2142 
2143 #ifndef OPENSSL_NO_EC
2144     if (SSL_CTX_set1_curves_list(ssl_ctx, "P-256") != 1) {
2145       std::cerr << "SSL_CTX_set1_curves_list failed: "
2146                 << ERR_error_string(ERR_get_error(), nullptr);
2147       return -1;
2148     }
2149 #endif // OPENSSL_NO_EC
2150 
2151     if (!config_->dh_param_file.empty()) {
2152       // Read DH parameters from file
2153       auto bio = BIO_new_file(config_->dh_param_file.c_str(), "rb");
2154       if (bio == nullptr) {
2155         std::cerr << "BIO_new_file() failed: "
2156                   << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2157         return -1;
2158       }
2159 
2160 #if OPENSSL_3_0_0_API
2161       EVP_PKEY *dh = nullptr;
2162       auto dctx = OSSL_DECODER_CTX_new_for_pkey(
2163           &dh, "PEM", nullptr, "DH", OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS,
2164           nullptr, nullptr);
2165 
2166       if (!OSSL_DECODER_from_bio(dctx, bio)) {
2167         std::cerr << "OSSL_DECODER_from_bio() failed: "
2168                   << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2169         return -1;
2170       }
2171 
2172       if (SSL_CTX_set0_tmp_dh_pkey(ssl_ctx, dh) != 1) {
2173         std::cerr << "SSL_CTX_set0_tmp_dh_pkey failed: "
2174                   << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2175         return -1;
2176       }
2177 #else  // !OPENSSL_3_0_0_API
2178       auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
2179 
2180       if (dh == nullptr) {
2181         std::cerr << "PEM_read_bio_DHparams() failed: "
2182                   << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
2183         return -1;
2184       }
2185 
2186       SSL_CTX_set_tmp_dh(ssl_ctx, dh);
2187       DH_free(dh);
2188 #endif // !OPENSSL_3_0_0_API
2189       BIO_free(bio);
2190     }
2191 
2192     if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config_->private_key_file.c_str(),
2193                                     SSL_FILETYPE_PEM) != 1) {
2194       std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl;
2195       return -1;
2196     }
2197     if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
2198                                            config_->cert_file.c_str()) != 1) {
2199       std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl;
2200       return -1;
2201     }
2202     if (SSL_CTX_check_private_key(ssl_ctx) != 1) {
2203       std::cerr << "SSL_CTX_check_private_key failed." << std::endl;
2204       return -1;
2205     }
2206     if (config_->verify_client) {
2207       SSL_CTX_set_verify(ssl_ctx,
2208                          SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
2209                              SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
2210                          verify_callback);
2211     }
2212 
2213     next_proto = util::get_default_alpn();
2214 
2215     // ALPN selection callback
2216     SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, this);
2217 
2218 #if defined(NGHTTP2_OPENSSL_IS_BORINGSSL) && defined(HAVE_LIBBROTLI)
2219     if (!SSL_CTX_add_cert_compression_alg(
2220             ssl_ctx, nghttp2::tls::CERTIFICATE_COMPRESSION_ALGO_BROTLI,
2221             nghttp2::tls::cert_compress, nghttp2::tls::cert_decompress)) {
2222       std::cerr << "SSL_CTX_add_cert_compression_alg failed." << std::endl;
2223       return -1;
2224     }
2225 #endif // NGHTTP2_OPENSSL_IS_BORINGSSL && HAVE_LIBBROTLI
2226   }
2227 
2228   auto loop = EV_DEFAULT;
2229 
2230   Sessions sessions(this, loop, config_, ssl_ctx);
2231   if (start_listen(this, loop, &sessions, config_) != 0) {
2232     std::cerr << "Could not listen" << std::endl;
2233     if (ssl_ctx) {
2234       SSL_CTX_free(ssl_ctx);
2235     }
2236     return -1;
2237   }
2238 
2239   ev_run(loop, 0);
2240 
2241   SSL_CTX_free(ssl_ctx);
2242 
2243   return 0;
2244 }
2245 
get_config() const2246 const Config *HttpServer::get_config() const { return config_; }
2247 
get_status_page(int status) const2248 const StatusPage *HttpServer::get_status_page(int status) const {
2249   switch (status) {
2250   case 200:
2251     return &status_pages_[IDX_200];
2252   case 301:
2253     return &status_pages_[IDX_301];
2254   case 400:
2255     return &status_pages_[IDX_400];
2256   case 404:
2257     return &status_pages_[IDX_404];
2258   case 405:
2259     return &status_pages_[IDX_405];
2260   default:
2261     assert(0);
2262   }
2263   return nullptr;
2264 }
2265 
2266 } // namespace nghttp2
2267