2 * nghttp2 - HTTP/2 C Library
4 * Copyright (c) 2013 Tatsuhiro Tsujikawa
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:
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
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.
25 #include "HttpServer.h"
28 #ifdef HAVE_SYS_SOCKET_H
29 #include <sys/socket.h>
30 #endif // HAVE_SYS_SOCKET_H
33 #endif // HAVE_NETDB_H
36 #endif // HAVE_UNISTD_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
55 #include <openssl/err.h>
59 #include "app_helper.h"
72 const std::string DEFAULT_HTML = "index.html";
73 const std::string NGHTTPD_SERVER = "nghttpd nghttp2/" NGHTTP2_VERSION;
77 void delete_handler(Http2Handler *handler) {
78 handler->remove_self();
84 void print_session_id(int64_t id) { std::cout << "[id=" << id << "] "; }
88 template <typename Array> void append_nv(Stream *stream, const Array &nva) {
89 for (size_t i = 0; i < nva.size(); ++i) {
91 auto token = http2::lookup_token(nv.name, nv.namelen);
93 http2::index_header(stream->hdidx, token, i);
95 http2::add_header(stream->headers, nv.name, nv.namelen, nv.value,
96 nv.valuelen, nv.flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
102 : stream_read_timeout(60.), stream_write_timeout(60.), data_ptr(nullptr),
103 padding(0), num_worker(1), max_concurrent_streams(100),
104 header_table_size(-1), port(0), verbose(false), daemon(false),
105 verify_client(false), no_tls(false), error_gzip(false),
106 early_response(false), hexdump(false), echo_upload(false) {}
111 void stream_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
113 auto stream = static_cast<Stream *>(w->data);
114 auto hd = stream->handler;
115 auto config = hd->get_config();
117 ev_timer_stop(hd->get_loop(), &stream->rtimer);
118 ev_timer_stop(hd->get_loop(), &stream->wtimer);
120 if (config->verbose) {
121 print_session_id(hd->session_id());
123 std::cout << " timeout stream_id=" << stream->stream_id << std::endl;
126 hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
136 void add_stream_read_timeout(Stream *stream) {
137 auto hd = stream->handler;
138 ev_timer_again(hd->get_loop(), &stream->rtimer);
143 void add_stream_read_timeout_if_pending(Stream *stream) {
144 auto hd = stream->handler;
145 if (ev_is_active(&stream->rtimer)) {
146 ev_timer_again(hd->get_loop(), &stream->rtimer);
152 void add_stream_write_timeout(Stream *stream) {
153 auto hd = stream->handler;
154 ev_timer_again(hd->get_loop(), &stream->wtimer);
159 void remove_stream_read_timeout(Stream *stream) {
160 auto hd = stream->handler;
161 ev_timer_stop(hd->get_loop(), &stream->rtimer);
166 void remove_stream_write_timeout(Stream *stream) {
167 auto hd = stream->handler;
168 ev_timer_stop(hd->get_loop(), &stream->wtimer);
173 void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config);
178 Sessions(HttpServer *sv, struct ev_loop *loop, const Config *config,
180 : sv_(sv), loop_(loop), config_(config), ssl_ctx_(ssl_ctx),
181 callbacks_(nullptr), next_session_id_(1), tstamp_cached_(ev_now(loop)),
182 cached_date_(util::http_date(tstamp_cached_)) {
183 nghttp2_session_callbacks_new(&callbacks_);
185 fill_callback(callbacks_, config_);
188 for (auto handler : handlers_) {
191 nghttp2_session_callbacks_del(callbacks_);
193 void add_handler(Http2Handler *handler) { handlers_.insert(handler); }
194 void remove_handler(Http2Handler *handler) { handlers_.erase(handler); }
195 SSL_CTX *get_ssl_ctx() const { return ssl_ctx_; }
196 SSL *ssl_session_new(int fd) {
197 SSL *ssl = SSL_new(ssl_ctx_);
199 std::cerr << "SSL_new() failed" << std::endl;
202 if (SSL_set_fd(ssl, fd) == 0) {
203 std::cerr << "SSL_set_fd() failed" << std::endl;
209 const Config *get_config() const { return config_; }
210 struct ev_loop *get_loop() const {
213 int64_t get_next_session_id() {
214 auto session_id = next_session_id_;
215 if (next_session_id_ == std::numeric_limits<int64_t>::max()) {
216 next_session_id_ = 1;
222 const nghttp2_session_callbacks *get_callbacks() const { return callbacks_; }
223 void accept_connection(int fd) {
224 util::make_socket_nodelay(fd);
227 ssl = ssl_session_new(fd);
234 make_unique<Http2Handler>(this, fd, ssl, get_next_session_id());
235 handler->setup_bev();
237 if (handler->connection_made() != 0) {
241 add_handler(handler.release());
243 void update_cached_date() { cached_date_ = util::http_date(tstamp_cached_); }
244 const std::string &get_cached_date() {
245 auto t = ev_now(loop_);
246 if (t != tstamp_cached_) {
248 update_cached_date();
252 FileEntry *get_cached_fd(const std::string &path) {
253 auto i = fd_cache_.find(path);
254 if (i == std::end(fd_cache_)) {
257 auto &ent = (*i).second;
261 FileEntry *cache_fd(const std::string &path, const FileEntry &ent) {
262 auto rv = fd_cache_.emplace(path, ent);
263 return &(*rv.first).second;
265 void release_fd(const std::string &path) {
266 auto i = fd_cache_.find(path);
267 if (i == std::end(fd_cache_)) {
270 auto &ent = (*i).second;
271 if (--ent.usecount == 0) {
276 const HttpServer *get_server() const { return sv_; }
279 std::set<Http2Handler *> handlers_;
280 // cache for file descriptors to read file.
281 std::map<std::string, FileEntry> fd_cache_;
283 struct ev_loop *loop_;
284 const Config *config_;
286 nghttp2_session_callbacks *callbacks_;
287 int64_t next_session_id_;
288 ev_tstamp tstamp_cached_;
289 std::string cached_date_;
292 Stream::Stream(Http2Handler *handler, int32_t stream_id)
293 : handler(handler), file_ent(nullptr), body_length(0), body_offset(0),
294 stream_id(stream_id), echo_upload(false) {
295 auto config = handler->get_config();
296 ev_timer_init(&rtimer, stream_timeout_cb, 0., config->stream_read_timeout);
297 ev_timer_init(&wtimer, stream_timeout_cb, 0., config->stream_write_timeout);
303 http2::init_hdidx(hdidx);
307 if (file_ent != nullptr) {
308 auto sessions = handler->get_sessions();
309 sessions->release_fd(file_ent->path);
312 auto loop = handler->get_loop();
313 ev_timer_stop(loop, &rtimer);
314 ev_timer_stop(loop, &wtimer);
318 void on_session_closed(Http2Handler *hd, int64_t session_id) {
319 if (hd->get_config()->verbose) {
320 print_session_id(session_id);
322 std::cout << " closed" << std::endl;
328 void settings_timeout_cb(struct ev_loop *loop, ev_timer *w, int revents) {
329 auto hd = static_cast<Http2Handler *>(w->data);
330 hd->terminate_session(NGHTTP2_SETTINGS_TIMEOUT);
336 void readcb(struct ev_loop *loop, ev_io *w, int revents) {
338 auto handler = static_cast<Http2Handler *>(w->data);
340 rv = handler->on_read();
342 delete_handler(handler);
348 void writecb(struct ev_loop *loop, ev_io *w, int revents) {
350 auto handler = static_cast<Http2Handler *>(w->data);
352 rv = handler->on_write();
354 delete_handler(handler);
359 Http2Handler::Http2Handler(Sessions *sessions, int fd, SSL *ssl,
361 : session_id_(session_id), session_(nullptr), sessions_(sessions),
362 ssl_(ssl), data_pending_(nullptr), data_pendinglen_(0), fd_(fd) {
363 ev_timer_init(&settings_timerev_, settings_timeout_cb, 10., 0.);
364 ev_io_init(&wev_, writecb, fd, EV_WRITE);
365 ev_io_init(&rev_, readcb, fd, EV_READ);
367 settings_timerev_.data = this;
371 auto loop = sessions_->get_loop();
372 ev_io_start(loop, &rev_);
375 SSL_set_accept_state(ssl);
376 read_ = &Http2Handler::tls_handshake;
377 write_ = &Http2Handler::tls_handshake;
379 read_ = &Http2Handler::read_clear;
380 write_ = &Http2Handler::write_clear;
384 Http2Handler::~Http2Handler() {
385 on_session_closed(this, session_id_);
386 nghttp2_session_del(session_);
388 SSL_set_shutdown(ssl_, SSL_RECEIVED_SHUTDOWN);
392 auto loop = sessions_->get_loop();
393 ev_timer_stop(loop, &settings_timerev_);
394 ev_io_stop(loop, &rev_);
395 ev_io_stop(loop, &wev_);
399 shutdown(fd_, SHUT_WR);
403 void Http2Handler::remove_self() { sessions_->remove_handler(this); }
405 struct ev_loop *Http2Handler::get_loop() const {
406 return sessions_->get_loop();
409 Http2Handler::WriteBuf *Http2Handler::get_wb() { return &wb_; }
411 int Http2Handler::setup_bev() { return 0; }
413 int Http2Handler::fill_wb() {
415 auto n = std::min(wb_.wleft(), data_pendinglen_);
416 wb_.write(data_pending_, n);
417 if (n < data_pendinglen_) {
419 data_pendinglen_ -= n;
423 data_pending_ = nullptr;
424 data_pendinglen_ = 0;
429 auto datalen = nghttp2_session_mem_send(session_, &data);
432 std::cerr << "nghttp2_session_mem_send() returned error: "
433 << nghttp2_strerror(datalen) << std::endl;
439 auto n = wb_.write(data, datalen);
440 if (n < static_cast<decltype(n)>(datalen)) {
441 data_pending_ = data + n;
442 data_pendinglen_ = datalen - n;
449 int Http2Handler::read_clear() {
451 std::array<uint8_t, 8192> buf;
455 while ((nread = read(fd_, buf.data(), buf.size())) == -1 && errno == EINTR)
458 if (errno == EAGAIN || errno == EWOULDBLOCK) {
467 if (get_config()->hexdump) {
468 util::hexdump(stdout, buf.data(), nread);
471 rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
473 if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
474 std::cerr << "nghttp2_session_mem_recv() returned error: "
475 << nghttp2_strerror(rv) << std::endl;
481 return write_(*this);
484 int Http2Handler::write_clear() {
485 auto loop = sessions_->get_loop();
487 if (wb_.rleft() > 0) {
489 while ((nwrite = write(fd_, wb_.pos, wb_.rleft())) == -1 &&
493 if (errno == EAGAIN || errno == EWOULDBLOCK) {
494 ev_io_start(loop, &wev_);
503 if (fill_wb() != 0) {
506 if (wb_.rleft() == 0) {
511 if (wb_.rleft() == 0) {
512 ev_io_stop(loop, &wev_);
514 ev_io_start(loop, &wev_);
517 if (nghttp2_session_want_read(session_) == 0 &&
518 nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
525 int Http2Handler::tls_handshake() {
526 ev_io_stop(sessions_->get_loop(), &wev_);
530 auto rv = SSL_do_handshake(ssl_);
537 auto err = SSL_get_error(ssl_, rv);
539 case SSL_ERROR_WANT_READ:
541 case SSL_ERROR_WANT_WRITE:
542 ev_io_start(sessions_->get_loop(), &wev_);
549 if (sessions_->get_config()->verbose) {
550 std::cerr << "SSL/TLS handshake completed" << std::endl;
553 if (verify_npn_result() != 0) {
557 read_ = &Http2Handler::read_tls;
558 write_ = &Http2Handler::write_tls;
560 if (connection_made() != 0) {
567 int Http2Handler::read_tls() {
568 std::array<uint8_t, 8192> buf;
573 auto rv = SSL_read(ssl_, buf.data(), buf.size());
580 auto err = SSL_get_error(ssl_, rv);
582 case SSL_ERROR_WANT_READ:
584 case SSL_ERROR_WANT_WRITE:
585 // renegotiation started
594 if (get_config()->hexdump) {
595 util::hexdump(stdout, buf.data(), nread);
598 rv = nghttp2_session_mem_recv(session_, buf.data(), nread);
600 if (rv != NGHTTP2_ERR_BAD_CLIENT_MAGIC) {
601 std::cerr << "nghttp2_session_mem_recv() returned error: "
602 << nghttp2_strerror(rv) << std::endl;
609 return write_(*this);
612 int Http2Handler::write_tls() {
613 auto loop = sessions_->get_loop();
618 if (wb_.rleft() > 0) {
619 auto rv = SSL_write(ssl_, wb_.pos, wb_.rleft());
626 auto err = SSL_get_error(ssl_, rv);
628 case SSL_ERROR_WANT_READ:
629 // renegotiation started
631 case SSL_ERROR_WANT_WRITE:
632 ev_io_start(sessions_->get_loop(), &wev_);
643 if (fill_wb() != 0) {
646 if (wb_.rleft() == 0) {
651 if (wb_.rleft() == 0) {
652 ev_io_stop(loop, &wev_);
654 ev_io_start(loop, &wev_);
657 if (nghttp2_session_want_read(session_) == 0 &&
658 nghttp2_session_want_write(session_) == 0 && wb_.rleft() == 0) {
665 int Http2Handler::on_read() { return read_(*this); }
667 int Http2Handler::on_write() { return write_(*this); }
669 int Http2Handler::connection_made() {
672 r = nghttp2_session_server_new(&session_, sessions_->get_callbacks(), this);
678 auto config = sessions_->get_config();
679 std::array<nghttp2_settings_entry, 4> entry;
682 entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
683 entry[0].value = config->max_concurrent_streams;
685 if (config->header_table_size >= 0) {
686 entry[niv].settings_id = NGHTTP2_SETTINGS_HEADER_TABLE_SIZE;
687 entry[niv].value = config->header_table_size;
690 r = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), niv);
695 ev_timer_start(sessions_->get_loop(), &settings_timerev_);
700 int Http2Handler::verify_npn_result() {
701 const unsigned char *next_proto = nullptr;
702 unsigned int next_proto_len;
703 // Check the negotiated protocol in NPN or ALPN
704 SSL_get0_next_proto_negotiated(ssl_, &next_proto, &next_proto_len);
705 for (int i = 0; i < 2; ++i) {
707 if (sessions_->get_config()->verbose) {
708 std::string proto(next_proto, next_proto + next_proto_len);
709 std::cout << "The negotiated protocol: " << proto << std::endl;
711 if (util::check_h2_is_selected(next_proto, next_proto_len)) {
716 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
717 SSL_get0_alpn_selected(ssl_, &next_proto, &next_proto_len);
718 #else // OPENSSL_VERSION_NUMBER < 0x10002000L
720 #endif // OPENSSL_VERSION_NUMBER < 0x10002000L
723 if (sessions_->get_config()->verbose) {
724 std::cerr << "Client did not advertise HTTP/2 protocol."
725 << " (nghttp2 expects " << NGHTTP2_PROTO_VERSION_ID << ")"
731 int Http2Handler::submit_file_response(const std::string &status,
732 Stream *stream, time_t last_modified,
734 nghttp2_data_provider *data_prd) {
735 std::string content_length = util::utos(file_length);
736 std::string last_modified_str;
737 auto nva = make_array(http2::make_nv_ls(":status", status),
738 http2::make_nv_ls("server", NGHTTPD_SERVER),
739 http2::make_nv_ls("content-length", content_length),
740 http2::make_nv_ll("cache-control", "max-age=3600"),
741 http2::make_nv_ls("date", sessions_->get_cached_date()),
742 http2::make_nv_ll("", ""), http2::make_nv_ll("", ""));
744 if (last_modified != 0) {
745 last_modified_str = util::http_date(last_modified);
746 nva[nvlen++] = http2::make_nv_ls("last-modified", last_modified_str);
748 auto &trailer = get_config()->trailer;
749 std::string trailer_names;
750 if (!trailer.empty()) {
751 trailer_names = trailer[0].name;
752 for (size_t i = 1; i < trailer.size(); ++i) {
753 trailer_names += ", ";
754 trailer_names += trailer[i].name;
756 nva[nvlen++] = http2::make_nv_ls("trailer", trailer_names);
758 return nghttp2_submit_response(session_, stream->stream_id, nva.data(), nvlen,
762 int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
763 const Headers &headers,
764 nghttp2_data_provider *data_prd) {
765 auto nva = std::vector<nghttp2_nv>();
766 nva.reserve(3 + headers.size());
767 nva.push_back(http2::make_nv_ls(":status", status));
768 nva.push_back(http2::make_nv_ls("server", NGHTTPD_SERVER));
769 nva.push_back(http2::make_nv_ls("date", sessions_->get_cached_date()));
770 for (auto &nv : headers) {
771 nva.push_back(http2::make_nv(nv.name, nv.value, nv.no_index));
773 int r = nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(),
778 int Http2Handler::submit_response(const std::string &status, int32_t stream_id,
779 nghttp2_data_provider *data_prd) {
780 auto nva = make_array(http2::make_nv_ls(":status", status),
781 http2::make_nv_ls("server", NGHTTPD_SERVER));
782 return nghttp2_submit_response(session_, stream_id, nva.data(), nva.size(),
786 int Http2Handler::submit_non_final_response(const std::string &status,
788 auto nva = make_array(http2::make_nv_ls(":status", status));
789 return nghttp2_submit_headers(session_, NGHTTP2_FLAG_NONE, stream_id, nullptr,
790 nva.data(), nva.size(), nullptr);
793 int Http2Handler::submit_push_promise(Stream *stream,
794 const std::string &push_path) {
796 http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers);
800 http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers);
804 make_array(http2::make_nv_ll(":method", "GET"),
805 http2::make_nv_ls(":path", push_path),
806 get_config()->no_tls ? http2::make_nv_ll(":scheme", "http")
807 : http2::make_nv_ll(":scheme", "https"),
808 http2::make_nv_ls(":authority", authority->value));
810 auto promised_stream_id = nghttp2_submit_push_promise(
811 session_, NGHTTP2_FLAG_END_HEADERS, stream->stream_id, nva.data(),
812 nva.size(), nullptr);
814 if (promised_stream_id < 0) {
815 return promised_stream_id;
818 auto promised_stream = make_unique<Stream>(this, promised_stream_id);
820 append_nv(promised_stream.get(), nva);
821 add_stream(promised_stream_id, std::move(promised_stream));
826 int Http2Handler::submit_rst_stream(Stream *stream, uint32_t error_code) {
827 remove_stream_read_timeout(stream);
828 remove_stream_write_timeout(stream);
830 return nghttp2_submit_rst_stream(session_, NGHTTP2_FLAG_NONE,
831 stream->stream_id, error_code);
834 void Http2Handler::add_stream(int32_t stream_id,
835 std::unique_ptr<Stream> stream) {
836 id2stream_[stream_id] = std::move(stream);
839 void Http2Handler::remove_stream(int32_t stream_id) {
840 id2stream_.erase(stream_id);
843 Stream *Http2Handler::get_stream(int32_t stream_id) {
844 auto itr = id2stream_.find(stream_id);
845 if (itr == std::end(id2stream_)) {
848 return (*itr).second.get();
852 int64_t Http2Handler::session_id() const { return session_id_; }
854 Sessions *Http2Handler::get_sessions() const { return sessions_; }
856 const Config *Http2Handler::get_config() const {
857 return sessions_->get_config();
860 void Http2Handler::remove_settings_timer() {
861 ev_timer_stop(sessions_->get_loop(), &settings_timerev_);
864 void Http2Handler::terminate_session(uint32_t error_code) {
865 nghttp2_session_terminate_session(session_, error_code);
868 ssize_t file_read_callback(nghttp2_session *session, int32_t stream_id,
869 uint8_t *buf, size_t length, uint32_t *data_flags,
870 nghttp2_data_source *source, void *user_data) {
872 auto hd = static_cast<Http2Handler *>(user_data);
873 auto stream = hd->get_stream(stream_id);
875 auto nread = std::min(stream->body_length - stream->body_offset,
876 static_cast<int64_t>(length));
878 *data_flags |= NGHTTP2_DATA_FLAG_NO_COPY;
880 if (nread == 0 || stream->body_length == stream->body_offset + nread) {
881 *data_flags |= NGHTTP2_DATA_FLAG_EOF;
883 auto config = hd->get_config();
884 if (!config->trailer.empty()) {
885 std::vector<nghttp2_nv> nva;
886 nva.reserve(config->trailer.size());
887 for (auto &kv : config->trailer) {
888 nva.push_back(http2::make_nv(kv.name, kv.value, kv.no_index));
890 rv = nghttp2_submit_trailer(session, stream_id, nva.data(), nva.size());
892 if (nghttp2_is_fatal(rv)) {
893 return NGHTTP2_ERR_CALLBACK_FAILURE;
896 *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
900 if (nghttp2_session_get_stream_remote_close(session, stream_id) == 0) {
901 remove_stream_read_timeout(stream);
902 remove_stream_write_timeout(stream);
904 hd->submit_rst_stream(stream, NGHTTP2_NO_ERROR);
912 void prepare_status_response(Stream *stream, Http2Handler *hd, int status) {
913 auto sessions = hd->get_sessions();
914 auto status_page = sessions->get_server()->get_status_page(status);
915 auto file_ent = &status_page->file_ent;
917 // we don't set stream->file_ent since we don't want to expire it.
918 stream->body_length = file_ent->length;
919 nghttp2_data_provider data_prd;
920 data_prd.source.fd = file_ent->fd;
921 data_prd.read_callback = file_read_callback;
924 headers.emplace_back("content-type", "text/html; charset=UTF-8");
925 hd->submit_response(status_page->status, stream->stream_id, headers,
931 void prepare_echo_response(Stream *stream, Http2Handler *hd) {
932 auto length = lseek(stream->file_ent->fd, 0, SEEK_END);
934 hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
937 stream->body_length = length;
938 if (lseek(stream->file_ent->fd, 0, SEEK_SET) == -1) {
939 hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
942 nghttp2_data_provider data_prd;
943 data_prd.source.fd = stream->file_ent->fd;
944 data_prd.read_callback = file_read_callback;
947 headers.emplace_back("nghttpd-response", "echo");
948 headers.emplace_back("content-length", util::utos(length));
950 hd->submit_response("200", stream->stream_id, headers, &data_prd);
955 bool prepare_upload_temp_store(Stream *stream, Http2Handler *hd) {
956 auto sessions = hd->get_sessions();
958 char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
959 auto fd = mkstemp(tempfn);
964 // Ordinary request never start with "echo:". The length is 0 for
965 // now. We will update it when we get whole request body.
966 stream->file_ent = sessions->cache_fd(std::string("echo:") + tempfn,
967 FileEntry(tempfn, 0, 0, fd));
968 stream->echo_upload = true;
974 void prepare_redirect_response(Stream *stream, Http2Handler *hd,
975 const std::string &path, int status) {
977 http2::get_header(stream->hdidx, http2::HD__SCHEME, stream->headers);
979 http2::get_header(stream->hdidx, http2::HD__AUTHORITY, stream->headers);
982 http2::get_header(stream->hdidx, http2::HD_HOST, stream->headers);
985 auto redirect_url = scheme->value;
986 redirect_url += "://";
987 redirect_url += authority->value;
988 redirect_url += path;
990 auto headers = Headers{{"location", redirect_url}};
992 auto sessions = hd->get_sessions();
993 auto status_page = sessions->get_server()->get_status_page(status);
995 hd->submit_response(status_page->status, stream->stream_id, headers, nullptr);
1000 void prepare_response(Stream *stream, Http2Handler *hd,
1001 bool allow_push = true) {
1004 http2::get_header(stream->hdidx, http2::HD__PATH, stream->headers)->value;
1006 get_header(stream->hdidx, http2::HD_IF_MODIFIED_SINCE, stream->headers);
1008 time_t last_mod = 0;
1009 bool last_mod_found = false;
1011 last_mod_found = true;
1012 last_mod = util::parse_http_date(ims->value);
1014 auto query_pos = reqpath.find("?");
1016 if (query_pos != std::string::npos) {
1017 // Do not response to this request to allow clients to test timeouts.
1018 if (reqpath.find("nghttpd_do_not_respond_to_req=yes", query_pos) !=
1019 std::string::npos) {
1022 url = reqpath.substr(0, query_pos);
1027 auto sessions = hd->get_sessions();
1029 url = util::percentDecode(std::begin(url), std::end(url));
1030 if (!util::check_path(url)) {
1031 if (stream->file_ent) {
1032 sessions->release_fd(stream->file_ent->path);
1033 stream->file_ent = nullptr;
1035 prepare_status_response(stream, hd, 404);
1038 auto push_itr = hd->get_config()->push.find(url);
1039 if (allow_push && push_itr != std::end(hd->get_config()->push)) {
1040 for (auto &push_path : (*push_itr).second) {
1041 rv = hd->submit_push_promise(stream, push_path);
1043 std::cerr << "nghttp2_submit_push_promise() returned error: "
1044 << nghttp2_strerror(rv) << std::endl;
1049 std::string path = hd->get_config()->htdocs + url;
1050 if (path[path.size() - 1] == '/') {
1051 path += DEFAULT_HTML;
1054 if (stream->echo_upload) {
1055 assert(stream->file_ent);
1056 prepare_echo_response(stream, hd);
1060 auto file_ent = sessions->get_cached_fd(path);
1062 if (file_ent == nullptr) {
1063 int file = open(path.c_str(), O_RDONLY | O_BINARY);
1065 prepare_status_response(stream, hd, 404);
1072 if (fstat(file, &buf) == -1) {
1074 prepare_status_response(stream, hd, 404);
1079 if (buf.st_mode & S_IFDIR) {
1082 if (query_pos == std::string::npos) {
1085 reqpath.insert(query_pos, "/");
1088 prepare_redirect_response(stream, hd, reqpath, 301);
1093 if (last_mod_found && buf.st_mtime <= last_mod) {
1095 prepare_status_response(stream, hd, 304);
1100 file_ent = sessions->cache_fd(
1101 path, FileEntry(path, buf.st_size, buf.st_mtime, file));
1102 } else if (last_mod_found && file_ent->mtime <= last_mod) {
1103 sessions->release_fd(file_ent->path);
1104 prepare_status_response(stream, hd, 304);
1109 stream->file_ent = file_ent;
1110 stream->body_length = file_ent->length;
1112 nghttp2_data_provider data_prd;
1114 data_prd.source.fd = file_ent->fd;
1115 data_prd.read_callback = file_read_callback;
1117 hd->submit_file_response("200", stream, file_ent->mtime, file_ent->length,
1123 int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
1124 const uint8_t *name, size_t namelen,
1125 const uint8_t *value, size_t valuelen, uint8_t flags,
1127 auto hd = static_cast<Http2Handler *>(user_data);
1128 if (hd->get_config()->verbose) {
1129 print_session_id(hd->session_id());
1130 verbose_on_header_callback(session, frame, name, namelen, value, valuelen,
1133 if (frame->hd.type != NGHTTP2_HEADERS ||
1134 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
1137 auto stream = hd->get_stream(frame->hd.stream_id);
1142 auto token = http2::lookup_token(name, namelen);
1144 http2::index_header(stream->hdidx, token, stream->headers.size());
1145 http2::add_header(stream->headers, name, namelen, value, valuelen,
1146 flags & NGHTTP2_NV_FLAG_NO_INDEX, token);
1152 int on_begin_headers_callback(nghttp2_session *session,
1153 const nghttp2_frame *frame, void *user_data) {
1154 auto hd = static_cast<Http2Handler *>(user_data);
1156 if (frame->hd.type != NGHTTP2_HEADERS ||
1157 frame->headers.cat != NGHTTP2_HCAT_REQUEST) {
1161 auto stream = make_unique<Stream>(hd, frame->hd.stream_id);
1163 add_stream_read_timeout(stream.get());
1165 hd->add_stream(frame->hd.stream_id, std::move(stream));
1172 int hd_on_frame_recv_callback(nghttp2_session *session,
1173 const nghttp2_frame *frame, void *user_data) {
1174 auto hd = static_cast<Http2Handler *>(user_data);
1175 if (hd->get_config()->verbose) {
1176 print_session_id(hd->session_id());
1177 verbose_on_frame_recv_callback(session, frame, user_data);
1179 switch (frame->hd.type) {
1180 case NGHTTP2_DATA: {
1182 auto stream = hd->get_stream(frame->hd.stream_id);
1187 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
1188 remove_stream_read_timeout(stream);
1189 if (stream->echo_upload || !hd->get_config()->early_response) {
1190 prepare_response(stream, hd);
1193 add_stream_read_timeout(stream);
1198 case NGHTTP2_HEADERS: {
1199 auto stream = hd->get_stream(frame->hd.stream_id);
1204 if (frame->headers.cat == NGHTTP2_HCAT_REQUEST) {
1207 http2::get_header(stream->hdidx, http2::HD_EXPECT, stream->headers);
1209 if (expect100 && util::strieq_l("100-continue", expect100->value)) {
1210 hd->submit_non_final_response("100", frame->hd.stream_id);
1213 auto &method = http2::get_header(stream->hdidx, http2::HD__METHOD,
1214 stream->headers)->value;
1215 if (hd->get_config()->echo_upload &&
1216 (method == "POST" || method == "PUT")) {
1217 if (!prepare_upload_temp_store(stream, hd)) {
1218 hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
1221 } else if (hd->get_config()->early_response) {
1222 prepare_response(stream, hd);
1226 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
1227 remove_stream_read_timeout(stream);
1228 if (stream->echo_upload || !hd->get_config()->early_response) {
1229 prepare_response(stream, hd);
1232 add_stream_read_timeout(stream);
1237 case NGHTTP2_SETTINGS:
1238 if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
1239 hd->remove_settings_timer();
1250 int hd_on_frame_send_callback(nghttp2_session *session,
1251 const nghttp2_frame *frame, void *user_data) {
1252 auto hd = static_cast<Http2Handler *>(user_data);
1254 if (hd->get_config()->verbose) {
1255 print_session_id(hd->session_id());
1256 verbose_on_frame_send_callback(session, frame, user_data);
1259 switch (frame->hd.type) {
1261 case NGHTTP2_HEADERS: {
1262 auto stream = hd->get_stream(frame->hd.stream_id);
1268 if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
1269 remove_stream_write_timeout(stream);
1270 } else if (std::min(nghttp2_session_get_stream_remote_window_size(
1271 session, frame->hd.stream_id),
1272 nghttp2_session_get_remote_window_size(session)) <= 0) {
1273 // If stream is blocked by flow control, enable write timeout.
1274 add_stream_read_timeout_if_pending(stream);
1275 add_stream_write_timeout(stream);
1277 add_stream_read_timeout_if_pending(stream);
1278 remove_stream_write_timeout(stream);
1283 case NGHTTP2_PUSH_PROMISE: {
1284 auto promised_stream_id = frame->push_promise.promised_stream_id;
1285 auto promised_stream = hd->get_stream(promised_stream_id);
1286 auto stream = hd->get_stream(frame->hd.stream_id);
1288 if (!stream || !promised_stream) {
1292 add_stream_read_timeout_if_pending(stream);
1293 add_stream_write_timeout(stream);
1295 prepare_response(promised_stream, hd, /*allow_push */ false);
1303 int send_data_callback(nghttp2_session *session, nghttp2_frame *frame,
1304 const uint8_t *framehd, size_t length,
1305 nghttp2_data_source *source, void *user_data) {
1306 auto hd = static_cast<Http2Handler *>(user_data);
1307 auto wb = hd->get_wb();
1308 auto padlen = frame->data.padlen;
1309 auto stream = hd->get_stream(frame->hd.stream_id);
1311 if (wb->wleft() < 9 + length + padlen) {
1312 return NGHTTP2_ERR_WOULDBLOCK;
1315 int fd = source->fd;
1319 p = std::copy_n(framehd, 9, p);
1327 while ((nread = pread(fd, p, length, stream->body_offset)) == -1 &&
1332 remove_stream_read_timeout(stream);
1333 remove_stream_write_timeout(stream);
1335 return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
1338 stream->body_offset += nread;
1344 std::fill(p, p + padlen - 1, 0);
1355 ssize_t select_padding_callback(nghttp2_session *session,
1356 const nghttp2_frame *frame, size_t max_payload,
1358 auto hd = static_cast<Http2Handler *>(user_data);
1359 return std::min(max_payload, frame->hd.length + hd->get_config()->padding);
1364 int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
1365 int32_t stream_id, const uint8_t *data,
1366 size_t len, void *user_data) {
1367 auto hd = static_cast<Http2Handler *>(user_data);
1368 auto stream = hd->get_stream(stream_id);
1374 if (stream->echo_upload) {
1375 assert(stream->file_ent);
1378 while ((n = write(stream->file_ent->fd, data, len)) == -1 &&
1382 hd->submit_rst_stream(stream, NGHTTP2_INTERNAL_ERROR);
1391 add_stream_read_timeout(stream);
1398 int on_stream_close_callback(nghttp2_session *session, int32_t stream_id,
1399 uint32_t error_code, void *user_data) {
1400 auto hd = static_cast<Http2Handler *>(user_data);
1401 hd->remove_stream(stream_id);
1402 if (hd->get_config()->verbose) {
1403 print_session_id(hd->session_id());
1405 printf(" stream_id=%d closed\n", stream_id);
1413 void fill_callback(nghttp2_session_callbacks *callbacks, const Config *config) {
1414 nghttp2_session_callbacks_set_on_stream_close_callback(
1415 callbacks, on_stream_close_callback);
1417 nghttp2_session_callbacks_set_on_frame_recv_callback(
1418 callbacks, hd_on_frame_recv_callback);
1420 nghttp2_session_callbacks_set_on_frame_send_callback(
1421 callbacks, hd_on_frame_send_callback);
1423 if (config->verbose) {
1424 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(
1425 callbacks, verbose_on_invalid_frame_recv_callback);
1428 nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
1429 callbacks, on_data_chunk_recv_callback);
1431 nghttp2_session_callbacks_set_on_header_callback(callbacks,
1432 on_header_callback);
1434 nghttp2_session_callbacks_set_on_begin_headers_callback(
1435 callbacks, on_begin_headers_callback);
1437 nghttp2_session_callbacks_set_send_data_callback(callbacks,
1438 send_data_callback);
1440 if (config->padding) {
1441 nghttp2_session_callbacks_set_select_padding_callback(
1442 callbacks, select_padding_callback);
1452 std::unique_ptr<Sessions> sessions;
1456 std::deque<ClientInfo> q;
1460 void worker_acceptcb(struct ev_loop *loop, ev_async *w, int revents) {
1461 auto worker = static_cast<Worker *>(w->data);
1462 auto &sessions = worker->sessions;
1464 std::deque<ClientInfo> q;
1466 std::lock_guard<std::mutex> lock(worker->m);
1471 sessions->accept_connection(c.fd);
1477 void run_worker(Worker *worker) {
1478 auto loop = worker->sessions->get_loop();
1484 class AcceptHandler {
1486 AcceptHandler(HttpServer *sv, Sessions *sessions, const Config *config)
1487 : sessions_(sessions), config_(config), next_worker_(0) {
1488 if (config_->num_worker == 1) {
1491 for (size_t i = 0; i < config_->num_worker; ++i) {
1492 if (config_->verbose) {
1493 std::cerr << "spawning thread #" << i << std::endl;
1495 auto worker = make_unique<Worker>();
1496 auto loop = ev_loop_new(0);
1498 make_unique<Sessions>(sv, loop, config_, sessions_->get_ssl_ctx());
1499 ev_async_init(&worker->w, worker_acceptcb);
1500 worker->w.data = worker.get();
1501 ev_async_start(loop, &worker->w);
1503 auto t = std::thread(run_worker, worker.get());
1505 workers_.push_back(std::move(worker));
1508 void accept_connection(int fd) {
1509 if (config_->num_worker == 1) {
1510 sessions_->accept_connection(fd);
1514 // Dispatch client to the one of the worker threads, in a round
1516 auto &worker = workers_[next_worker_];
1517 if (next_worker_ == config_->num_worker - 1) {
1523 std::lock_guard<std::mutex> lock(worker->m);
1524 worker->q.push_back({fd});
1526 ev_async_send(worker->sessions->get_loop(), &worker->w);
1530 std::vector<std::unique_ptr<Worker>> workers_;
1531 Sessions *sessions_;
1532 const Config *config_;
1533 // In multi threading mode, this points to the next thread that
1534 // client will be dispatched.
1535 size_t next_worker_;
1539 void acceptcb(struct ev_loop *loop, ev_io *w, int revents);
1542 class ListenEventHandler {
1544 ListenEventHandler(Sessions *sessions, int fd,
1545 std::shared_ptr<AcceptHandler> acceptor)
1546 : acceptor_(acceptor), sessions_(sessions), fd_(fd) {
1547 ev_io_init(&w_, acceptcb, fd, EV_READ);
1549 ev_io_start(sessions_->get_loop(), &w_);
1551 void accept_connection() {
1554 auto fd = accept4(fd_, nullptr, nullptr, SOCK_NONBLOCK);
1555 #else // !HAVE_ACCEPT4
1556 auto fd = accept(fd_, nullptr, nullptr);
1557 #endif // !HAVE_ACCEPT4
1561 #ifndef HAVE_ACCEPT4
1562 util::make_socket_nonblocking(fd);
1563 #endif // !HAVE_ACCEPT4
1564 acceptor_->accept_connection(fd);
1570 std::shared_ptr<AcceptHandler> acceptor_;
1571 Sessions *sessions_;
1576 void acceptcb(struct ev_loop *loop, ev_io *w, int revents) {
1577 auto handler = static_cast<ListenEventHandler *>(w->data);
1578 handler->accept_connection();
1583 FileEntry make_status_body(int status, uint16_t port) {
1585 body = "<html><head><title>";
1586 body += http2::get_status_string(status);
1587 body += "</title></head><body><h1>";
1588 body += http2::get_status_string(status);
1589 body += "</h1><hr><address>";
1590 body += NGHTTPD_SERVER;
1591 body += " at port ";
1592 body += util::utos(port);
1593 body += "</address>";
1594 body += "</body></html>";
1596 char tempfn[] = "/tmp/nghttpd.temp.XXXXXX";
1597 int fd = mkstemp(tempfn);
1600 std::cerr << "Could not open status response body file: errno=" << error;
1605 while ((nwrite = write(fd, body.c_str(), body.size())) == -1 &&
1610 std::cerr << "Could not write status response body into file: errno="
1615 return FileEntry(util::utos(status), nwrite, 0, fd);
1619 // index into HttpServer::status_pages_
1628 HttpServer::HttpServer(const Config *config) : config_(config) {
1629 status_pages_ = std::vector<StatusPage>{
1630 {"200", make_status_body(200, config_->port)},
1631 {"301", make_status_body(301, config_->port)},
1632 {"304", make_status_body(304, config_->port)},
1633 {"400", make_status_body(400, config_->port)},
1634 {"404", make_status_body(404, config_->port)},
1639 int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len,
1641 auto next_proto = static_cast<std::vector<unsigned char> *>(arg);
1642 *data = next_proto->data();
1643 *len = next_proto->size();
1644 return SSL_TLSEXT_ERR_OK;
1649 int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
1650 // We don't verify the client certificate. Just request it for the
1657 int start_listen(HttpServer *sv, struct ev_loop *loop, Sessions *sessions,
1658 const Config *config) {
1662 const char *addr = nullptr;
1664 auto acceptor = std::make_shared<AcceptHandler>(sv, sessions, config);
1665 auto service = util::utos(config->port);
1667 memset(&hints, 0, sizeof(addrinfo));
1668 hints.ai_family = AF_UNSPEC;
1669 hints.ai_socktype = SOCK_STREAM;
1670 hints.ai_flags = AI_PASSIVE;
1671 #ifdef AI_ADDRCONFIG
1672 hints.ai_flags |= AI_ADDRCONFIG;
1673 #endif // AI_ADDRCONFIG
1675 if (!config->address.empty()) {
1676 addr = config->address.c_str();
1680 r = getaddrinfo(addr, service.c_str(), &hints, &res);
1682 std::cerr << "getaddrinfo() failed: " << gai_strerror(r) << std::endl;
1686 for (rp = res; rp; rp = rp->ai_next) {
1687 int fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
1692 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val,
1693 static_cast<socklen_t>(sizeof(val))) == -1) {
1697 (void)util::make_socket_nonblocking(fd);
1699 if (rp->ai_family == AF_INET6) {
1700 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &val,
1701 static_cast<socklen_t>(sizeof(val))) == -1) {
1706 #endif // IPV6_V6ONLY
1707 if (bind(fd, rp->ai_addr, rp->ai_addrlen) == 0 && listen(fd, 1000) == 0) {
1708 new ListenEventHandler(sessions, fd, acceptor);
1710 if (config->verbose) {
1711 std::string s = util::numeric_name(rp->ai_addr, rp->ai_addrlen);
1712 std::cout << (rp->ai_family == AF_INET ? "IPv4" : "IPv6") << ": listen "
1713 << s << ":" << config->port << std::endl;
1718 std::cerr << strerror(errno) << std::endl;
1731 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1733 int alpn_select_proto_cb(SSL *ssl, const unsigned char **out,
1734 unsigned char *outlen, const unsigned char *in,
1735 unsigned int inlen, void *arg) {
1736 auto config = static_cast<HttpServer *>(arg)->get_config();
1737 if (config->verbose) {
1738 std::cout << "[ALPN] client offers:" << std::endl;
1740 if (config->verbose) {
1741 for (unsigned int i = 0; i < inlen; i += in [i] + 1) {
1743 std::cout.write(reinterpret_cast<const char *>(&in[i + 1]), in[i]);
1744 std::cout << std::endl;
1747 if (!util::select_h2(out, outlen, in, inlen)) {
1748 return SSL_TLSEXT_ERR_NOACK;
1750 return SSL_TLSEXT_ERR_OK;
1753 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
1755 int HttpServer::run() {
1756 SSL_CTX *ssl_ctx = nullptr;
1757 std::vector<unsigned char> next_proto;
1759 if (!config_->no_tls) {
1760 ssl_ctx = SSL_CTX_new(SSLv23_server_method());
1762 std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
1766 SSL_CTX_set_options(ssl_ctx,
1767 SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
1768 SSL_OP_NO_COMPRESSION |
1769 SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION |
1770 SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_TICKET |
1771 SSL_OP_CIPHER_SERVER_PREFERENCE);
1772 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY);
1773 SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
1775 if (SSL_CTX_set_cipher_list(ssl_ctx, ssl::DEFAULT_CIPHER_LIST) == 0) {
1776 std::cerr << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
1780 const unsigned char sid_ctx[] = "nghttpd";
1781 SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1);
1782 SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER);
1784 #ifndef OPENSSL_NO_EC
1786 // Disabled SSL_CTX_set_ecdh_auto, because computational cost of
1787 // chosen curve is much higher than P-256.
1789 // #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1790 // SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
1791 // #else // OPENSSL_VERSION_NUBMER < 0x10002000L
1792 // Use P-256, which is sufficiently secure at the time of this
1794 auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
1795 if (ecdh == nullptr) {
1796 std::cerr << "EC_KEY_new_by_curv_name failed: "
1797 << ERR_error_string(ERR_get_error(), nullptr);
1800 SSL_CTX_set_tmp_ecdh(ssl_ctx, ecdh);
1802 // #endif // OPENSSL_VERSION_NUBMER < 0x10002000L
1804 #endif // OPENSSL_NO_EC
1806 if (!config_->dh_param_file.empty()) {
1807 // Read DH parameters from file
1808 auto bio = BIO_new_file(config_->dh_param_file.c_str(), "r");
1809 if (bio == nullptr) {
1810 std::cerr << "BIO_new_file() failed: "
1811 << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
1815 auto dh = PEM_read_bio_DHparams(bio, nullptr, nullptr, nullptr);
1817 if (dh == nullptr) {
1818 std::cerr << "PEM_read_bio_DHparams() failed: "
1819 << ERR_error_string(ERR_get_error(), nullptr) << std::endl;
1823 SSL_CTX_set_tmp_dh(ssl_ctx, dh);
1828 if (SSL_CTX_use_PrivateKey_file(ssl_ctx, config_->private_key_file.c_str(),
1829 SSL_FILETYPE_PEM) != 1) {
1830 std::cerr << "SSL_CTX_use_PrivateKey_file failed." << std::endl;
1833 if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
1834 config_->cert_file.c_str()) != 1) {
1835 std::cerr << "SSL_CTX_use_certificate_file failed." << std::endl;
1838 if (SSL_CTX_check_private_key(ssl_ctx) != 1) {
1839 std::cerr << "SSL_CTX_check_private_key failed." << std::endl;
1842 if (config_->verify_client) {
1843 SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE |
1844 SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
1848 next_proto = util::get_default_alpn();
1850 SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);
1851 #if OPENSSL_VERSION_NUMBER >= 0x10002000L
1852 // ALPN selection callback
1853 SSL_CTX_set_alpn_select_cb(ssl_ctx, alpn_select_proto_cb, this);
1854 #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
1857 auto loop = EV_DEFAULT;
1859 Sessions sessions(this, loop, config_, ssl_ctx);
1860 if (start_listen(this, loop, &sessions, config_) != 0) {
1861 std::cerr << "Could not listen" << std::endl;
1869 const Config *HttpServer::get_config() const { return config_; }
1871 const StatusPage *HttpServer::get_status_page(int status) const {
1874 return &status_pages_[IDX_200];
1876 return &status_pages_[IDX_301];
1878 return &status_pages_[IDX_304];
1880 return &status_pages_[IDX_400];
1882 return &status_pages_[IDX_404];
1889 } // namespace nghttp2