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