From f3e1dc7a4fd7d0760dd39e35aa650186331cf9bb Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 18 Jan 2016 14:21:09 +0900 Subject: [PATCH 1/7] nghttpx: Structured TLS related configurations --- src/shrpx.cc | 125 +++++++++++++----------- src/shrpx_client_handler.cc | 7 +- src/shrpx_config.cc | 64 ++++++------ src/shrpx_config.h | 124 ++++++++++++++--------- src/shrpx_connection_handler.cc | 10 +- src/shrpx_http2_session.cc | 10 +- src/shrpx_http_downstream_connection.cc | 4 +- src/shrpx_ssl.cc | 100 ++++++++++--------- src/shrpx_worker.cc | 6 +- src/shrpx_worker_process.cc | 28 +++--- 10 files changed, 266 insertions(+), 212 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index 2bf15a5b..806dc240 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -903,9 +903,6 @@ void fill_default_config() { mod_config()->server_name = "nghttpx nghttp2/" NGHTTP2_VERSION; mod_config()->host = strcopy("*"); mod_config()->port = 3000; - mod_config()->private_key_file = nullptr; - mod_config()->private_key_passwd = nullptr; - mod_config()->cert_file = nullptr; // Read timeout for HTTP2 upstream connection mod_config()->http2_upstream_read_timeout = 3_min; @@ -957,14 +954,11 @@ void fill_default_config() { mod_config()->syslog_facility = LOG_DAEMON; // Default accept() backlog mod_config()->backlog = 512; - mod_config()->ciphers = nullptr; mod_config()->http2_proxy = false; mod_config()->http2_bridge = false; mod_config()->client_proxy = false; mod_config()->client = false; mod_config()->client_mode = false; - mod_config()->insecure = false; - mod_config()->cacert = nullptr; mod_config()->pid_file = nullptr; mod_config()->user = nullptr; mod_config()->uid = 0; @@ -981,10 +975,6 @@ void fill_default_config() { mod_config()->worker_read_burst = 0; mod_config()->worker_write_rate = 0; mod_config()->worker_write_burst = 0; - mod_config()->verify_client = false; - mod_config()->verify_client_cacert = nullptr; - mod_config()->client_private_key_file = nullptr; - mod_config()->client_cert_file = nullptr; mod_config()->http2_upstream_dump_request_header = nullptr; mod_config()->http2_upstream_dump_response_header = nullptr; mod_config()->http2_no_cookie_crumbling = false; @@ -1002,7 +992,6 @@ void fill_default_config() { nghttp2_option_set_peer_max_concurrent_streams( get_config()->http2_client_option, 100); - mod_config()->tls_proto_mask = 0; mod_config()->no_location_rewrite = false; mod_config()->no_host_rewrite = true; mod_config()->argc = 0; @@ -1015,23 +1004,42 @@ void fill_default_config() { mod_config()->no_server_push = false; mod_config()->host_unix = false; mod_config()->http2_downstream_connections_per_worker = 0; - // ocsp update interval = 14400 secs = 4 hours, borrowed from h2o - mod_config()->ocsp_update_interval = 4_h; - mod_config()->fetch_ocsp_response_file = - strcopy(PKGDATADIR "/fetch-ocsp-response"); - mod_config()->no_ocsp = false; mod_config()->header_field_buffer = 64_k; mod_config()->max_header_fields = 100; mod_config()->downstream_addr_group_catch_all = 0; - mod_config()->tls_ticket_key_cipher = EVP_aes_128_cbc(); - mod_config()->tls_ticket_key_cipher_given = false; - mod_config()->tls_session_timeout = std::chrono::hours(12); - mod_config()->tls_ticket_key_memcached_max_retry = 3; - mod_config()->tls_ticket_key_memcached_max_fail = 2; - mod_config()->tls_ticket_key_memcached_interval = 10_min; mod_config()->fastopen = 0; - mod_config()->tls_dyn_rec_warmup_threshold = 1_m; - mod_config()->tls_dyn_rec_idle_timeout = 1.; + + auto &tlsconf = mod_config()->tls; + { + auto &ticketconf = tlsconf.ticket; + ticketconf.cipher = EVP_aes_128_cbc(); + ticketconf.cipher_given = false; + + { + auto &memcachedconf = ticketconf.memcached; + memcachedconf.max_retry = 3; + memcachedconf.max_fail = 2; + memcachedconf.interval = 10_min; + } + + auto &ocspconf = tlsconf.ocsp; + // ocsp update interval = 14400 secs = 4 hours, borrowed from h2o + ocspconf.update_interval = 4_h; + ocspconf.fetch_ocsp_response_file = + strcopy(PKGDATADIR "/fetch-ocsp-response"); + ocspconf.disabled = false; + + auto &client_verify = tlsconf.client_verify; + client_verify.enabled = false; + + auto &dyn_recconf = tlsconf.dyn_rec; + dyn_recconf.warmup_threshold = 1_m; + dyn_recconf.idle_timeout = 1.; + + tlsconf.session_timeout = std::chrono::hours(12); + tlsconf.tls_proto_mask = 0; + tlsconf.insecure = false; + } } } // namespace @@ -1386,8 +1394,7 @@ SSL/TLS: --tls-ticket-key-memcached-interval= Set interval to get TLS ticket keys from memcached. Default: )" - << util::duration_str(get_config()->tls_ticket_key_memcached_interval) - << R"( + << util::duration_str(get_config()->tls.ticket.memcached.interval) << R"( --tls-ticket-key-memcached-max-retry= Set maximum number of consecutive retries before abandoning TLS ticket key retrieval. If this number is @@ -1395,13 +1402,11 @@ SSL/TLS: "failure" count is incremented by 1, which contributed to the value controlled --tls-ticket-key-memcached-max-fail option. - Default: )" << get_config()->tls_ticket_key_memcached_max_retry - << R"( + Default: )" << get_config()->tls.ticket.memcached.max_retry << R"( --tls-ticket-key-memcached-max-fail= Set maximum number of consecutive failure before disabling TLS ticket until next scheduled key retrieval. - Default: )" << get_config()->tls_ticket_key_memcached_max_fail - << R"( + Default: )" << get_config()->tls.ticket.memcached.max_fail << R"( --tls-ticket-key-cipher= Specify cipher to encrypt TLS session ticket. Specify either aes-128-cbc or aes-256-cbc. By default, @@ -1409,11 +1414,12 @@ SSL/TLS: --fetch-ocsp-response-file= Path to fetch-ocsp-response script file. It should be absolute path. - Default: )" << get_config()->fetch_ocsp_response_file.get() << R"( + Default: )" + << get_config()->tls.ocsp.fetch_ocsp_response_file.get() << R"( --ocsp-update-interval= Set interval to update OCSP response cache. Default: )" - << util::duration_str(get_config()->ocsp_update_interval) << R"( + << util::duration_str(get_config()->tls.ocsp.update_interval) << R"( --no-ocsp Disable OCSP stapling. --tls-session-cache-memcached=, Specify address of memcached server to store session @@ -1431,14 +1437,14 @@ SSL/TLS: period. This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends. Default: )" - << util::utos_unit(get_config()->tls_dyn_rec_warmup_threshold) << R"( + << util::utos_unit(get_config()->tls.dyn_rec.warmup_threshold) << R"( --tls-dyn-rec-idle-timeout= Specify TLS dynamic record size behaviour timeout. See --tls-dyn-rec-warmup-threshold for more information. This behaviour applies to all TLS based frontends, and TLS HTTP/2 backends. Default: )" - << util::duration_str(get_config()->tls_dyn_rec_idle_timeout) << R"( + << util::duration_str(get_config()->tls.dyn_rec.idle_timeout) << R"( HTTP/2 and SPDY: -c, --http2-max-concurrent-streams= @@ -2410,18 +2416,19 @@ int main(int argc, char **argv) { } } - if (get_config()->npn_list.empty()) { - mod_config()->npn_list = util::parse_config_str_list(DEFAULT_NPN_LIST); + auto &tlsconf = mod_config()->tls; + + if (tlsconf.npn_list.empty()) { + tlsconf.npn_list = util::parse_config_str_list(DEFAULT_NPN_LIST); } - if (get_config()->tls_proto_list.empty()) { - mod_config()->tls_proto_list = + if (tlsconf.tls_proto_list.empty()) { + tlsconf.tls_proto_list = util::parse_config_str_list(DEFAULT_TLS_PROTO_LIST); } - mod_config()->tls_proto_mask = - ssl::create_tls_proto_mask(get_config()->tls_proto_list); + tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list); - mod_config()->alpn_prefs = ssl::set_alpn_prefs(get_config()->npn_list); + tlsconf.alpn_prefs = ssl::set_alpn_prefs(tlsconf.npn_list); if (get_config()->backend_ipv4 && get_config()->backend_ipv6) { LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the " @@ -2454,18 +2461,18 @@ int main(int argc, char **argv) { } if (!get_config()->upstream_no_tls && - (!get_config()->private_key_file || !get_config()->cert_file)) { + (!tlsconf.private_key_file || !tlsconf.cert_file)) { print_usage(std::cerr); LOG(FATAL) << "Too few arguments"; exit(EXIT_FAILURE); } - if (!get_config()->upstream_no_tls && !get_config()->no_ocsp) { + if (!get_config()->upstream_no_tls && !tlsconf.ocsp.disabled) { struct stat buf; - if (stat(get_config()->fetch_ocsp_response_file.get(), &buf) != 0) { - mod_config()->no_ocsp = true; + if (stat(tlsconf.ocsp.fetch_ocsp_response_file.get(), &buf) != 0) { + tlsconf.ocsp.disabled = true; LOG(WARN) << "--fetch-ocsp-response-file: " - << get_config()->fetch_ocsp_response_file.get() + << tlsconf.ocsp.fetch_ocsp_response_file.get() << " not found. OCSP stapling has been disabled."; } } @@ -2581,21 +2588,23 @@ int main(int argc, char **argv) { } } - if (get_config()->session_cache_memcached_host) { - if (resolve_hostname(&mod_config()->session_cache_memcached_addr, - get_config()->session_cache_memcached_host.get(), - get_config()->session_cache_memcached_port, - AF_UNSPEC) == -1) { - exit(EXIT_FAILURE); + { + auto &memcachedconf = tlsconf.session_cache.memcached; + if (memcachedconf.host) { + if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.get(), + memcachedconf.port, AF_UNSPEC) == -1) { + exit(EXIT_FAILURE); + } } } - if (get_config()->tls_ticket_key_memcached_host) { - if (resolve_hostname(&mod_config()->tls_ticket_key_memcached_addr, - get_config()->tls_ticket_key_memcached_host.get(), - get_config()->tls_ticket_key_memcached_port, - AF_UNSPEC) == -1) { - exit(EXIT_FAILURE); + { + auto &memcachedconf = tlsconf.ticket.memcached; + if (memcachedconf.host) { + if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.get(), + memcachedconf.port, AF_UNSPEC) == -1) { + exit(EXIT_FAILURE); + } } } diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index 6d05af72..4993cb71 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -375,8 +375,8 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl, get_config()->upstream_read_timeout, get_config()->write_rate, get_config()->write_burst, get_config()->read_rate, get_config()->read_burst, writecb, readcb, timeoutcb, this, - get_config()->tls_dyn_rec_warmup_threshold, - get_config()->tls_dyn_rec_idle_timeout), + get_config()->tls.dyn_rec.warmup_threshold, + get_config()->tls.dyn_rec.idle_timeout), pinned_http2sessions_( get_config()->downstream_proto == PROTO_HTTP2 ? make_unique>( @@ -521,7 +521,8 @@ int ClientHandler::validate_next_proto() { CLOG(INFO, this) << "The negotiated next protocol: " << proto; } - if (!ssl::in_proto_list(get_config()->npn_list, next_proto, next_proto_len)) { + if (!ssl::in_proto_list(get_config()->tls.npn_list, next_proto, + next_proto_len)) { if (LOG_ENABLED(INFO)) { CLOG(INFO, this) << "The negotiated protocol is not supported"; } diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 332f30d8..a3d6c6fa 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -1601,7 +1601,7 @@ int parse_config(const char *opt, const char *optarg, return 0; case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD: - mod_config()->backend_tls_sni_name = optarg; + mod_config()->tls.backend_sni_name = optarg; return 0; case SHRPX_OPTID_PID_FILE: @@ -1622,7 +1622,7 @@ int parse_config(const char *opt, const char *optarg, return 0; } case SHRPX_OPTID_PRIVATE_KEY_FILE: - mod_config()->private_key_file = strcopy(optarg); + mod_config()->tls.private_key_file = strcopy(optarg); return 0; case SHRPX_OPTID_PRIVATE_KEY_PASSWD_FILE: { @@ -1631,16 +1631,16 @@ int parse_config(const char *opt, const char *optarg, LOG(ERROR) << opt << ": Couldn't read key file's passwd from " << optarg; return -1; } - mod_config()->private_key_passwd = strcopy(passwd); + mod_config()->tls.private_key_passwd = strcopy(passwd); return 0; } case SHRPX_OPTID_CERTIFICATE_FILE: - mod_config()->cert_file = strcopy(optarg); + mod_config()->tls.cert_file = strcopy(optarg); return 0; case SHRPX_OPTID_DH_PARAM_FILE: - mod_config()->dh_param_file = strcopy(optarg); + mod_config()->tls.dh_param_file = strcopy(optarg); return 0; case SHRPX_OPTID_SUBCERT: { @@ -1649,7 +1649,7 @@ int parse_config(const char *opt, const char *optarg, if (sp) { std::string keyfile(optarg, sp); // TODO Do we need private key for subcert? - mod_config()->subcerts.emplace_back(keyfile, sp + 1); + mod_config()->tls.subcerts.emplace_back(keyfile, sp + 1); } return 0; @@ -1681,7 +1681,7 @@ int parse_config(const char *opt, const char *optarg, return 0; } case SHRPX_OPTID_CIPHERS: - mod_config()->ciphers = strcopy(optarg); + mod_config()->tls.ciphers = strcopy(optarg); return 0; case SHRPX_OPTID_CLIENT: @@ -1689,11 +1689,11 @@ int parse_config(const char *opt, const char *optarg, return 0; case SHRPX_OPTID_INSECURE: - mod_config()->insecure = util::strieq(optarg, "yes"); + mod_config()->tls.insecure = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_CACERT: - mod_config()->cacert = strcopy(optarg); + mod_config()->tls.cacert = strcopy(optarg); return 0; case SHRPX_OPTID_BACKEND_IPV4: @@ -1762,27 +1762,27 @@ int parse_config(const char *opt, const char *optarg, LOG(WARN) << opt << ": not implemented yet"; return parse_uint_with_unit(&mod_config()->worker_write_burst, opt, optarg); case SHRPX_OPTID_NPN_LIST: - mod_config()->npn_list = util::parse_config_str_list(optarg); + mod_config()->tls.npn_list = util::parse_config_str_list(optarg); return 0; case SHRPX_OPTID_TLS_PROTO_LIST: - mod_config()->tls_proto_list = util::parse_config_str_list(optarg); + mod_config()->tls.tls_proto_list = util::parse_config_str_list(optarg); return 0; case SHRPX_OPTID_VERIFY_CLIENT: - mod_config()->verify_client = util::strieq(optarg, "yes"); + mod_config()->tls.client_verify.enabled = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_VERIFY_CLIENT_CACERT: - mod_config()->verify_client_cacert = strcopy(optarg); + mod_config()->tls.client_verify.cacert = strcopy(optarg); return 0; case SHRPX_OPTID_CLIENT_PRIVATE_KEY_FILE: - mod_config()->client_private_key_file = strcopy(optarg); + mod_config()->tls.client.private_key_file = strcopy(optarg); return 0; case SHRPX_OPTID_CLIENT_CERT_FILE: - mod_config()->client_cert_file = strcopy(optarg); + mod_config()->tls.client.cert_file = strcopy(optarg); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER: @@ -1899,7 +1899,7 @@ int parse_config(const char *opt, const char *optarg, case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT: return parse_duration(&mod_config()->listener_disable_timeout, opt, optarg); case SHRPX_OPTID_TLS_TICKET_KEY_FILE: - mod_config()->tls_ticket_key_files.push_back(optarg); + mod_config()->tls.ticket.files.push_back(optarg); return 0; case SHRPX_OPTID_RLIMIT_NOFILE: { int n; @@ -1948,13 +1948,13 @@ int parse_config(const char *opt, const char *optarg, return parse_uint(&mod_config()->http2_downstream_connections_per_worker, opt, optarg); case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE: - mod_config()->fetch_ocsp_response_file = strcopy(optarg); + mod_config()->tls.ocsp.fetch_ocsp_response_file = strcopy(optarg); return 0; case SHRPX_OPTID_OCSP_UPDATE_INTERVAL: - return parse_duration(&mod_config()->ocsp_update_interval, opt, optarg); + return parse_duration(&mod_config()->tls.ocsp.update_interval, opt, optarg); case SHRPX_OPTID_NO_OCSP: - mod_config()->no_ocsp = util::strieq(optarg, "yes"); + mod_config()->tls.ocsp.disabled = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_HEADER_FIELD_BUFFER: @@ -1980,15 +1980,15 @@ int parse_config(const char *opt, const char *optarg, } case SHRPX_OPTID_TLS_TICKET_KEY_CIPHER: if (util::strieq(optarg, "aes-128-cbc")) { - mod_config()->tls_ticket_key_cipher = EVP_aes_128_cbc(); + mod_config()->tls.ticket.cipher = EVP_aes_128_cbc(); } else if (util::strieq(optarg, "aes-256-cbc")) { - mod_config()->tls_ticket_key_cipher = EVP_aes_256_cbc(); + mod_config()->tls.ticket.cipher = EVP_aes_256_cbc(); } else { LOG(ERROR) << opt << ": unsupported cipher for ticket encryption: " << optarg; return -1; } - mod_config()->tls_ticket_key_cipher_given = true; + mod_config()->tls.ticket.cipher_given = true; return 0; case SHRPX_OPTID_HOST_REWRITE: @@ -2001,8 +2001,9 @@ int parse_config(const char *opt, const char *optarg, return -1; } - mod_config()->session_cache_memcached_host = strcopy(host); - mod_config()->session_cache_memcached_port = port; + auto &memcachedconf = mod_config()->tls.session_cache.memcached; + memcachedconf.host = strcopy(host); + memcachedconf.port = port; return 0; } @@ -2012,13 +2013,14 @@ int parse_config(const char *opt, const char *optarg, return -1; } - mod_config()->tls_ticket_key_memcached_host = strcopy(host); - mod_config()->tls_ticket_key_memcached_port = port; + auto &memcachedconf = mod_config()->tls.ticket.memcached; + memcachedconf.host = strcopy(host); + memcachedconf.port = port; return 0; } case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_INTERVAL: - return parse_duration(&mod_config()->tls_ticket_key_memcached_interval, opt, + return parse_duration(&mod_config()->tls.ticket.memcached.interval, opt, optarg); case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_RETRY: { int n; @@ -2031,11 +2033,11 @@ int parse_config(const char *opt, const char *optarg, return -1; } - mod_config()->tls_ticket_key_memcached_max_retry = n; + mod_config()->tls.ticket.memcached.max_retry = n; return 0; } case SHRPX_OPTID_TLS_TICKET_KEY_MEMCACHED_MAX_FAIL: - return parse_uint(&mod_config()->tls_ticket_key_memcached_max_fail, opt, + return parse_uint(&mod_config()->tls.ticket.memcached.max_fail, opt, optarg); case SHRPX_OPTID_TLS_DYN_REC_WARMUP_THRESHOLD: { size_t n; @@ -2043,13 +2045,13 @@ int parse_config(const char *opt, const char *optarg, return -1; } - mod_config()->tls_dyn_rec_warmup_threshold = n; + mod_config()->tls.dyn_rec.warmup_threshold = n; return 0; } case SHRPX_OPTID_TLS_DYN_REC_IDLE_TIMEOUT: - return parse_duration(&mod_config()->tls_dyn_rec_idle_timeout, opt, optarg); + return parse_duration(&mod_config()->tls.dyn_rec.idle_timeout, opt, optarg); case SHRPX_OPTID_MRUBY_FILE: #ifdef HAVE_MRUBY diff --git a/src/shrpx_config.h b/src/shrpx_config.h index e75661dd..e0636bdd 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -292,25 +292,94 @@ struct HttpProxy { uint16_t port; }; -struct Config { +struct TLSConfig { + // RFC 5077 Session ticket related configurations + struct { + struct { + Address addr; + uint16_t port; + std::unique_ptr host; + ev_tstamp interval; + // Maximum number of retries when getting TLS ticket key from + // mamcached, due to network error. + size_t max_retry; + // Maximum number of consecutive error from memcached, when this + // limit reached, TLS ticket is disabled. + size_t max_fail; + } memcached; + std::vector files; + const EVP_CIPHER *cipher; + // true if --tls-ticket-key-cipher is used + bool cipher_given; + } ticket; + + // Session cache related configurations + struct { + struct { + Address addr; + uint16_t port; + std::unique_ptr host; + } memcached; + } session_cache; + + // Dynamic record sizing configurations + struct { + size_t warmup_threshold; + ev_tstamp idle_timeout; + } dyn_rec; + + // OCSP realted configurations + struct { + ev_tstamp update_interval; + std::unique_ptr fetch_ocsp_response_file; + bool disabled; + } ocsp; + + // Client verification configurations + struct { + // Path to file containing CA certificate solely used for client + // certificate validation + std::unique_ptr cacert; + bool enabled; + } client_verify; + + // Client private key and certificate used in backend connections. + struct { + std::unique_ptr private_key_file; + std::unique_ptr cert_file; + } client; + // The list of (private key file, certificate file) pair std::vector> subcerts; - std::vector altsvcs; - std::vector> add_request_headers; - std::vector> add_response_headers; std::vector alpn_prefs; - std::vector accesslog_format; - std::vector downstream_addr_groups; - std::vector tls_ticket_key_files; // list of supported NPN/ALPN protocol strings in the order of // preference. std::vector npn_list; // list of supported SSL/TLS protocol strings. std::vector tls_proto_list; - Address session_cache_memcached_addr; - Address tls_ticket_key_memcached_addr; + // Bit mask to disable SSL/TLS protocol versions. This will be + // passed to SSL_CTX_set_options(). + long int tls_proto_mask; + std::string backend_sni_name; + std::chrono::seconds session_timeout; + std::unique_ptr private_key_file; + std::unique_ptr private_key_passwd; + std::unique_ptr cert_file; + std::unique_ptr dh_param_file; + std::unique_ptr ciphers; + std::unique_ptr cacert; + bool insecure; +}; + +struct Config { + std::vector altsvcs; + std::vector> add_request_headers; + std::vector> add_response_headers; + std::vector accesslog_format; + std::vector downstream_addr_groups; Router router; HttpProxy downstream_http_proxy; + TLSConfig tls; // obfuscated value used in "by" parameter of Forwarded header // field. std::string forwarded_by_obfuscated; @@ -318,9 +387,7 @@ struct Config { // field. This is only used when user defined static obfuscated // string is provided. std::string forwarded_for_obfuscated; - std::string backend_tls_sni_name; StringRef server_name; - std::chrono::seconds tls_session_timeout; ev_tstamp http2_upstream_read_timeout; ev_tstamp upstream_read_timeout; ev_tstamp upstream_write_timeout; @@ -330,32 +397,16 @@ struct Config { ev_tstamp stream_write_timeout; ev_tstamp downstream_idle_read_timeout; ev_tstamp listener_disable_timeout; - ev_tstamp ocsp_update_interval; - ev_tstamp tls_ticket_key_memcached_interval; // address of frontend connection. This could be a path to UNIX // domain socket. In this case, |host_unix| must be true. std::unique_ptr host; - std::unique_ptr private_key_file; - std::unique_ptr private_key_passwd; - std::unique_ptr cert_file; - std::unique_ptr dh_param_file; std::unique_ptr pid_file; std::unique_ptr conf_path; - std::unique_ptr ciphers; - std::unique_ptr cacert; std::unique_ptr http2_upstream_dump_request_header_file; std::unique_ptr http2_upstream_dump_response_header_file; - // Path to file containing CA certificate solely used for client - // certificate validation - std::unique_ptr verify_client_cacert; - std::unique_ptr client_private_key_file; - std::unique_ptr client_cert_file; std::unique_ptr accesslog_file; std::unique_ptr errorlog_file; - std::unique_ptr fetch_ocsp_response_file; std::unique_ptr user; - std::unique_ptr session_cache_memcached_host; - std::unique_ptr tls_ticket_key_memcached_host; std::unique_ptr mruby_file; FILE *http2_upstream_dump_request_header; FILE *http2_upstream_dump_response_header; @@ -363,7 +414,6 @@ struct Config { nghttp2_session_callbacks *http2_downstream_callbacks; nghttp2_option *http2_option; nghttp2_option *http2_client_option; - const EVP_CIPHER *tls_ticket_key_cipher; char **original_argv; char **argv; char *cwd; @@ -393,15 +443,6 @@ struct Config { size_t max_header_fields; // The index of catch-all group in downstream_addr_groups. size_t downstream_addr_group_catch_all; - // Maximum number of retries when getting TLS ticket key from - // mamcached, due to network error. - size_t tls_ticket_key_memcached_max_retry; - // Maximum number of consecutive error from memcached, when this - // limit reached, TLS ticket is disabled. - size_t tls_ticket_key_memcached_max_fail; - // Bit mask to disable SSL/TLS protocol versions. This will be - // passed to SSL_CTX_set_options(). - long int tls_proto_mask; // downstream protocol; this will be determined by given options. shrpx_proto downstream_proto; // bitwise-OR of one or more of shrpx_forwarded_param values. @@ -424,11 +465,8 @@ struct Config { uint16_t port; // port in http proxy URI uint16_t downstream_http_proxy_port; - uint16_t session_cache_memcached_port; - uint16_t tls_ticket_key_memcached_port; bool verbose; bool daemon; - bool verify_client; bool http2_proxy; bool http2_bridge; bool client_proxy; @@ -445,7 +483,6 @@ struct Config { bool client; // true if --client or --client-proxy are enabled. bool client_mode; - bool insecure; bool backend_ipv4; bool backend_ipv6; bool http2_no_cookie_crumbling; @@ -455,12 +492,7 @@ struct Config { bool no_server_push; // true if host contains UNIX domain socket path bool host_unix; - bool no_ocsp; - // true if --tls-ticket-key-cipher is used - bool tls_ticket_key_cipher_given; bool accept_proxy_protocol; - size_t tls_dyn_rec_warmup_threshold; - ev_tstamp tls_dyn_rec_idle_timeout; }; const Config *get_config(); diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc index 87aab2f9..ee97437b 100644 --- a/src/shrpx_connection_handler.cc +++ b/src/shrpx_connection_handler.cc @@ -450,7 +450,7 @@ int ConnectionHandler::start_ocsp_update(const char *cert_file) { assert(!ev_is_active(&ocsp_.chldev)); char *const argv[] = { - const_cast(get_config()->fetch_ocsp_response_file.get()), + const_cast(get_config()->tls.ocsp.fetch_ocsp_response_file.get()), const_cast(cert_file), nullptr}; char *const envp[] = {nullptr}; @@ -634,7 +634,7 @@ void ConnectionHandler::proceed_next_cert_ocsp() { if (ocsp_.next == all_ssl_ctx_.size()) { ocsp_.next = 0; // We have updated all ocsp response, and schedule next update. - ev_timer_set(&ocsp_timer_, get_config()->ocsp_update_interval, 0.); + ev_timer_set(&ocsp_timer_, get_config()->tls.ocsp.update_interval, 0.); ev_timer_start(loop_, &ocsp_timer_); return; } @@ -673,7 +673,7 @@ ConnectionHandler::get_tls_ticket_key_memcached_dispatcher() const { void ConnectionHandler::on_tls_ticket_key_network_error(ev_timer *w) { if (++tls_ticket_key_memcached_get_retry_count_ >= - get_config()->tls_ticket_key_memcached_max_retry) { + get_config()->tls.ticket.memcached.max_retry) { LOG(WARN) << "Memcached: tls ticket get retry all failed " << tls_ticket_key_memcached_get_retry_count_ << " times."; @@ -697,7 +697,7 @@ void ConnectionHandler::on_tls_ticket_key_not_found(ev_timer *w) { tls_ticket_key_memcached_get_retry_count_ = 0; if (++tls_ticket_key_memcached_fail_count_ >= - get_config()->tls_ticket_key_memcached_max_fail) { + get_config()->tls.ticket.memcached.max_fail) { LOG(WARN) << "Memcached: could not get tls ticket; disable tls ticket"; tls_ticket_key_memcached_fail_count_ = 0; @@ -742,7 +742,7 @@ void ConnectionHandler::on_tls_ticket_key_get_success( void ConnectionHandler::schedule_next_tls_ticket_key_memcached_get( ev_timer *w) { - ev_timer_set(w, get_config()->tls_ticket_key_memcached_interval, 0.); + ev_timer_set(w, get_config()->tls.ticket.memcached.interval, 0.); ev_timer_start(loop_, w); } diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index dff02931..48efa69e 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -148,8 +148,8 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, : conn_(loop, -1, nullptr, worker->get_mcpool(), get_config()->downstream_write_timeout, get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb, - timeoutcb, this, get_config()->tls_dyn_rec_warmup_threshold, - get_config()->tls_dyn_rec_idle_timeout), + timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, + get_config()->tls.dyn_rec.idle_timeout), worker_(worker), connect_blocker_(connect_blocker), ssl_ctx_(ssl_ctx), session_(nullptr), data_pending_(nullptr), data_pendinglen_(0), addr_idx_(0), group_(group), index_(idx), state_(DISCONNECTED), @@ -331,8 +331,8 @@ int Http2Session::initiate_connection() { conn_.set_ssl(ssl); } - StringRef sni_name = !get_config()->backend_tls_sni_name.empty() - ? get_config()->backend_tls_sni_name + StringRef sni_name = !get_config()->tls.backend_sni_name.empty() + ? get_config()->tls.backend_sni_name : downstream_addr.host; if (!util::numeric_host(sni_name.c_str())) { @@ -1718,7 +1718,7 @@ int Http2Session::tls_handshake() { SSLOG(INFO, this) << "SSL/TLS handshake completed"; } - if (!get_config()->downstream_no_tls && !get_config()->insecure && + if (!get_config()->downstream_no_tls && !get_config()->tls.insecure && check_cert() != 0) { return -1; } diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index 4790a84d..542259f7 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -115,8 +115,8 @@ HttpDownstreamConnection::HttpDownstreamConnection( : DownstreamConnection(dconn_pool), conn_(loop, -1, nullptr, nullptr, get_config()->downstream_write_timeout, get_config()->downstream_read_timeout, 0, 0, 0, 0, connectcb, - readcb, timeoutcb, this, get_config()->tls_dyn_rec_warmup_threshold, - get_config()->tls_dyn_rec_idle_timeout), + readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, + get_config()->tls.dyn_rec.idle_timeout), ioctrl_(&conn_.rlimit), response_htp_{0}, group_(group), addr_idx_(0), connected_(false) {} diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 9d27c6eb..412071fd 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -70,7 +70,7 @@ namespace ssl { namespace { int next_proto_cb(SSL *s, const unsigned char **data, unsigned int *len, void *arg) { - auto &prefs = get_config()->alpn_prefs; + auto &prefs = get_config()->tls.alpn_prefs; *data = prefs.data(); *len = prefs.size(); return SSL_TLSEXT_ERR_OK; @@ -124,13 +124,13 @@ set_alpn_prefs(const std::vector &protos) { namespace { int ssl_pem_passwd_cb(char *buf, int size, int rwflag, void *user_data) { auto config = static_cast(user_data); - int len = (int)strlen(config->private_key_passwd.get()); + int len = (int)strlen(config->tls.private_key_passwd.get()); if (size < len + 1) { LOG(ERROR) << "ssl_pem_passwd_cb: buf is too small " << size; return 0; } // Copy string including last '\0'. - memcpy(buf, config->private_key_passwd.get(), len + 1); + memcpy(buf, config->tls.private_key_passwd.get(), len + 1); return len; } } // namespace @@ -346,7 +346,7 @@ int ticket_key_cb(SSL *ssl, unsigned char *key_name, unsigned char *iv, std::copy(std::begin(key.data.name), std::end(key.data.name), key_name); - EVP_EncryptInit_ex(ctx, get_config()->tls_ticket_key_cipher, nullptr, + EVP_EncryptInit_ex(ctx, get_config()->tls.ticket.cipher, nullptr, key.data.enc_key.data(), iv); HMAC_Init_ex(hctx, key.data.hmac_key.data(), key.hmac_keylen, key.hmac, nullptr); @@ -411,7 +411,7 @@ int alpn_select_proto_cb(SSL *ssl, const unsigned char **out, // We assume that get_config()->npn_list contains ALPN protocol // identifier sorted by preference order. So we just break when we // found the first overlap. - for (const auto &target_proto_id : get_config()->npn_list) { + for (const auto &target_proto_id : get_config()->tls.npn_list) { for (auto p = in, end = in + inlen; p < end;) { auto proto_id = p + 1; auto proto_len = *p; @@ -477,22 +477,24 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION | SSL_OP_SINGLE_ECDH_USE | SSL_OP_SINGLE_DH_USE | SSL_OP_CIPHER_SERVER_PREFERENCE; - SSL_CTX_set_options(ssl_ctx, ssl_opts | get_config()->tls_proto_mask); + auto &tlsconf = get_config()->tls; + + SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask); const unsigned char sid_ctx[] = "shrpx"; SSL_CTX_set_session_id_context(ssl_ctx, sid_ctx, sizeof(sid_ctx) - 1); SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_SERVER); - if (get_config()->session_cache_memcached_host) { + if (tlsconf.session_cache.memcached.host) { SSL_CTX_sess_set_new_cb(ssl_ctx, tls_session_new_cb); SSL_CTX_sess_set_get_cb(ssl_ctx, tls_session_get_cb); } - SSL_CTX_set_timeout(ssl_ctx, get_config()->tls_session_timeout.count()); + SSL_CTX_set_timeout(ssl_ctx, tlsconf.session_timeout.count()); const char *ciphers; - if (get_config()->ciphers) { - ciphers = get_config()->ciphers.get(); + if (tlsconf.ciphers) { + ciphers = tlsconf.ciphers.get(); } else { ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST; } @@ -525,9 +527,9 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file #endif // OPENSSL_NO_EC - if (get_config()->dh_param_file) { + if (tlsconf.dh_param_file) { // Read DH parameters from file - auto bio = BIO_new_file(get_config()->dh_param_file.get(), "r"); + auto bio = BIO_new_file(tlsconf.dh_param_file.get(), "r"); if (bio == nullptr) { LOG(FATAL) << "BIO_new_file() failed: " << ERR_error_string(ERR_get_error(), nullptr); @@ -546,7 +548,7 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file SSL_CTX_set_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_RELEASE_BUFFERS); - if (get_config()->private_key_passwd) { + if (tlsconf.private_key_passwd) { SSL_CTX_set_default_passwd_cb(ssl_ctx, ssl_pem_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, (void *)get_config()); } @@ -576,14 +578,13 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file << ERR_error_string(ERR_get_error(), nullptr); DIE(); } - if (get_config()->verify_client) { - if (get_config()->verify_client_cacert) { + if (tlsconf.client_verify.enabled) { + if (tlsconf.client_verify.cacert) { if (SSL_CTX_load_verify_locations( - ssl_ctx, get_config()->verify_client_cacert.get(), nullptr) != - 1) { + ssl_ctx, tlsconf.client_verify.cacert.get(), nullptr) != 1) { LOG(FATAL) << "Could not load trusted ca certificates from " - << get_config()->verify_client_cacert.get() << ": " + << tlsconf.client_verify.cacert.get() << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } @@ -591,11 +592,10 @@ SSL_CTX *create_ssl_context(const char *private_key_file, const char *cert_file // error even though it returns success. See // http://forum.nginx.org/read.php?29,242540 ERR_clear_error(); - auto list = - SSL_load_client_CA_file(get_config()->verify_client_cacert.get()); + auto list = SSL_load_client_CA_file(tlsconf.client_verify.cacert.get()); if (!list) { LOG(FATAL) << "Could not load ca certificates from " - << get_config()->verify_client_cacert.get() << ": " + << tlsconf.client_verify.cacert.get() << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } @@ -656,11 +656,13 @@ SSL_CTX *create_ssl_client_context( SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; - SSL_CTX_set_options(ssl_ctx, ssl_opts | get_config()->tls_proto_mask); + auto &tlsconf = get_config()->tls; + + SSL_CTX_set_options(ssl_ctx, ssl_opts | tlsconf.tls_proto_mask); const char *ciphers; - if (get_config()->ciphers) { - ciphers = get_config()->ciphers.get(); + if (tlsconf.ciphers) { + ciphers = tlsconf.ciphers.get(); } else { ciphers = nghttp2::ssl::DEFAULT_CIPHER_LIST; } @@ -678,44 +680,44 @@ SSL_CTX *create_ssl_client_context( << ERR_error_string(ERR_get_error(), nullptr); } - if (get_config()->cacert) { - if (SSL_CTX_load_verify_locations(ssl_ctx, get_config()->cacert.get(), - nullptr) != 1) { + if (tlsconf.cacert) { + if (SSL_CTX_load_verify_locations(ssl_ctx, tlsconf.cacert.get(), nullptr) != + 1) { LOG(FATAL) << "Could not load trusted ca certificates from " - << get_config()->cacert.get() << ": " + << tlsconf.cacert.get() << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } } - if (get_config()->client_private_key_file) { + if (tlsconf.client.private_key_file) { #ifndef HAVE_NEVERBLEED if (SSL_CTX_use_PrivateKey_file(ssl_ctx, - get_config()->client_private_key_file.get(), + tlsconf.client.private_key_file.get(), SSL_FILETYPE_PEM) != 1) { LOG(FATAL) << "Could not load client private key from " - << get_config()->client_private_key_file.get() << ": " + << tlsconf.client.private_key_file.get() << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } #else // HAVE_NEVERBLEED std::array errbuf; - if (neverbleed_load_private_key_file( - nb, ssl_ctx, get_config()->client_private_key_file.get(), - errbuf.data()) != 1) { + if (neverbleed_load_private_key_file(nb, ssl_ctx, + tlsconf.client.private_key_file.get(), + errbuf.data()) != 1) { LOG(FATAL) << "neverbleed_load_private_key_file failed: " << errbuf.data(); DIE(); } #endif // HAVE_NEVERBLEED } - if (get_config()->client_cert_file) { + if (tlsconf.client.cert_file) { if (SSL_CTX_use_certificate_chain_file( - ssl_ctx, get_config()->client_cert_file.get()) != 1) { + ssl_ctx, tlsconf.client.cert_file.get()) != 1) { LOG(FATAL) << "Could not load client certificate from " - << get_config()->client_cert_file.get() << ": " + << tlsconf.client.cert_file.get() << ": " << ERR_error_string(ERR_get_error(), nullptr); DIE(); } @@ -971,9 +973,11 @@ int check_cert(SSL *ssl, const DownstreamAddr *addr) { << X509_verify_cert_error_string(verify_res); return -1; } - auto hostname = !get_config()->backend_tls_sni_name.empty() - ? StringRef(get_config()->backend_tls_sni_name) - : StringRef(addr->host); + + auto &backend_sni_name = get_config()->tls.backend_sni_name; + + auto hostname = !backend_sni_name.empty() ? StringRef(backend_sni_name) + : StringRef(addr->host); if (verify_hostname(cert, hostname.c_str(), hostname.size(), &addr->addr) != 0) { LOG(ERROR) << "Certificate verification failed: hostname does not match"; @@ -1214,8 +1218,10 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, return nullptr; } - auto ssl_ctx = ssl::create_ssl_context(get_config()->private_key_file.get(), - get_config()->cert_file.get() + auto &tlsconf = get_config()->tls; + + auto ssl_ctx = ssl::create_ssl_context(tlsconf.private_key_file.get(), + tlsconf.cert_file.get() #ifdef HAVE_NEVERBLEED , nb @@ -1224,7 +1230,7 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, all_ssl_ctx.push_back(ssl_ctx); - if (get_config()->subcerts.empty()) { + if (tlsconf.subcerts.empty()) { return ssl_ctx; } @@ -1234,7 +1240,7 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, return ssl_ctx; } - for (auto &keycert : get_config()->subcerts) { + for (auto &keycert : tlsconf.subcerts) { auto ssl_ctx = ssl::create_ssl_context(keycert.first.c_str(), keycert.second.c_str() #ifdef HAVE_NEVERBLEED @@ -1250,8 +1256,8 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, } } - if (ssl::cert_lookup_tree_add_cert_from_file( - cert_tree, ssl_ctx, get_config()->cert_file.get()) == -1) { + if (ssl::cert_lookup_tree_add_cert_from_file(cert_tree, ssl_ctx, + tlsconf.cert_file.get()) == -1) { LOG(FATAL) << "Failed to add default certificate."; DIE(); } @@ -1284,7 +1290,7 @@ SSL_CTX *setup_client_ssl_context( } CertLookupTree *create_cert_lookup_tree() { - if (get_config()->upstream_no_tls || get_config()->subcerts.empty()) { + if (get_config()->upstream_no_tls || get_config()->tls.subcerts.empty()) { return nullptr; } return new ssl::CertLookupTree(); diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index 3d329900..84e6d9c0 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -83,9 +83,11 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, ev_timer_init(&mcpool_clear_timer_, mcpool_clear_cb, 0., 0.); mcpool_clear_timer_.data = this; - if (get_config()->session_cache_memcached_host) { + auto &session_cacheconf = get_config()->tls.session_cache; + + if (session_cacheconf.memcached.host) { session_cache_memcached_dispatcher_ = make_unique( - &get_config()->session_cache_memcached_addr, loop); + &session_cacheconf.memcached.addr, loop); } if (get_config()->downstream_proto == PROTO_HTTP2) { diff --git a/src/shrpx_worker_process.cc b/src/shrpx_worker_process.cc index 0ab76932..181e4c96 100644 --- a/src/shrpx_worker_process.cc +++ b/src/shrpx_worker_process.cc @@ -170,7 +170,7 @@ void ipc_readcb(struct ev_loop *loop, ev_io *w, int revents) { namespace { int generate_ticket_key(TicketKey &ticket_key) { - ticket_key.cipher = get_config()->tls_ticket_key_cipher; + ticket_key.cipher = get_config()->tls.ticket.cipher; ticket_key.hmac = EVP_sha256(); ticket_key.hmac_keylen = EVP_MD_size(ticket_key.hmac); @@ -217,7 +217,7 @@ void renew_ticket_key_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto max_tickets = static_cast(std::chrono::duration_cast( - get_config()->tls_session_timeout).count()); + get_config()->tls.session_timeout).count()); new_keys.resize(std::min(max_tickets, old_keys.size() + 1)); std::copy_n(std::begin(old_keys), new_keys.size() - 1, @@ -297,14 +297,16 @@ void memcached_get_ticket_key_cb(struct ev_loop *loop, ev_timer *w, auto end = p + value.size(); p += 4; + auto &ticketconf = get_config()->tls.ticket; + size_t expectedlen; size_t enc_keylen; size_t hmac_keylen; - if (get_config()->tls_ticket_key_cipher == EVP_aes_128_cbc()) { + if (ticketconf.cipher == EVP_aes_128_cbc()) { expectedlen = 48; enc_keylen = 16; hmac_keylen = 16; - } else if (get_config()->tls_ticket_key_cipher == EVP_aes_256_cbc()) { + } else if (ticketconf.cipher == EVP_aes_256_cbc()) { expectedlen = 80; enc_keylen = 32; hmac_keylen = 32; @@ -335,7 +337,7 @@ void memcached_get_ticket_key_cb(struct ev_loop *loop, ev_timer *w, return; } auto key = TicketKey(); - key.cipher = get_config()->tls_ticket_key_cipher; + key.cipher = ticketconf.cipher; key.hmac = EVP_sha256(); key.hmac_keylen = hmac_keylen; @@ -423,10 +425,11 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { ev_timer renew_ticket_key_timer; if (!get_config()->upstream_no_tls) { - if (get_config()->tls_ticket_key_memcached_host) { + auto &ticketconf = get_config()->tls.ticket; + + if (ticketconf.memcached.host) { conn_handler.set_tls_ticket_key_memcached_dispatcher( - make_unique( - &get_config()->tls_ticket_key_memcached_addr, loop)); + make_unique(&ticketconf.memcached.addr, loop)); ev_timer_init(&renew_ticket_key_timer, memcached_get_ticket_key_cb, 0., 0.); @@ -435,8 +438,8 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { memcached_get_ticket_key_cb(loop, &renew_ticket_key_timer, 0); } else { bool auto_tls_ticket_key = true; - if (!get_config()->tls_ticket_key_files.empty()) { - if (!get_config()->tls_ticket_key_cipher_given) { + if (!ticketconf.files.empty()) { + if (!ticketconf.cipher_given) { LOG(WARN) << "It is strongly recommended to specify " "--tls-ticket-key-cipher=aes-128-cbc (or " @@ -446,8 +449,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { "becomes aes-256-cbc"; } auto ticket_keys = read_tls_ticket_key_file( - get_config()->tls_ticket_key_files, - get_config()->tls_ticket_key_cipher, EVP_sha256()); + ticketconf.files, ticketconf.cipher, EVP_sha256()); if (!ticket_keys) { LOG(WARN) << "Use internal session ticket key generator"; } else { @@ -512,7 +514,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { ipcev.data = &conn_handler; ev_io_start(loop, &ipcev); - if (!get_config()->upstream_no_tls && !get_config()->no_ocsp) { + if (!get_config()->upstream_no_tls && !get_config()->tls.ocsp.disabled) { conn_handler.proceed_next_cert_ocsp(); } From 16549bb276316ca3324698adae5b93af4e6688ae Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 18 Jan 2016 17:00:20 +0900 Subject: [PATCH 2/7] nghttpx: Structured configurations for http and http2 --- src/shrpx.cc | 177 ++++++++++++----------- src/shrpx_client_handler.cc | 18 ++- src/shrpx_config.cc | 77 +++++----- src/shrpx_config.h | 107 ++++++++------ src/shrpx_http.cc | 2 +- src/shrpx_http2_downstream_connection.cc | 38 +++-- src/shrpx_http2_session.cc | 21 +-- src/shrpx_http2_upstream.cc | 64 ++++---- src/shrpx_http_downstream_connection.cc | 29 ++-- src/shrpx_https_upstream.cc | 30 ++-- src/shrpx_spdy_upstream.cc | 54 ++++--- src/shrpx_worker.cc | 2 +- 12 files changed, 346 insertions(+), 273 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index 806dc240..5986ee89 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -900,7 +900,6 @@ void fill_default_config() { mod_config()->verbose = false; mod_config()->daemon = false; - mod_config()->server_name = "nghttpx nghttp2/" NGHTTP2_VERSION; mod_config()->host = strcopy("*"); mod_config()->port = 3000; @@ -926,25 +925,10 @@ void fill_default_config() { // Timeout for pooled (idle) connections mod_config()->downstream_idle_read_timeout = 2.; - // window bits for HTTP/2 and SPDY upstream/downstream connection - // per stream. 2**16-1 = 64KiB-1, which is HTTP/2 default. Please - // note that SPDY/3 default is 64KiB. - mod_config()->http2_upstream_window_bits = 16; - mod_config()->http2_downstream_window_bits = 16; - - // HTTP/2 SPDY/3.1 has connection-level flow control. The default - // window size for HTTP/2 is 64KiB - 1. SPDY/3's default is 64KiB - mod_config()->http2_upstream_connection_window_bits = 16; - mod_config()->http2_downstream_connection_window_bits = 16; - mod_config()->upstream_no_tls = false; mod_config()->downstream_no_tls = false; mod_config()->num_worker = 1; - mod_config()->http2_max_concurrent_streams = 100; - mod_config()->add_x_forwarded_for = false; - mod_config()->strip_incoming_x_forwarded_for = false; - mod_config()->no_via = false; mod_config()->accesslog_file = nullptr; mod_config()->accesslog_syslog = false; mod_config()->accesslog_format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT); @@ -966,7 +950,6 @@ void fill_default_config() { mod_config()->pid = getpid(); mod_config()->backend_ipv4 = false; mod_config()->backend_ipv6 = false; - mod_config()->downstream_http_proxy_port = 0; mod_config()->read_rate = 0; mod_config()->read_burst = 0; mod_config()->write_rate = 0; @@ -975,25 +958,9 @@ void fill_default_config() { mod_config()->worker_read_burst = 0; mod_config()->worker_write_rate = 0; mod_config()->worker_write_burst = 0; - mod_config()->http2_upstream_dump_request_header = nullptr; - mod_config()->http2_upstream_dump_response_header = nullptr; - mod_config()->http2_no_cookie_crumbling = false; - mod_config()->upstream_frame_debug = false; mod_config()->padding = 0; mod_config()->worker_frontend_connections = 0; - nghttp2_option_new(&mod_config()->http2_option); - nghttp2_option_set_no_auto_window_update(get_config()->http2_option, 1); - nghttp2_option_set_no_recv_client_magic(get_config()->http2_option, 1); - - nghttp2_option_new(&mod_config()->http2_client_option); - nghttp2_option_set_no_auto_window_update(get_config()->http2_client_option, - 1); - nghttp2_option_set_peer_max_concurrent_streams( - get_config()->http2_client_option, 100); - - mod_config()->no_location_rewrite = false; - mod_config()->no_host_rewrite = true; mod_config()->argc = 0; mod_config()->argv = nullptr; mod_config()->downstream_connections_per_host = 8; @@ -1001,11 +968,7 @@ void fill_default_config() { mod_config()->listener_disable_timeout = 0.; mod_config()->downstream_request_buffer_size = 16_k; mod_config()->downstream_response_buffer_size = 16_k; - mod_config()->no_server_push = false; mod_config()->host_unix = false; - mod_config()->http2_downstream_connections_per_worker = 0; - mod_config()->header_field_buffer = 64_k; - mod_config()->max_header_fields = 100; mod_config()->downstream_addr_group_catch_all = 0; mod_config()->fastopen = 0; @@ -1040,12 +1003,57 @@ void fill_default_config() { tlsconf.tls_proto_mask = 0; tlsconf.insecure = false; } + + auto &httpconf = mod_config()->http; + { + auto &xffconf = httpconf.xff; + xffconf.add = false; + xffconf.strip_incoming = false; + } + + httpconf.server_name = "nghttpx nghttp2/" NGHTTP2_VERSION; + httpconf.no_via = false; + httpconf.no_location_rewrite = false; + httpconf.no_host_rewrite = true; + httpconf.header_field_buffer = 64_k; + httpconf.max_header_fields = 100; + + auto &http2conf = mod_config()->http2; + { + auto &upstreamconf = http2conf.upstream; + // window bits for HTTP/2 and SPDY upstream connection per + // stream. 2**16-1 = 64KiB-1, which is HTTP/2 default. Please note + // that SPDY/3 default is 64KiB. + upstreamconf.window_bits = 16; + // HTTP/2 SPDY/3.1 has connection-level flow control. The default + // window size for HTTP/2 is 64KiB - 1. SPDY/3's default is 64KiB + upstreamconf.connection_window_bits = 16; + + nghttp2_option_new(&upstreamconf.option); + nghttp2_option_set_no_auto_window_update(upstreamconf.option, 1); + nghttp2_option_set_no_recv_client_magic(upstreamconf.option, 1); + } + { + auto &downstreamconf = http2conf.downstream; + downstreamconf.window_bits = 16; + downstreamconf.connection_window_bits = 16; + downstreamconf.connections_per_worker = 0; + + nghttp2_option_new(&downstreamconf.option); + nghttp2_option_set_no_auto_window_update(downstreamconf.option, 1); + nghttp2_option_set_peer_max_concurrent_streams(downstreamconf.option, 100); + } + + http2conf.max_concurrent_streams = 100; + http2conf.no_cookie_crumbling = false; + http2conf.no_server_push = false; } + } // namespace namespace { void print_version(std::ostream &out) { - out << get_config()->server_name.c_str() << std::endl; + out << get_config()->http.server_name.c_str() << std::endl; } } // namespace @@ -1450,29 +1458,29 @@ HTTP/2 and SPDY: -c, --http2-max-concurrent-streams= Set the maximum number of the concurrent streams in one HTTP/2 and SPDY session. - Default: )" << get_config()->http2_max_concurrent_streams << R"( + Default: )" << get_config()->http2.max_concurrent_streams << R"( --frontend-http2-window-bits= Sets the per-stream initial window size of HTTP/2 SPDY frontend connection. For HTTP/2, the size is 2**-1. For SPDY, the size is 2**. - Default: )" << get_config()->http2_upstream_window_bits << R"( + Default: )" << get_config()->http2.upstream.window_bits << R"( --frontend-http2-connection-window-bits= Sets the per-connection window size of HTTP/2 and SPDY frontend connection. For HTTP/2, the size is 2**-1. For SPDY, the size is 2**. - Default: )" << get_config()->http2_upstream_connection_window_bits + Default: )" << get_config()->http2.upstream.connection_window_bits << R"( --frontend-no-tls Disable SSL/TLS on frontend connections. --backend-http2-window-bits= Sets the initial window size of HTTP/2 backend connection to 2**-1. - Default: )" << get_config()->http2_downstream_window_bits << R"( + Default: )" << get_config()->http2.downstream.window_bits << R"( --backend-http2-connection-window-bits= Sets the per-connection window size of HTTP/2 backend connection to 2**-1. Default: )" - << get_config()->http2_downstream_connection_window_bits << R"( + << get_config()->http2.downstream.connection_window_bits << R"( --backend-no-tls Disable SSL/TLS on backend connections. --http2-no-cookie-crumbling @@ -1650,13 +1658,13 @@ HTTP: Set maximum buffer size for incoming HTTP request header field list. This is the sum of header name and value in bytes. - Default: )" << util::utos_unit(get_config()->header_field_buffer) - << R"( + Default: )" + << util::utos_unit(get_config()->http.header_field_buffer) << R"( --max-header-fields= Set maximum number of incoming HTTP request header fields, which appear in one request or response header field list. - Default: )" << get_config()->max_header_fields << R"( + Default: )" << get_config()->http.max_header_fields << R"( Debug: --frontend-http2-dump-request-header= @@ -2374,44 +2382,49 @@ int main(int argc, char **argv) { } } - if (get_config()->http2_upstream_dump_request_header_file) { - auto path = get_config()->http2_upstream_dump_request_header_file.get(); - auto f = open_file_for_write(path); + auto &http2conf = mod_config()->http2; + { + auto &dumpconf = http2conf.upstream.debug.dump; - if (f == nullptr) { - LOG(FATAL) << "Failed to open http2 upstream request header file: " - << path; - exit(EXIT_FAILURE); - } + if (dumpconf.request_header_file) { + auto path = dumpconf.request_header_file.get(); + auto f = open_file_for_write(path); - mod_config()->http2_upstream_dump_request_header = f; + if (f == nullptr) { + LOG(FATAL) << "Failed to open http2 upstream request header file: " + << path; + exit(EXIT_FAILURE); + } - if (get_config()->uid != 0) { - if (chown_to_running_user(path) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of http2 upstream request header file " - << path << " failed: " << strerror(error); + dumpconf.request_header = f; + + if (get_config()->uid != 0) { + if (chown_to_running_user(path) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of http2 upstream request header file " + << path << " failed: " << strerror(error); + } } } - } - if (get_config()->http2_upstream_dump_response_header_file) { - auto path = get_config()->http2_upstream_dump_response_header_file.get(); - auto f = open_file_for_write(path); + if (dumpconf.response_header_file) { + auto path = dumpconf.response_header_file.get(); + auto f = open_file_for_write(path); - if (f == nullptr) { - LOG(FATAL) << "Failed to open http2 upstream response header file: " - << path; - exit(EXIT_FAILURE); - } + if (f == nullptr) { + LOG(FATAL) << "Failed to open http2 upstream response header file: " + << path; + exit(EXIT_FAILURE); + } - mod_config()->http2_upstream_dump_response_header = f; + dumpconf.response_header = f; - if (get_config()->uid != 0) { - if (chown_to_running_user(path) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of http2 upstream response header file" - << " " << path << " failed: " << strerror(error); + if (get_config()->uid != 0) { + if (chown_to_running_user(path) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of http2 upstream response header file" + << " " << path << " failed: " << strerror(error); + } } } } @@ -2617,16 +2630,18 @@ int main(int argc, char **argv) { } } - if (get_config()->forwarded_by_node_type == FORWARDED_NODE_OBFUSCATED && - get_config()->forwarded_by_obfuscated.empty()) { + auto &fwdconf = mod_config()->http.forwarded; + + if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED && + fwdconf.by_obfuscated.empty()) { std::random_device rd; std::mt19937 gen(rd()); - auto &dst = mod_config()->forwarded_by_obfuscated; + auto &dst = fwdconf.by_obfuscated; dst = "_"; dst += util::random_alpha_digit(gen, SHRPX_OBFUSCATED_NODE_LENGTH); } - if (get_config()->upstream_frame_debug) { + if (get_config()->http2.upstream.debug.frame_debug) { // To make it sync to logging set_output(stderr); if (isatty(fileno(stdout))) { @@ -2635,8 +2650,8 @@ int main(int argc, char **argv) { reset_timer(); } - mod_config()->http2_upstream_callbacks = create_http2_upstream_callbacks(); - mod_config()->http2_downstream_callbacks = + mod_config()->http2.upstream.callbacks = create_http2_upstream_callbacks(); + mod_config()->http2.downstream.callbacks = create_http2_downstream_callbacks(); if (event_loop() != 0) { diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index 4993cb71..1313c9b1 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -404,14 +404,16 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl, setup_upstream_io_callback(); } - if ((get_config()->forwarded_params & FORWARDED_FOR) && - get_config()->forwarded_for_node_type == FORWARDED_NODE_OBFUSCATED) { - if (get_config()->forwarded_for_obfuscated.empty()) { + auto &fwdconf = get_config()->http.forwarded; + + if ((fwdconf.params & FORWARDED_FOR) && + fwdconf.for_node_type == FORWARDED_NODE_OBFUSCATED) { + if (fwdconf.for_obfuscated.empty()) { forwarded_for_obfuscated_ = "_"; forwarded_for_obfuscated_ += util::random_alpha_digit( worker_->get_randgen(), SHRPX_OBFUSCATED_NODE_LENGTH); } else { - forwarded_for_obfuscated_ = get_config()->forwarded_for_obfuscated; + forwarded_for_obfuscated_ = fwdconf.for_obfuscated; } } } @@ -1120,8 +1122,10 @@ int ClientHandler::proxy_protocol_read() { } const std::string &ClientHandler::get_forwarded_by() { - if (get_config()->forwarded_by_node_type == FORWARDED_NODE_OBFUSCATED) { - return get_config()->forwarded_by_obfuscated; + auto &fwdconf = get_config()->http.forwarded; + + if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED) { + return fwdconf.by_obfuscated; } if (!local_hostport_.empty()) { return local_hostport_; @@ -1158,7 +1162,7 @@ const std::string &ClientHandler::get_forwarded_by() { } const std::string &ClientHandler::get_forwarded_for() const { - if (get_config()->forwarded_for_node_type == FORWARDED_NODE_OBFUSCATED) { + if (get_config()->http.forwarded.for_node_type == FORWARDED_NODE_OBFUSCATED) { return forwarded_for_obfuscated_; } diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index a3d6c6fa..3bda4f42 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -1447,7 +1447,7 @@ int parse_config(const char *opt, const char *optarg, return parse_uint(&mod_config()->num_worker, opt, optarg); #endif // !NOTHREADS case SHRPX_OPTID_HTTP2_MAX_CONCURRENT_STREAMS: - return parse_uint(&mod_config()->http2_max_concurrent_streams, opt, optarg); + return parse_uint(&mod_config()->http2.max_concurrent_streams, opt, optarg); case SHRPX_OPTID_LOG_LEVEL: if (Log::set_severity_level_by_name(optarg) == -1) { LOG(ERROR) << opt << ": Invalid severity level: " << optarg; @@ -1472,15 +1472,15 @@ int parse_config(const char *opt, const char *optarg, return 0; case SHRPX_OPTID_ADD_X_FORWARDED_FOR: - mod_config()->add_x_forwarded_for = util::strieq(optarg, "yes"); + mod_config()->http.xff.add = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_STRIP_INCOMING_X_FORWARDED_FOR: - mod_config()->strip_incoming_x_forwarded_for = util::strieq(optarg, "yes"); + mod_config()->http.xff.strip_incoming = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_NO_VIA: - mod_config()->no_via = util::strieq(optarg, "yes"); + mod_config()->http.no_via = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT: @@ -1541,9 +1541,9 @@ int parse_config(const char *opt, const char *optarg, size_t *resp; if (optid == SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS) { - resp = &mod_config()->http2_upstream_window_bits; + resp = &mod_config()->http2.upstream.window_bits; } else { - resp = &mod_config()->http2_downstream_window_bits; + resp = &mod_config()->http2.downstream.window_bits; } errno = 0; @@ -1569,9 +1569,9 @@ int parse_config(const char *opt, const char *optarg, size_t *resp; if (optid == SHRPX_OPTID_FRONTEND_HTTP2_CONNECTION_WINDOW_BITS) { - resp = &mod_config()->http2_upstream_connection_window_bits; + resp = &mod_config()->http2.upstream.connection_window_bits; } else { - resp = &mod_config()->http2_downstream_connection_window_bits; + resp = &mod_config()->http2.downstream.connection_window_bits; } errno = 0; @@ -1786,19 +1786,22 @@ int parse_config(const char *opt, const char *optarg, return 0; case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_REQUEST_HEADER: - mod_config()->http2_upstream_dump_request_header_file = strcopy(optarg); + mod_config()->http2.upstream.debug.dump.request_header_file = + strcopy(optarg); return 0; case SHRPX_OPTID_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER: - mod_config()->http2_upstream_dump_response_header_file = strcopy(optarg); + mod_config()->http2.upstream.debug.dump.response_header_file = + strcopy(optarg); return 0; case SHRPX_OPTID_HTTP2_NO_COOKIE_CRUMBLING: - mod_config()->http2_no_cookie_crumbling = util::strieq(optarg, "yes"); + mod_config()->http2.no_cookie_crumbling = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_FRONTEND_FRAME_DEBUG: - mod_config()->upstream_frame_debug = util::strieq(optarg, "yes"); + mod_config()->http2.upstream.debug.frame_debug = + util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_PADDING: @@ -1845,7 +1848,7 @@ int parse_config(const char *opt, const char *optarg, } } - mod_config()->altsvcs.push_back(std::move(altsvc)); + mod_config()->http.altsvcs.push_back(std::move(altsvc)); return 0; } @@ -1857,16 +1860,16 @@ int parse_config(const char *opt, const char *optarg, return -1; } if (optid == SHRPX_OPTID_ADD_REQUEST_HEADER) { - mod_config()->add_request_headers.push_back(std::move(p)); + mod_config()->http.add_request_headers.push_back(std::move(p)); } else { - mod_config()->add_response_headers.push_back(std::move(p)); + mod_config()->http.add_response_headers.push_back(std::move(p)); } return 0; } case SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS: return parse_uint(&mod_config()->worker_frontend_connections, opt, optarg); case SHRPX_OPTID_NO_LOCATION_REWRITE: - mod_config()->no_location_rewrite = util::strieq(optarg, "yes"); + mod_config()->http.no_location_rewrite = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_NO_HOST_REWRITE: @@ -1941,11 +1944,11 @@ int parse_config(const char *opt, const char *optarg, } case SHRPX_OPTID_NO_SERVER_PUSH: - mod_config()->no_server_push = util::strieq(optarg, "yes"); + mod_config()->http2.no_server_push = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_BACKEND_HTTP2_CONNECTIONS_PER_WORKER: - return parse_uint(&mod_config()->http2_downstream_connections_per_worker, + return parse_uint(&mod_config()->http2.downstream.connections_per_worker, opt, optarg); case SHRPX_OPTID_FETCH_OCSP_RESPONSE_FILE: mod_config()->tls.ocsp.fetch_ocsp_response_file = strcopy(optarg); @@ -1958,10 +1961,10 @@ int parse_config(const char *opt, const char *optarg, return 0; case SHRPX_OPTID_HEADER_FIELD_BUFFER: - return parse_uint_with_unit(&mod_config()->header_field_buffer, opt, + return parse_uint_with_unit(&mod_config()->http.header_field_buffer, opt, optarg); case SHRPX_OPTID_MAX_HEADER_FIELDS: - return parse_uint(&mod_config()->max_header_fields, opt, optarg); + return parse_uint(&mod_config()->http.max_header_fields, opt, optarg); case SHRPX_OPTID_INCLUDE: { if (included_set.count(optarg)) { LOG(ERROR) << opt << ": " << optarg << " has already been included"; @@ -1992,7 +1995,7 @@ int parse_config(const char *opt, const char *optarg, return 0; case SHRPX_OPTID_HOST_REWRITE: - mod_config()->no_host_rewrite = !util::strieq(optarg, "yes"); + mod_config()->http.no_host_rewrite = !util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_TLS_SESSION_CACHE_MEMCACHED: { @@ -2065,23 +2068,24 @@ int parse_config(const char *opt, const char *optarg, mod_config()->accept_proxy_protocol = util::strieq(optarg, "yes"); return 0; - case SHRPX_OPTID_ADD_FORWARDED: - mod_config()->forwarded_params = FORWARDED_NONE; + case SHRPX_OPTID_ADD_FORWARDED: { + auto &fwdconf = mod_config()->http.forwarded; + fwdconf.params = FORWARDED_NONE; for (const auto ¶m : util::parse_config_str_list(optarg)) { if (util::strieq(param, "by")) { - mod_config()->forwarded_params |= FORWARDED_BY; + fwdconf.params |= FORWARDED_BY; continue; } if (util::strieq(param, "for")) { - mod_config()->forwarded_params |= FORWARDED_FOR; + fwdconf.params |= FORWARDED_FOR; continue; } if (util::strieq(param, "host")) { - mod_config()->forwarded_params |= FORWARDED_HOST; + fwdconf.params |= FORWARDED_HOST; continue; } if (util::strieq(param, "proto")) { - mod_config()->forwarded_params |= FORWARDED_PROTO; + fwdconf.params |= FORWARDED_PROTO; continue; } @@ -2091,8 +2095,9 @@ int parse_config(const char *opt, const char *optarg, } return 0; + } case SHRPX_OPTID_STRIP_INCOMING_FORWARDED: - mod_config()->strip_incoming_forwarded = util::strieq(optarg, "yes"); + mod_config()->http.forwarded.strip_incoming = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_FORWARDED_BY: @@ -2105,23 +2110,23 @@ int parse_config(const char *opt, const char *optarg, return -1; } + auto &fwdconf = mod_config()->http.forwarded; + switch (optid) { case SHRPX_OPTID_FORWARDED_BY: - mod_config()->forwarded_by_node_type = - static_cast(type); + fwdconf.by_node_type = static_cast(type); if (optarg[0] == '_') { - mod_config()->forwarded_by_obfuscated = optarg; + fwdconf.by_obfuscated = optarg; } else { - mod_config()->forwarded_by_obfuscated = ""; + fwdconf.by_obfuscated = ""; } break; case SHRPX_OPTID_FORWARDED_FOR: - mod_config()->forwarded_for_node_type = - static_cast(type); + fwdconf.for_node_type = static_cast(type); if (optarg[0] == '_') { - mod_config()->forwarded_for_obfuscated = optarg; + fwdconf.for_obfuscated = optarg; } else { - mod_config()->forwarded_for_obfuscated = ""; + fwdconf.for_obfuscated = ""; } break; } diff --git a/src/shrpx_config.h b/src/shrpx_config.h index e0636bdd..b94751b3 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -289,6 +289,7 @@ struct HttpProxy { std::string host; // userinfo in http proxy URI, not percent-encoded form std::string userinfo; + // port in http proxy URI uint16_t port; }; @@ -371,23 +372,76 @@ struct TLSConfig { bool insecure; }; -struct Config { +struct HttpConfig { + struct { + // obfuscated value used in "by" parameter of Forwarded header + // field. + std::string by_obfuscated; + // obfuscated value used in "for" parameter of Forwarded header + // field. This is only used when user defined static obfuscated + // string is provided. + std::string for_obfuscated; + // bitwise-OR of one or more of shrpx_forwarded_param values. + uint32_t params; + // type of value recorded in "by" parameter of Forwarded header + // field. + shrpx_forwarded_node_type by_node_type; + // type of value recorded in "for" parameter of Forwarded header + // field. + shrpx_forwarded_node_type for_node_type; + bool strip_incoming; + } forwarded; + struct { + bool add; + bool strip_incoming; + } xff; std::vector altsvcs; std::vector> add_request_headers; std::vector> add_response_headers; + StringRef server_name; + size_t header_field_buffer; + size_t max_header_fields; + bool no_via; + bool no_location_rewrite; + bool no_host_rewrite; +}; + +struct Http2Config { + struct { + struct { + struct { + std::unique_ptr request_header_file; + std::unique_ptr response_header_file; + FILE *request_header; + FILE *response_header; + } dump; + bool frame_debug; + } debug; + nghttp2_option *option; + nghttp2_session_callbacks *callbacks; + size_t window_bits; + size_t connection_window_bits; + } upstream; + struct { + nghttp2_option *option; + nghttp2_session_callbacks *callbacks; + size_t window_bits; + size_t connection_window_bits; + size_t connections_per_worker; + } downstream; + size_t max_concurrent_streams; + bool no_cookie_crumbling; + bool no_server_push; +}; + +struct Config { std::vector accesslog_format; std::vector downstream_addr_groups; Router router; HttpProxy downstream_http_proxy; + HttpConfig http; + Http2Config http2; TLSConfig tls; - // obfuscated value used in "by" parameter of Forwarded header - // field. - std::string forwarded_by_obfuscated; - // obfuscated value used in "for" parameter of Forwarded header - // field. This is only used when user defined static obfuscated - // string is provided. - std::string forwarded_for_obfuscated; - StringRef server_name; ev_tstamp http2_upstream_read_timeout; ev_tstamp upstream_read_timeout; ev_tstamp upstream_write_timeout; @@ -402,28 +456,14 @@ struct Config { std::unique_ptr host; std::unique_ptr pid_file; std::unique_ptr conf_path; - std::unique_ptr http2_upstream_dump_request_header_file; - std::unique_ptr http2_upstream_dump_response_header_file; std::unique_ptr accesslog_file; std::unique_ptr errorlog_file; std::unique_ptr user; std::unique_ptr mruby_file; - FILE *http2_upstream_dump_request_header; - FILE *http2_upstream_dump_response_header; - nghttp2_session_callbacks *http2_upstream_callbacks; - nghttp2_session_callbacks *http2_downstream_callbacks; - nghttp2_option *http2_option; - nghttp2_option *http2_client_option; char **original_argv; char **argv; char *cwd; size_t num_worker; - size_t http2_max_concurrent_streams; - size_t http2_upstream_window_bits; - size_t http2_downstream_window_bits; - size_t http2_upstream_connection_window_bits; - size_t http2_downstream_connection_window_bits; - size_t http2_downstream_connections_per_worker; size_t downstream_connections_per_host; size_t downstream_connections_per_frontend; size_t read_rate; @@ -439,20 +479,10 @@ struct Config { size_t rlimit_nofile; size_t downstream_request_buffer_size; size_t downstream_response_buffer_size; - size_t header_field_buffer; - size_t max_header_fields; // The index of catch-all group in downstream_addr_groups. size_t downstream_addr_group_catch_all; // downstream protocol; this will be determined by given options. shrpx_proto downstream_proto; - // bitwise-OR of one or more of shrpx_forwarded_param values. - uint32_t forwarded_params; - // type of value recorded in "by" parameter of Forwarded header - // field. - shrpx_forwarded_node_type forwarded_by_node_type; - // type of value recorded in "for" parameter of Forwarded header - // field. - shrpx_forwarded_node_type forwarded_for_node_type; int syslog_facility; int backlog; int argc; @@ -463,17 +493,11 @@ struct Config { // frontend listening port. 0 if frontend listens on UNIX domain // socket, in this case |host_unix| must be true. uint16_t port; - // port in http proxy URI - uint16_t downstream_http_proxy_port; bool verbose; bool daemon; bool http2_proxy; bool http2_bridge; bool client_proxy; - bool add_x_forwarded_for; - bool strip_incoming_x_forwarded_for; - bool strip_incoming_forwarded; - bool no_via; bool upstream_no_tls; bool downstream_no_tls; // Send accesslog to syslog, ignoring accesslog_file. @@ -485,11 +509,6 @@ struct Config { bool client_mode; bool backend_ipv4; bool backend_ipv6; - bool http2_no_cookie_crumbling; - bool upstream_frame_debug; - bool no_location_rewrite; - bool no_host_rewrite; - bool no_server_push; // true if host contains UNIX domain socket path bool host_unix; bool accept_proxy_protocol; diff --git a/src/shrpx_http.cc b/src/shrpx_http.cc index 79524fe9..0a67247b 100644 --- a/src/shrpx_http.cc +++ b/src/shrpx_http.cc @@ -44,7 +44,7 @@ std::string create_error_html(unsigned int status_code) { res += "

"; res += status; res += "

"; - const auto &server_name = get_config()->server_name; + const auto &server_name = get_config()->http.server_name; res.append(server_name.c_str(), server_name.size()); res += " at port "; res += util::utos(get_config()->port); diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index e46cefed..c39a722c 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -256,8 +256,11 @@ int Http2DownstreamConnection::push_request_headers() { const auto &req = downstream_->request(); + auto &httpconf = get_config()->http; + auto &http2conf = get_config()->http2; + auto no_host_rewrite = - get_config()->no_host_rewrite || get_config()->http2_proxy || + httpconf.no_host_rewrite || get_config()->http2_proxy || get_config()->client_proxy || req.method == HTTP_CONNECT; // http2session_ has already in CONNECTED state, so we can get @@ -278,7 +281,7 @@ int Http2DownstreamConnection::push_request_headers() { downstream_->set_request_downstream_host(authority.str()); size_t num_cookies = 0; - if (!get_config()->http2_no_cookie_crumbling) { + if (!http2conf.no_cookie_crumbling) { num_cookies = downstream_->count_crumble_request_cookie(); } @@ -294,7 +297,7 @@ int Http2DownstreamConnection::push_request_headers() { // 9. forwarded (optional) auto nva = std::vector(); nva.reserve(req.fs.headers().size() + 9 + num_cookies + - get_config()->add_request_headers.size()); + httpconf.add_request_headers.size()); nva.push_back( http2::make_nv_lc_nocopy(":method", http2::to_method_string(req.method))); @@ -328,7 +331,7 @@ int Http2DownstreamConnection::push_request_headers() { chunked_encoding = true; } - if (!get_config()->http2_no_cookie_crumbling) { + if (!http2conf.no_cookie_crumbling) { downstream_->crumble_request_cookie(nva); } @@ -337,12 +340,13 @@ int Http2DownstreamConnection::push_request_headers() { std::string forwarded_value; - auto fwd = get_config()->strip_incoming_forwarded - ? nullptr - : req.fs.header(http2::HD_FORWARDED); + auto &fwdconf = httpconf.forwarded; - if (get_config()->forwarded_params) { - auto params = get_config()->forwarded_params; + auto fwd = + fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED); + + if (fwdconf.params) { + auto params = fwdconf.params; if (get_config()->http2_proxy || get_config()->client_proxy || req.method == HTTP_CONNECT) { @@ -370,12 +374,14 @@ int Http2DownstreamConnection::push_request_headers() { forwarded_value = fwd->value; } - std::string xff_value; - auto xff = get_config()->strip_incoming_x_forwarded_for - ? nullptr - : req.fs.header(http2::HD_X_FORWARDED_FOR); + auto &xffconf = httpconf.xff; - if (get_config()->add_x_forwarded_for) { + auto xff = xffconf.strip_incoming ? nullptr + : req.fs.header(http2::HD_X_FORWARDED_FOR); + + std::string xff_value; + + if (xffconf.add) { if (xff) { xff_value = (*xff).value; xff_value += ", "; @@ -394,7 +400,7 @@ int Http2DownstreamConnection::push_request_headers() { std::string via_value; auto via = req.fs.header(http2::HD_VIA); - if (get_config()->no_via) { + if (httpconf.no_via) { if (via) { nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value)); } @@ -415,7 +421,7 @@ int Http2DownstreamConnection::push_request_headers() { nva.push_back(http2::make_nv_ll("te", "trailers")); } - for (auto &p : get_config()->add_request_headers) { + for (auto &p : httpconf.add_request_headers) { nva.push_back(http2::make_nv_nocopy(p.first, p.second)); } diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 48efa69e..24cb4ce1 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -1309,9 +1309,10 @@ int Http2Session::connection_made() { } } - rv = nghttp2_session_client_new2(&session_, - get_config()->http2_downstream_callbacks, - this, get_config()->http2_client_option); + auto &http2conf = get_config()->http2; + + rv = nghttp2_session_client_new2(&session_, http2conf.downstream.callbacks, + this, http2conf.downstream.option); if (rv != 0) { return -1; @@ -1322,12 +1323,12 @@ int Http2Session::connection_made() { std::array entry; size_t nentry = 2; entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - entry[0].value = get_config()->http2_max_concurrent_streams; + entry[0].value = http2conf.max_concurrent_streams; entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - entry[1].value = (1 << get_config()->http2_downstream_window_bits) - 1; + entry[1].value = (1 << http2conf.downstream.window_bits) - 1; - if (get_config()->no_server_push || get_config()->http2_proxy || + if (http2conf.no_server_push || get_config()->http2_proxy || get_config()->client_proxy) { entry[nentry].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH; entry[nentry].value = 0; @@ -1340,10 +1341,10 @@ int Http2Session::connection_made() { return -1; } - if (get_config()->http2_downstream_connection_window_bits > 16) { - int32_t delta = - (1 << get_config()->http2_downstream_connection_window_bits) - 1 - - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + auto connection_window_bits = http2conf.downstream.connection_window_bits; + if (connection_window_bits > 16) { + int32_t delta = (1 << connection_window_bits) - 1 - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta); if (rv != 0) { return -1; diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 03de4fc7..f152434a 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -154,7 +154,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data) { - if (get_config()->upstream_frame_debug) { + if (get_config()->http2.upstream.debug.frame_debug) { verbose_on_header_callback(session, frame, name, namelen, value, valuelen, flags, user_data); } @@ -170,9 +170,11 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, auto &req = downstream->request(); + auto &httpconf = get_config()->http; + if (req.fs.buffer_size() + namelen + valuelen > - get_config()->header_field_buffer || - req.fs.num_fields() >= get_config()->max_header_fields) { + httpconf.header_field_buffer || + req.fs.num_fields() >= httpconf.max_header_fields) { if (downstream->get_response_state() == Downstream::MSG_COMPLETE) { return 0; } @@ -263,8 +265,10 @@ int Http2Upstream::on_request_headers(Downstream *downstream, << downstream->get_stream_id() << "\n" << ss.str(); } - if (get_config()->http2_upstream_dump_request_header) { - http2::dump_nv(get_config()->http2_upstream_dump_request_header, nva); + auto &dump = get_config()->http2.upstream.debug.dump; + + if (dump.request_header) { + http2::dump_nv(dump.request_header, nva); } auto content_length = req.fs.header(http2::HD_CONTENT_LENGTH); @@ -398,7 +402,7 @@ void Http2Upstream::initiate_downstream(Downstream *downstream) { namespace { int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { - if (get_config()->upstream_frame_debug) { + if (get_config()->http2.upstream.debug.frame_debug) { verbose_on_frame_recv_callback(session, frame, user_data); } auto upstream = static_cast(user_data); @@ -500,7 +504,7 @@ int on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags, namespace { int on_frame_send_callback(nghttp2_session *session, const nghttp2_frame *frame, void *user_data) { - if (get_config()->upstream_frame_debug) { + if (get_config()->http2.upstream.debug.frame_debug) { verbose_on_frame_send_callback(session, frame, user_data); } auto upstream = static_cast(user_data); @@ -860,9 +864,10 @@ Http2Upstream::Http2Upstream(ClientHandler *handler) int rv; - rv = nghttp2_session_server_new2(&session_, - get_config()->http2_upstream_callbacks, this, - get_config()->http2_option); + auto &http2conf = get_config()->http2; + + rv = nghttp2_session_server_new2(&session_, http2conf.upstream.callbacks, + this, http2conf.upstream.option); assert(rv == 0); @@ -871,10 +876,10 @@ Http2Upstream::Http2Upstream(ClientHandler *handler) // TODO Maybe call from outside? std::array entry; entry[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS; - entry[0].value = get_config()->http2_max_concurrent_streams; + entry[0].value = http2conf.max_concurrent_streams; entry[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE; - entry[1].value = (1 << get_config()->http2_upstream_window_bits) - 1; + entry[1].value = (1 << http2conf.upstream.window_bits) - 1; rv = nghttp2_submit_settings(session_, NGHTTP2_FLAG_NONE, entry.data(), entry.size()); @@ -883,9 +888,9 @@ Http2Upstream::Http2Upstream(ClientHandler *handler) << nghttp2_strerror(rv); } - if (get_config()->http2_upstream_connection_window_bits > 16) { - int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits) - - 1 - NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; + if (http2conf.upstream.connection_window_bits > 16) { + int32_t delta = (1 << http2conf.upstream.connection_window_bits) - 1 - + NGHTTP2_INITIAL_CONNECTION_WINDOW_SIZE; rv = nghttp2_submit_window_update(session_, NGHTTP2_FLAG_NONE, 0, delta); if (rv != 0) { @@ -1337,7 +1342,7 @@ int Http2Upstream::send_reply(Downstream *downstream, const uint8_t *body, if (!resp.fs.header(http2::HD_SERVER)) { nva.push_back( - http2::make_nv_ls_nocopy("server", get_config()->server_name)); + http2::make_nv_ls_nocopy("server", get_config()->http.server_name)); } rv = nghttp2_submit_response(session_, downstream->get_stream_id(), @@ -1386,7 +1391,7 @@ int Http2Upstream::error_reply(Downstream *downstream, : http2::make_nv_ls(":status", (status_code_str = util::utos(status_code))), http2::make_nv_ll("content-type", "text/html; charset=UTF-8"), - http2::make_nv_ls_nocopy("server", get_config()->server_name), + http2::make_nv_ls_nocopy("server", get_config()->http.server_name), http2::make_nv_ls("content-length", content_length), http2::make_nv_ls("date", lgconf->time_http_str)); @@ -1442,8 +1447,10 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { } } + auto &httpconf = get_config()->http; + if (!get_config()->http2_proxy && !get_config()->client_proxy && - !get_config()->no_location_rewrite) { + !httpconf.no_location_rewrite) { downstream->rewrite_location_response_header(req.scheme); } @@ -1470,7 +1477,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { // 4 means :status and possible server, via and x-http2-push header // field. nva.reserve(resp.fs.headers().size() + 4 + - get_config()->add_response_headers.size()); + httpconf.add_response_headers.size()); std::string via_value; std::string response_status; @@ -1506,8 +1513,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { http2::copy_headers_to_nva_nocopy(nva, resp.fs.headers()); if (!get_config()->http2_proxy && !get_config()->client_proxy) { - nva.push_back( - http2::make_nv_ls_nocopy("server", get_config()->server_name)); + nva.push_back(http2::make_nv_ls_nocopy("server", httpconf.server_name)); } else { auto server = resp.fs.header(http2::HD_SERVER); if (server) { @@ -1516,7 +1522,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { } auto via = resp.fs.header(http2::HD_VIA); - if (get_config()->no_via) { + if (httpconf.no_via) { if (via) { nva.push_back(http2::make_nv_ls_nocopy("via", (*via).value)); } @@ -1530,7 +1536,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { nva.push_back(http2::make_nv_ls("via", via_value)); } - for (auto &p : get_config()->add_response_headers) { + for (auto &p : httpconf.add_response_headers) { nva.push_back(http2::make_nv_nocopy(p.first, p.second)); } @@ -1544,9 +1550,11 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { log_response_headers(downstream, nva); } - if (get_config()->http2_upstream_dump_response_header) { - http2::dump_nv(get_config()->http2_upstream_dump_response_header, - nva.data(), nva.size()); + auto &http2conf = get_config()->http2; + + if (http2conf.upstream.debug.dump.response_header) { + http2::dump_nv(http2conf.upstream.debug.dump.response_header, nva.data(), + nva.size()); } nghttp2_data_provider data_prd; @@ -1574,7 +1582,7 @@ int Http2Upstream::on_downstream_header_complete(Downstream *downstream) { // * We requires GET or POST for associated resource. Probably we // don't want to push for HEAD request. Not sure other methods // are also eligible for push. - if (!get_config()->no_server_push && + if (!http2conf.no_server_push && nghttp2_session_get_remote_settings(session_, NGHTTP2_SETTINGS_ENABLE_PUSH) == 1 && !get_config()->http2_proxy && !get_config()->client_proxy && @@ -1875,7 +1883,7 @@ int Http2Upstream::submit_push_promise(const std::string &scheme, } bool Http2Upstream::push_enabled() const { - return !(get_config()->no_server_push || + return !(get_config()->http2.no_server_push || nghttp2_session_get_remote_settings( session_, NGHTTP2_SETTINGS_ENABLE_PUSH) == 0 || get_config()->http2_proxy || get_config()->client_proxy); diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index 542259f7..3f86ad6c 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -217,10 +217,12 @@ int HttpDownstreamConnection::push_request_headers() { auto connect_method = req.method == HTTP_CONNECT; + auto &httpconf = get_config()->http; + // For HTTP/1.0 request, there is no authority in request. In that // case, we use backend server's host nonetheless. auto authority = StringRef(downstream_hostport); - auto no_host_rewrite = get_config()->no_host_rewrite || + auto no_host_rewrite = httpconf.no_host_rewrite || get_config()->http2_proxy || get_config()->client_proxy || connect_method; @@ -296,11 +298,13 @@ int HttpDownstreamConnection::push_request_headers() { auto upstream = downstream_->get_upstream(); auto handler = upstream->get_client_handler(); - auto fwd = get_config()->strip_incoming_forwarded - ? nullptr - : req.fs.header(http2::HD_FORWARDED); - if (get_config()->forwarded_params) { - auto params = get_config()->forwarded_params; + auto &fwdconf = httpconf.forwarded; + + auto fwd = + fwdconf.strip_incoming ? nullptr : req.fs.header(http2::HD_FORWARDED); + + if (fwdconf.params) { + auto params = fwdconf.params; if (get_config()->http2_proxy || get_config()->client_proxy || connect_method) { @@ -328,11 +332,12 @@ int HttpDownstreamConnection::push_request_headers() { buf->append("\r\n"); } - auto xff = get_config()->strip_incoming_x_forwarded_for - ? nullptr - : req.fs.header(http2::HD_X_FORWARDED_FOR); + auto &xffconf = httpconf.xff; - if (get_config()->add_x_forwarded_for) { + auto xff = xffconf.strip_incoming ? nullptr + : req.fs.header(http2::HD_X_FORWARDED_FOR); + + if (xffconf.add) { buf->append("X-Forwarded-For: "); if (xff) { buf->append((*xff).value); @@ -353,7 +358,7 @@ int HttpDownstreamConnection::push_request_headers() { buf->append("\r\n"); } auto via = req.fs.header(http2::HD_VIA); - if (get_config()->no_via) { + if (httpconf.no_via) { if (via) { buf->append("Via: "); buf->append((*via).value); @@ -369,7 +374,7 @@ int HttpDownstreamConnection::push_request_headers() { buf->append("\r\n"); } - for (auto &p : get_config()->add_request_headers) { + for (auto &p : httpconf.add_request_headers) { buf->append(p.first); buf->append(": "); buf->append(p.second); diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index c4466fa5..cb8918ad 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -88,7 +88,7 @@ int htp_uricb(http_parser *htp, const char *data, size_t len) { // We happen to have the same value for method token. req.method = htp->method; - if (req.fs.buffer_size() + len > get_config()->header_field_buffer) { + if (req.fs.buffer_size() + len > get_config()->http.header_field_buffer) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large URI size=" << req.fs.buffer_size() + len; @@ -115,8 +115,9 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { auto upstream = static_cast(htp->data); auto downstream = upstream->get_downstream(); auto &req = downstream->request(); + auto &httpconf = get_config()->http; - if (req.fs.buffer_size() + len > get_config()->header_field_buffer) { + if (req.fs.buffer_size() + len > httpconf.header_field_buffer) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large header block size=" << req.fs.buffer_size() + len; @@ -130,7 +131,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { if (req.fs.header_key_prev()) { req.fs.append_last_header_key(data, len); } else { - if (req.fs.num_fields() >= get_config()->max_header_fields) { + if (req.fs.num_fields() >= httpconf.max_header_fields) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too many header field num=" << req.fs.num_fields() + 1; @@ -146,7 +147,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { if (req.fs.trailer_key_prev()) { req.fs.append_last_trailer_key(data, len); } else { - if (req.fs.num_fields() >= get_config()->max_header_fields) { + if (req.fs.num_fields() >= httpconf.max_header_fields) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too many header field num=" << req.fs.num_fields() + 1; @@ -166,7 +167,7 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { auto downstream = upstream->get_downstream(); auto &req = downstream->request(); - if (req.fs.buffer_size() + len > get_config()->header_field_buffer) { + if (req.fs.buffer_size() + len > get_config()->http.header_field_buffer) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Too large header block size=" << req.fs.buffer_size() + len; @@ -802,7 +803,7 @@ int HttpsUpstream::send_reply(Downstream *downstream, const uint8_t *body, if (!resp.fs.header(http2::HD_SERVER)) { output->append("Server: "); - output->append(get_config()->server_name); + output->append(get_config()->http.server_name); output->append("\r\n"); } @@ -839,8 +840,7 @@ void HttpsUpstream::error_reply(unsigned int status_code) { auto status_str = http2::get_status_string(status_code); output->append(status_str); output->append("\r\nServer: "); - const auto &server_name = get_config()->server_name; - output->append(server_name); + output->append(get_config()->http.server_name); output->append("\r\nContent-Length: "); auto cl = util::utos(html.size()); output->append(cl); @@ -927,8 +927,10 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { buf->append(http2::get_status_string(resp.http_status)); buf->append("\r\n"); + auto &httpconf = get_config()->http; + if (!get_config()->http2_proxy && !get_config()->client_proxy && - !get_config()->no_location_rewrite) { + !httpconf.no_location_rewrite) { downstream->rewrite_location_response_header( get_client_handler()->get_upstream_scheme()); } @@ -984,10 +986,10 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { if (!resp.fs.header(http2::HD_ALT_SVC)) { // We won't change or alter alt-svc from backend for now - if (!get_config()->altsvcs.empty()) { + if (!httpconf.altsvcs.empty()) { buf->append("Alt-Svc: "); - auto &altsvcs = get_config()->altsvcs; + auto &altsvcs = httpconf.altsvcs; write_altsvc(buf, altsvcs[0]); for (size_t i = 1; i < altsvcs.size(); ++i) { buf->append(", "); @@ -999,7 +1001,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { if (!get_config()->http2_proxy && !get_config()->client_proxy) { buf->append("Server: "); - buf->append(get_config()->server_name); + buf->append(httpconf.server_name); buf->append("\r\n"); } else { auto server = resp.fs.header(http2::HD_SERVER); @@ -1011,7 +1013,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { } auto via = resp.fs.header(http2::HD_VIA); - if (get_config()->no_via) { + if (httpconf.no_via) { if (via) { buf->append("Via: "); buf->append((*via).value); @@ -1028,7 +1030,7 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) { buf->append("\r\n"); } - for (auto &p : get_config()->add_response_headers) { + for (auto &p : httpconf.add_response_headers) { buf->append(p.first); buf->append(": "); buf->append(p.second); diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 2ed20c52..4d9cfa56 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -173,10 +173,12 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, header_buffer += strlen(nv[i]) + strlen(nv[i + 1]); } + auto &httpconf = get_config()->http; + // spdy does not define usage of trailer fields, and we ignores // them. - if (header_buffer > get_config()->header_field_buffer || - num_headers > get_config()->max_header_fields) { + if (header_buffer > httpconf.header_field_buffer || + num_headers > httpconf.max_header_fields) { upstream->rst_stream(downstream, SPDYLAY_INTERNAL_ERROR); return; } @@ -370,31 +372,33 @@ void on_data_chunk_recv_callback(spdylay_session *session, uint8_t flags, return; } + auto &http2conf = get_config()->http2; + // If connection-level window control is not enabled (e.g, // spdy/3), spdylay_session_get_recv_data_length() is always // returns 0. if (spdylay_session_get_recv_data_length(session) > std::max(SPDYLAY_INITIAL_WINDOW_SIZE, - 1 << get_config()->http2_upstream_connection_window_bits)) { + 1 << http2conf.upstream.connection_window_bits)) { if (LOG_ENABLED(INFO)) { - ULOG(INFO, upstream) - << "Flow control error on connection: " - << "recv_window_size=" - << spdylay_session_get_recv_data_length(session) << ", window_size=" - << (1 << get_config()->http2_upstream_connection_window_bits); + ULOG(INFO, upstream) << "Flow control error on connection: " + << "recv_window_size=" + << spdylay_session_get_recv_data_length(session) + << ", window_size=" + << (1 << http2conf.upstream.connection_window_bits); } spdylay_session_fail_session(session, SPDYLAY_GOAWAY_PROTOCOL_ERROR); return; } if (spdylay_session_get_stream_recv_data_length(session, stream_id) > std::max(SPDYLAY_INITIAL_WINDOW_SIZE, - 1 << get_config()->http2_upstream_window_bits)) { + 1 << http2conf.upstream.window_bits)) { if (LOG_ENABLED(INFO)) { ULOG(INFO, upstream) << "Flow control error: recv_window_size=" << spdylay_session_get_stream_recv_data_length( session, stream_id) << ", initial_window_size=" - << (1 << get_config()->http2_upstream_window_bits); + << (1 << http2conf.upstream.window_bits); } upstream->rst_stream(downstream, SPDYLAY_FLOW_CONTROL_ERROR); return; @@ -518,10 +522,12 @@ SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler) &max_buffer, sizeof(max_buffer)); assert(rv == 0); + auto &http2conf = get_config()->http2; + if (version >= SPDYLAY_PROTO_SPDY3) { int val = 1; flow_control_ = true; - initial_window_size_ = 1 << get_config()->http2_upstream_window_bits; + initial_window_size_ = 1 << http2conf.upstream.window_bits; rv = spdylay_session_set_option( session_, SPDYLAY_OPT_NO_AUTO_WINDOW_UPDATE2, &val, sizeof(val)); assert(rv == 0); @@ -532,7 +538,7 @@ SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler) // TODO Maybe call from outside? std::array entry; entry[0].settings_id = SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS; - entry[0].value = get_config()->http2_max_concurrent_streams; + entry[0].value = http2conf.max_concurrent_streams; entry[0].flags = SPDYLAY_ID_FLAG_SETTINGS_NONE; entry[1].settings_id = SPDYLAY_SETTINGS_INITIAL_WINDOW_SIZE; @@ -544,8 +550,8 @@ SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler) assert(rv == 0); if (version >= SPDYLAY_PROTO_SPDY3_1 && - get_config()->http2_upstream_connection_window_bits > 16) { - int32_t delta = (1 << get_config()->http2_upstream_connection_window_bits) - + http2conf.upstream.connection_window_bits > 16) { + int32_t delta = (1 << http2conf.upstream.connection_window_bits) - SPDYLAY_INITIAL_WINDOW_SIZE; rv = spdylay_submit_window_update(session_, 0, delta); assert(rv == 0); @@ -875,7 +881,7 @@ int SpdyUpstream::send_reply(Downstream *downstream, const uint8_t *body, if (!resp.fs.header(http2::HD_SERVER)) { nva.push_back("server"); - nva.push_back(get_config()->server_name.c_str()); + nva.push_back(get_config()->http.server_name.c_str()); } nva.push_back(nullptr); @@ -919,7 +925,7 @@ int SpdyUpstream::error_reply(Downstream *downstream, std::string status_string = http2::get_status_string(status_code); const char *nv[] = {":status", status_string.c_str(), ":version", "http/1.1", "content-type", "text/html; charset=UTF-8", "server", - get_config()->server_name.c_str(), "content-length", + get_config()->http.server_name.c_str(), "content-length", content_length.c_str(), "date", lgconf->time_http_str.c_str(), nullptr}; @@ -997,15 +1003,17 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { DLOG(INFO, downstream) << "HTTP response header completed"; } + auto &httpconf = get_config()->http; + if (!get_config()->http2_proxy && !get_config()->client_proxy && - !get_config()->no_location_rewrite) { + !httpconf.no_location_rewrite) { downstream->rewrite_location_response_header(req.scheme); } // 8 means server, :status, :version and possible via header field. - auto nv = make_unique( - resp.fs.headers().size() * 2 + 8 + - get_config()->add_response_headers.size() * 2 + 1); + auto nv = + make_unique(resp.fs.headers().size() * 2 + 8 + + httpconf.add_response_headers.size() * 2 + 1); size_t hdidx = 0; std::string via_value; @@ -1034,7 +1042,7 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { if (!get_config()->http2_proxy && !get_config()->client_proxy) { nv[hdidx++] = "server"; - nv[hdidx++] = get_config()->server_name.c_str(); + nv[hdidx++] = httpconf.server_name.c_str(); } else { auto server = resp.fs.header(http2::HD_SERVER); if (server) { @@ -1044,7 +1052,7 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { } auto via = resp.fs.header(http2::HD_VIA); - if (get_config()->no_via) { + if (httpconf.no_via) { if (via) { nv[hdidx++] = "via"; nv[hdidx++] = via->value.c_str(); @@ -1060,7 +1068,7 @@ int SpdyUpstream::on_downstream_header_complete(Downstream *downstream) { nv[hdidx++] = via_value.c_str(); } - for (auto &p : get_config()->add_response_headers) { + for (auto &p : httpconf.add_response_headers) { nv[hdidx++] = p.first.c_str(); nv[hdidx++] = p.second.c_str(); } diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index 84e6d9c0..65103c92 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -91,7 +91,7 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, } if (get_config()->downstream_proto == PROTO_HTTP2) { - auto n = get_config()->http2_downstream_connections_per_worker; + auto n = get_config()->http2.downstream.connections_per_worker; size_t group = 0; for (auto &dgrp : dgrps_) { auto m = n; From a053d10839894a8d4e88295f12298d98439096e6 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 18 Jan 2016 17:08:28 +0900 Subject: [PATCH 3/7] nghttpx: Move option handling code to separate function --- src/shrpx.cc | 672 ++++++++++++++++++++++++++------------------------- 1 file changed, 340 insertions(+), 332 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index 5986ee89..2dbfc0e2 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1723,6 +1723,345 @@ Misc: } } // namespace +namespace { +void process_options( + int argc, char **argv, + std::vector> &cmdcfgs) { + if (conf_exists(get_config()->conf_path.get())) { + std::set include_set; + if (load_config(get_config()->conf_path.get(), include_set) == -1) { + LOG(FATAL) << "Failed to load configuration from " + << get_config()->conf_path.get(); + exit(EXIT_FAILURE); + } + assert(include_set.empty()); + } + + if (argc - optind >= 2) { + cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, argv[optind++]); + cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, argv[optind++]); + } + + // Reopen log files using configurations in file + reopen_log_files(); + + { + std::set include_set; + + for (size_t i = 0, len = cmdcfgs.size(); i < len; ++i) { + if (parse_config(cmdcfgs[i].first, cmdcfgs[i].second, include_set) == + -1) { + LOG(FATAL) << "Failed to parse command-line argument."; + exit(EXIT_FAILURE); + } + } + + assert(include_set.empty()); + } + + if (get_config()->accesslog_syslog || get_config()->errorlog_syslog) { + openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID, + get_config()->syslog_facility); + } + + if (reopen_log_files() != 0) { + LOG(FATAL) << "Failed to open log file"; + exit(EXIT_FAILURE); + } + + redirect_stderr_to_errorlog(); + + if (get_config()->uid != 0) { + if (log_config()->accesslog_fd != -1 && + fchown(log_config()->accesslog_fd, get_config()->uid, + get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of access log file failed: " + << strerror(error); + } + if (log_config()->errorlog_fd != -1 && + fchown(log_config()->errorlog_fd, get_config()->uid, + get_config()->gid) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of error log file failed: " + << strerror(error); + } + } + + auto &http2conf = mod_config()->http2; + { + auto &dumpconf = http2conf.upstream.debug.dump; + + if (dumpconf.request_header_file) { + auto path = dumpconf.request_header_file.get(); + auto f = open_file_for_write(path); + + if (f == nullptr) { + LOG(FATAL) << "Failed to open http2 upstream request header file: " + << path; + exit(EXIT_FAILURE); + } + + dumpconf.request_header = f; + + if (get_config()->uid != 0) { + if (chown_to_running_user(path) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of http2 upstream request header file " + << path << " failed: " << strerror(error); + } + } + } + + if (dumpconf.response_header_file) { + auto path = dumpconf.response_header_file.get(); + auto f = open_file_for_write(path); + + if (f == nullptr) { + LOG(FATAL) << "Failed to open http2 upstream response header file: " + << path; + exit(EXIT_FAILURE); + } + + dumpconf.response_header = f; + + if (get_config()->uid != 0) { + if (chown_to_running_user(path) == -1) { + auto error = errno; + LOG(WARN) << "Changing owner of http2 upstream response header file" + << " " << path << " failed: " << strerror(error); + } + } + } + } + + auto &tlsconf = mod_config()->tls; + + if (tlsconf.npn_list.empty()) { + tlsconf.npn_list = util::parse_config_str_list(DEFAULT_NPN_LIST); + } + if (tlsconf.tls_proto_list.empty()) { + tlsconf.tls_proto_list = + util::parse_config_str_list(DEFAULT_TLS_PROTO_LIST); + } + + tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list); + + tlsconf.alpn_prefs = ssl::set_alpn_prefs(tlsconf.npn_list); + + if (get_config()->backend_ipv4 && get_config()->backend_ipv6) { + LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the " + << "same time."; + exit(EXIT_FAILURE); + } + + if (get_config()->worker_frontend_connections == 0) { + mod_config()->worker_frontend_connections = + std::numeric_limits::max(); + } + + if (get_config()->http2_proxy + get_config()->http2_bridge + + get_config()->client_proxy + get_config()->client > + 1) { + LOG(FATAL) << "--http2-proxy, --http2-bridge, --client-proxy and --client " + << "cannot be used at the same time."; + exit(EXIT_FAILURE); + } + + if (get_config()->client || get_config()->client_proxy) { + mod_config()->client_mode = true; + mod_config()->upstream_no_tls = true; + } + + if (get_config()->client_mode || get_config()->http2_bridge) { + mod_config()->downstream_proto = PROTO_HTTP2; + } else { + mod_config()->downstream_proto = PROTO_HTTP; + } + + if (!get_config()->upstream_no_tls && + (!tlsconf.private_key_file || !tlsconf.cert_file)) { + print_usage(std::cerr); + LOG(FATAL) << "Too few arguments"; + exit(EXIT_FAILURE); + } + + if (!get_config()->upstream_no_tls && !tlsconf.ocsp.disabled) { + struct stat buf; + if (stat(tlsconf.ocsp.fetch_ocsp_response_file.get(), &buf) != 0) { + tlsconf.ocsp.disabled = true; + LOG(WARN) << "--fetch-ocsp-response-file: " + << tlsconf.ocsp.fetch_ocsp_response_file.get() + << " not found. OCSP stapling has been disabled."; + } + } + + if (get_config()->downstream_addr_groups.empty()) { + DownstreamAddr addr; + addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST); + addr.port = DEFAULT_DOWNSTREAM_PORT; + + DownstreamAddrGroup g("/"); + g.addrs.push_back(std::move(addr)); + mod_config()->router.add_route(g.pattern.get(), 1, + get_config()->downstream_addr_groups.size()); + mod_config()->downstream_addr_groups.push_back(std::move(g)); + } else if (get_config()->http2_proxy || get_config()->client_proxy) { + // We don't support host mapping in these cases. Move all + // non-catch-all patterns to catch-all pattern. + DownstreamAddrGroup catch_all("/"); + for (auto &g : mod_config()->downstream_addr_groups) { + std::move(std::begin(g.addrs), std::end(g.addrs), + std::back_inserter(catch_all.addrs)); + } + std::vector().swap( + mod_config()->downstream_addr_groups); + // maybe not necessary? + mod_config()->router = Router(); + mod_config()->router.add_route(catch_all.pattern.get(), 1, + get_config()->downstream_addr_groups.size()); + mod_config()->downstream_addr_groups.push_back(std::move(catch_all)); + } + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Resolving backend address"; + } + + ssize_t catch_all_group = -1; + for (size_t i = 0; i < mod_config()->downstream_addr_groups.size(); ++i) { + auto &g = mod_config()->downstream_addr_groups[i]; + if (util::streq(g.pattern.get(), "/")) { + catch_all_group = i; + } + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern.get() + << "'"; + for (auto &addr : g.addrs) { + LOG(INFO) << "group " << i << " -> " << addr.host.c_str() + << (addr.host_unix ? "" : ":" + util::utos(addr.port)); + } + } + } + + if (catch_all_group == -1) { + LOG(FATAL) << "-b: No catch-all backend address is configured"; + exit(EXIT_FAILURE); + } + mod_config()->downstream_addr_group_catch_all = catch_all_group; + + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Catch-all pattern is group " << catch_all_group; + } + + for (auto &g : mod_config()->downstream_addr_groups) { + for (auto &addr : g.addrs) { + + if (addr.host_unix) { + // for AF_UNIX socket, we use "localhost" as host for backend + // hostport. This is used as Host header field to backend and + // not going to be passed to any syscalls. + addr.hostport = ImmutableString( + util::make_hostport("localhost", get_config()->port)); + + auto path = addr.host.c_str(); + auto pathlen = addr.host.size(); + + if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) { + LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " + << sizeof(addr.addr.su.un.sun_path); + exit(EXIT_FAILURE); + } + + LOG(INFO) << "Use UNIX domain socket path " << path + << " for backend connection"; + + addr.addr.su.un.sun_family = AF_UNIX; + // copy path including terminal NULL + std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path); + addr.addr.len = sizeof(addr.addr.su.un); + + continue; + } + + addr.hostport = + ImmutableString(util::make_hostport(addr.host.c_str(), addr.port)); + + if (resolve_hostname( + &addr.addr, addr.host.c_str(), addr.port, + get_config()->backend_ipv4 ? AF_INET : (get_config()->backend_ipv6 + ? AF_INET6 + : AF_UNSPEC)) == -1) { + exit(EXIT_FAILURE); + } + } + } + + auto &proxy = mod_config()->downstream_http_proxy; + if (!proxy.host.empty()) { + if (LOG_ENABLED(INFO)) { + LOG(INFO) << "Resolving backend http proxy address"; + } + if (resolve_hostname(&proxy.addr, proxy.host.c_str(), proxy.port, + AF_UNSPEC) == -1) { + exit(EXIT_FAILURE); + } + } + + { + auto &memcachedconf = tlsconf.session_cache.memcached; + if (memcachedconf.host) { + if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.get(), + memcachedconf.port, AF_UNSPEC) == -1) { + exit(EXIT_FAILURE); + } + } + } + + { + auto &memcachedconf = tlsconf.ticket.memcached; + if (memcachedconf.host) { + if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.get(), + memcachedconf.port, AF_UNSPEC) == -1) { + exit(EXIT_FAILURE); + } + } + } + + if (get_config()->rlimit_nofile) { + struct rlimit lim = {static_cast(get_config()->rlimit_nofile), + static_cast(get_config()->rlimit_nofile)}; + if (setrlimit(RLIMIT_NOFILE, &lim) != 0) { + auto error = errno; + LOG(WARN) << "Setting rlimit-nofile failed: " << strerror(error); + } + } + + auto &fwdconf = mod_config()->http.forwarded; + + if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED && + fwdconf.by_obfuscated.empty()) { + std::random_device rd; + std::mt19937 gen(rd()); + auto &dst = fwdconf.by_obfuscated; + dst = "_"; + dst += util::random_alpha_digit(gen, SHRPX_OBFUSCATED_NODE_LENGTH); + } + + if (get_config()->http2.upstream.debug.frame_debug) { + // To make it sync to logging + set_output(stderr); + if (isatty(fileno(stdout))) { + set_color_output(true); + } + reset_timer(); + } + + mod_config()->http2.upstream.callbacks = create_http2_upstream_callbacks(); + mod_config()->http2.downstream.callbacks = + create_http2_downstream_callbacks(); +} +} // namespace + int main(int argc, char **argv) { nghttp2::ssl::libssl_init(); @@ -2321,338 +2660,7 @@ int main(int argc, char **argv) { } } - if (conf_exists(get_config()->conf_path.get())) { - std::set include_set; - if (load_config(get_config()->conf_path.get(), include_set) == -1) { - LOG(FATAL) << "Failed to load configuration from " - << get_config()->conf_path.get(); - exit(EXIT_FAILURE); - } - assert(include_set.empty()); - } - - if (argc - optind >= 2) { - cmdcfgs.emplace_back(SHRPX_OPT_PRIVATE_KEY_FILE, argv[optind++]); - cmdcfgs.emplace_back(SHRPX_OPT_CERTIFICATE_FILE, argv[optind++]); - } - - // Reopen log files using configurations in file - reopen_log_files(); - - { - std::set include_set; - - for (size_t i = 0, len = cmdcfgs.size(); i < len; ++i) { - if (parse_config(cmdcfgs[i].first, cmdcfgs[i].second, include_set) == - -1) { - LOG(FATAL) << "Failed to parse command-line argument."; - exit(EXIT_FAILURE); - } - } - - assert(include_set.empty()); - } - - if (get_config()->accesslog_syslog || get_config()->errorlog_syslog) { - openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID, - get_config()->syslog_facility); - } - - if (reopen_log_files() != 0) { - LOG(FATAL) << "Failed to open log file"; - exit(EXIT_FAILURE); - } - - redirect_stderr_to_errorlog(); - - if (get_config()->uid != 0) { - if (log_config()->accesslog_fd != -1 && - fchown(log_config()->accesslog_fd, get_config()->uid, - get_config()->gid) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of access log file failed: " - << strerror(error); - } - if (log_config()->errorlog_fd != -1 && - fchown(log_config()->errorlog_fd, get_config()->uid, - get_config()->gid) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of error log file failed: " - << strerror(error); - } - } - - auto &http2conf = mod_config()->http2; - { - auto &dumpconf = http2conf.upstream.debug.dump; - - if (dumpconf.request_header_file) { - auto path = dumpconf.request_header_file.get(); - auto f = open_file_for_write(path); - - if (f == nullptr) { - LOG(FATAL) << "Failed to open http2 upstream request header file: " - << path; - exit(EXIT_FAILURE); - } - - dumpconf.request_header = f; - - if (get_config()->uid != 0) { - if (chown_to_running_user(path) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of http2 upstream request header file " - << path << " failed: " << strerror(error); - } - } - } - - if (dumpconf.response_header_file) { - auto path = dumpconf.response_header_file.get(); - auto f = open_file_for_write(path); - - if (f == nullptr) { - LOG(FATAL) << "Failed to open http2 upstream response header file: " - << path; - exit(EXIT_FAILURE); - } - - dumpconf.response_header = f; - - if (get_config()->uid != 0) { - if (chown_to_running_user(path) == -1) { - auto error = errno; - LOG(WARN) << "Changing owner of http2 upstream response header file" - << " " << path << " failed: " << strerror(error); - } - } - } - } - - auto &tlsconf = mod_config()->tls; - - if (tlsconf.npn_list.empty()) { - tlsconf.npn_list = util::parse_config_str_list(DEFAULT_NPN_LIST); - } - if (tlsconf.tls_proto_list.empty()) { - tlsconf.tls_proto_list = - util::parse_config_str_list(DEFAULT_TLS_PROTO_LIST); - } - - tlsconf.tls_proto_mask = ssl::create_tls_proto_mask(tlsconf.tls_proto_list); - - tlsconf.alpn_prefs = ssl::set_alpn_prefs(tlsconf.npn_list); - - if (get_config()->backend_ipv4 && get_config()->backend_ipv6) { - LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the " - << "same time."; - exit(EXIT_FAILURE); - } - - if (get_config()->worker_frontend_connections == 0) { - mod_config()->worker_frontend_connections = - std::numeric_limits::max(); - } - - if (get_config()->http2_proxy + get_config()->http2_bridge + - get_config()->client_proxy + get_config()->client > - 1) { - LOG(FATAL) << "--http2-proxy, --http2-bridge, --client-proxy and --client " - << "cannot be used at the same time."; - exit(EXIT_FAILURE); - } - - if (get_config()->client || get_config()->client_proxy) { - mod_config()->client_mode = true; - mod_config()->upstream_no_tls = true; - } - - if (get_config()->client_mode || get_config()->http2_bridge) { - mod_config()->downstream_proto = PROTO_HTTP2; - } else { - mod_config()->downstream_proto = PROTO_HTTP; - } - - if (!get_config()->upstream_no_tls && - (!tlsconf.private_key_file || !tlsconf.cert_file)) { - print_usage(std::cerr); - LOG(FATAL) << "Too few arguments"; - exit(EXIT_FAILURE); - } - - if (!get_config()->upstream_no_tls && !tlsconf.ocsp.disabled) { - struct stat buf; - if (stat(tlsconf.ocsp.fetch_ocsp_response_file.get(), &buf) != 0) { - tlsconf.ocsp.disabled = true; - LOG(WARN) << "--fetch-ocsp-response-file: " - << tlsconf.ocsp.fetch_ocsp_response_file.get() - << " not found. OCSP stapling has been disabled."; - } - } - - if (get_config()->downstream_addr_groups.empty()) { - DownstreamAddr addr; - addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST); - addr.port = DEFAULT_DOWNSTREAM_PORT; - - DownstreamAddrGroup g("/"); - g.addrs.push_back(std::move(addr)); - mod_config()->router.add_route(g.pattern.get(), 1, - get_config()->downstream_addr_groups.size()); - mod_config()->downstream_addr_groups.push_back(std::move(g)); - } else if (get_config()->http2_proxy || get_config()->client_proxy) { - // We don't support host mapping in these cases. Move all - // non-catch-all patterns to catch-all pattern. - DownstreamAddrGroup catch_all("/"); - for (auto &g : mod_config()->downstream_addr_groups) { - std::move(std::begin(g.addrs), std::end(g.addrs), - std::back_inserter(catch_all.addrs)); - } - std::vector().swap( - mod_config()->downstream_addr_groups); - // maybe not necessary? - mod_config()->router = Router(); - mod_config()->router.add_route(catch_all.pattern.get(), 1, - get_config()->downstream_addr_groups.size()); - mod_config()->downstream_addr_groups.push_back(std::move(catch_all)); - } - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Resolving backend address"; - } - - ssize_t catch_all_group = -1; - for (size_t i = 0; i < mod_config()->downstream_addr_groups.size(); ++i) { - auto &g = mod_config()->downstream_addr_groups[i]; - if (util::streq(g.pattern.get(), "/")) { - catch_all_group = i; - } - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Host-path pattern: group " << i << ": '" << g.pattern.get() - << "'"; - for (auto &addr : g.addrs) { - LOG(INFO) << "group " << i << " -> " << addr.host.c_str() - << (addr.host_unix ? "" : ":" + util::utos(addr.port)); - } - } - } - - if (catch_all_group == -1) { - LOG(FATAL) << "-b: No catch-all backend address is configured"; - exit(EXIT_FAILURE); - } - mod_config()->downstream_addr_group_catch_all = catch_all_group; - - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Catch-all pattern is group " << catch_all_group; - } - - for (auto &g : mod_config()->downstream_addr_groups) { - for (auto &addr : g.addrs) { - - if (addr.host_unix) { - // for AF_UNIX socket, we use "localhost" as host for backend - // hostport. This is used as Host header field to backend and - // not going to be passed to any syscalls. - addr.hostport = ImmutableString( - util::make_hostport("localhost", get_config()->port)); - - auto path = addr.host.c_str(); - auto pathlen = addr.host.size(); - - if (pathlen + 1 > sizeof(addr.addr.su.un.sun_path)) { - LOG(FATAL) << "UNIX domain socket path " << path << " is too long > " - << sizeof(addr.addr.su.un.sun_path); - exit(EXIT_FAILURE); - } - - LOG(INFO) << "Use UNIX domain socket path " << path - << " for backend connection"; - - addr.addr.su.un.sun_family = AF_UNIX; - // copy path including terminal NULL - std::copy_n(path, pathlen + 1, addr.addr.su.un.sun_path); - addr.addr.len = sizeof(addr.addr.su.un); - - continue; - } - - addr.hostport = - ImmutableString(util::make_hostport(addr.host.c_str(), addr.port)); - - if (resolve_hostname( - &addr.addr, addr.host.c_str(), addr.port, - get_config()->backend_ipv4 ? AF_INET : (get_config()->backend_ipv6 - ? AF_INET6 - : AF_UNSPEC)) == -1) { - exit(EXIT_FAILURE); - } - } - } - - auto &proxy = mod_config()->downstream_http_proxy; - if (!proxy.host.empty()) { - if (LOG_ENABLED(INFO)) { - LOG(INFO) << "Resolving backend http proxy address"; - } - if (resolve_hostname(&proxy.addr, proxy.host.c_str(), proxy.port, - AF_UNSPEC) == -1) { - exit(EXIT_FAILURE); - } - } - - { - auto &memcachedconf = tlsconf.session_cache.memcached; - if (memcachedconf.host) { - if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.get(), - memcachedconf.port, AF_UNSPEC) == -1) { - exit(EXIT_FAILURE); - } - } - } - - { - auto &memcachedconf = tlsconf.ticket.memcached; - if (memcachedconf.host) { - if (resolve_hostname(&memcachedconf.addr, memcachedconf.host.get(), - memcachedconf.port, AF_UNSPEC) == -1) { - exit(EXIT_FAILURE); - } - } - } - - if (get_config()->rlimit_nofile) { - struct rlimit lim = {static_cast(get_config()->rlimit_nofile), - static_cast(get_config()->rlimit_nofile)}; - if (setrlimit(RLIMIT_NOFILE, &lim) != 0) { - auto error = errno; - LOG(WARN) << "Setting rlimit-nofile failed: " << strerror(error); - } - } - - auto &fwdconf = mod_config()->http.forwarded; - - if (fwdconf.by_node_type == FORWARDED_NODE_OBFUSCATED && - fwdconf.by_obfuscated.empty()) { - std::random_device rd; - std::mt19937 gen(rd()); - auto &dst = fwdconf.by_obfuscated; - dst = "_"; - dst += util::random_alpha_digit(gen, SHRPX_OBFUSCATED_NODE_LENGTH); - } - - if (get_config()->http2.upstream.debug.frame_debug) { - // To make it sync to logging - set_output(stderr); - if (isatty(fileno(stdout))) { - set_color_output(true); - } - reset_timer(); - } - - mod_config()->http2.upstream.callbacks = create_http2_upstream_callbacks(); - mod_config()->http2.downstream.callbacks = - create_http2_downstream_callbacks(); + process_options(argc, argv, cmdcfgs); if (event_loop() != 0) { return -1; From 35feae3b0c10897443d058d44be5b4ee851ee838 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Mon, 18 Jan 2016 17:26:27 +0900 Subject: [PATCH 4/7] nghttpx: Group up logging related options --- src/shrpx.cc | 31 ++++++++++++++++++++----------- src/shrpx_client_handler.cc | 4 ++-- src/shrpx_config.cc | 12 ++++++------ src/shrpx_config.h | 24 ++++++++++++++++-------- src/shrpx_log.cc | 36 +++++++++++++++++++----------------- 5 files changed, 63 insertions(+), 44 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index 2dbfc0e2..b8c391a2 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -929,13 +929,7 @@ void fill_default_config() { mod_config()->downstream_no_tls = false; mod_config()->num_worker = 1; - mod_config()->accesslog_file = nullptr; - mod_config()->accesslog_syslog = false; - mod_config()->accesslog_format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT); - mod_config()->errorlog_file = strcopy("/dev/stderr"); - mod_config()->errorlog_syslog = false; mod_config()->conf_path = strcopy("/etc/nghttpx/nghttpx.conf"); - mod_config()->syslog_facility = LOG_DAEMON; // Default accept() backlog mod_config()->backlog = 512; mod_config()->http2_proxy = false; @@ -1047,6 +1041,19 @@ void fill_default_config() { http2conf.max_concurrent_streams = 100; http2conf.no_cookie_crumbling = false; http2conf.no_server_push = false; + + auto &loggingconf = mod_config()->logging; + { + auto &accessconf = loggingconf.access; + accessconf.syslog = false; + accessconf.format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT); + + auto &errorconf = loggingconf.error; + errorconf.file = strcopy("/dev/stderr"); + errorconf.syslog = false; + } + + loggingconf.syslog_facility = LOG_DAEMON; } } // namespace @@ -1573,14 +1580,14 @@ Logging: Set path to write error log. To reopen file, send USR1 signal to nghttpx. stderr will be redirected to the error log file unless --errorlog-syslog is used. - Default: )" << get_config()->errorlog_file.get() << R"( + Default: )" << get_config()->logging.error.file.get() << R"( --errorlog-syslog Send error log to syslog. If this option is used, --errorlog-file option is ignored. --syslog-facility= Set syslog facility to . - Default: )" << str_syslog_facility(get_config()->syslog_facility) - << R"( + Default: )" + << str_syslog_facility(get_config()->logging.syslog_facility) << R"( HTTP: --add-x-forwarded-for @@ -1759,9 +1766,11 @@ void process_options( assert(include_set.empty()); } - if (get_config()->accesslog_syslog || get_config()->errorlog_syslog) { + auto &loggingconf = get_config()->logging; + + if (loggingconf.access.syslog || loggingconf.error.syslog) { openlog("nghttpx", LOG_NDELAY | LOG_NOWAIT | LOG_PID, - get_config()->syslog_facility); + loggingconf.syslog_facility); } if (reopen_log_files() != 0) { diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index 1313c9b1..1a8822de 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -836,7 +836,7 @@ void ClientHandler::write_accesslog(Downstream *downstream) { const auto &resp = downstream->response(); upstream_accesslog( - get_config()->accesslog_format, + get_config()->logging.access.format, LogSpec{ downstream, ipaddr_, http2::to_method_string(req.method), @@ -869,7 +869,7 @@ void ClientHandler::write_accesslog(int major, int minor, unsigned int status, nghttp2::ssl::TLSSessionInfo tls_info; upstream_accesslog( - get_config()->accesslog_format, + get_config()->logging.access.format, LogSpec{ nullptr, ipaddr_, StringRef::from_lit("-"), // method diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 3bda4f42..47e8a80c 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -1499,23 +1499,23 @@ int parse_config(const char *opt, const char *optarg, case SHRPX_OPTID_STREAM_WRITE_TIMEOUT: return parse_duration(&mod_config()->stream_write_timeout, opt, optarg); case SHRPX_OPTID_ACCESSLOG_FILE: - mod_config()->accesslog_file = strcopy(optarg); + mod_config()->logging.access.file = strcopy(optarg); return 0; case SHRPX_OPTID_ACCESSLOG_SYSLOG: - mod_config()->accesslog_syslog = util::strieq(optarg, "yes"); + mod_config()->logging.access.syslog = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_ACCESSLOG_FORMAT: - mod_config()->accesslog_format = parse_log_format(optarg); + mod_config()->logging.access.format = parse_log_format(optarg); return 0; case SHRPX_OPTID_ERRORLOG_FILE: - mod_config()->errorlog_file = strcopy(optarg); + mod_config()->logging.error.file = strcopy(optarg); return 0; case SHRPX_OPTID_ERRORLOG_SYSLOG: - mod_config()->errorlog_syslog = util::strieq(optarg, "yes"); + mod_config()->logging.error.syslog = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_FASTOPEN: { @@ -1660,7 +1660,7 @@ int parse_config(const char *opt, const char *optarg, LOG(ERROR) << opt << ": Unknown syslog facility: " << optarg; return -1; } - mod_config()->syslog_facility = facility; + mod_config()->logging.syslog_facility = facility; return 0; } diff --git a/src/shrpx_config.h b/src/shrpx_config.h index b94751b3..4620227b 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -434,14 +434,29 @@ struct Http2Config { bool no_server_push; }; +struct LoggingConfig { + struct { + std::vector format; + std::unique_ptr file; + // Send accesslog to syslog, ignoring accesslog_file. + bool syslog; + } access; + struct { + std::unique_ptr file; + // Send errorlog to syslog, ignoring errorlog_file. + bool syslog; + } error; + int syslog_facility; +}; + struct Config { - std::vector accesslog_format; std::vector downstream_addr_groups; Router router; HttpProxy downstream_http_proxy; HttpConfig http; Http2Config http2; TLSConfig tls; + LoggingConfig logging; ev_tstamp http2_upstream_read_timeout; ev_tstamp upstream_read_timeout; ev_tstamp upstream_write_timeout; @@ -456,8 +471,6 @@ struct Config { std::unique_ptr host; std::unique_ptr pid_file; std::unique_ptr conf_path; - std::unique_ptr accesslog_file; - std::unique_ptr errorlog_file; std::unique_ptr user; std::unique_ptr mruby_file; char **original_argv; @@ -483,7 +496,6 @@ struct Config { size_t downstream_addr_group_catch_all; // downstream protocol; this will be determined by given options. shrpx_proto downstream_proto; - int syslog_facility; int backlog; int argc; int fastopen; @@ -500,10 +512,6 @@ struct Config { bool client_proxy; bool upstream_no_tls; bool downstream_no_tls; - // Send accesslog to syslog, ignoring accesslog_file. - bool accesslog_syslog; - // Send errorlog to syslog, ignoring errorlog_file. - bool errorlog_syslog; bool client; // true if --client or --client-proxy are enabled. bool client_mode; diff --git a/src/shrpx_log.cc b/src/shrpx_log.cc index aacde3f7..d5e0fdc4 100644 --- a/src/shrpx_log.cc +++ b/src/shrpx_log.cc @@ -109,12 +109,14 @@ Log::~Log() { auto lgconf = log_config(); + auto &errorconf = get_config()->logging.error; + if (!log_enabled(severity_) || - (lgconf->errorlog_fd == -1 && !get_config()->errorlog_syslog)) { + (lgconf->errorlog_fd == -1 && !errorconf.syslog)) { return; } - if (get_config()->errorlog_syslog) { + if (errorconf.syslog) { if (severity_ == NOTICE) { syslog(severity_to_syslog_level(severity_), "[%s] %s", SEVERITY_STR[severity_], stream_.str().c_str()); @@ -219,8 +221,9 @@ std::pair copy_hex_low(const uint8_t *src, void upstream_accesslog(const std::vector &lfv, const LogSpec &lgsp) { auto lgconf = log_config(); + auto &accessconf = get_config()->logging.access; - if (lgconf->accesslog_fd == -1 && !get_config()->accesslog_syslog) { + if (lgconf->accesslog_fd == -1 && !accessconf.syslog) { return; } @@ -360,7 +363,7 @@ void upstream_accesslog(const std::vector &lfv, *p = '\0'; - if (get_config()->accesslog_syslog) { + if (accessconf.syslog) { syslog(LOG_INFO, "%s", buf); return; @@ -379,29 +382,27 @@ int reopen_log_files() { int new_errorlog_fd = -1; auto lgconf = log_config(); + auto &accessconf = get_config()->logging.access; + auto &errorconf = get_config()->logging.error; - if (!get_config()->accesslog_syslog && get_config()->accesslog_file) { - - new_accesslog_fd = util::open_log_file(get_config()->accesslog_file.get()); + if (!accessconf.syslog && accessconf.file) { + new_accesslog_fd = util::open_log_file(accessconf.file.get()); if (new_accesslog_fd == -1) { - LOG(ERROR) << "Failed to open accesslog file " - << get_config()->accesslog_file.get(); + LOG(ERROR) << "Failed to open accesslog file " << accessconf.file.get(); res = -1; } } - if (!get_config()->errorlog_syslog && get_config()->errorlog_file) { - - new_errorlog_fd = util::open_log_file(get_config()->errorlog_file.get()); + if (!errorconf.syslog && errorconf.file) { + new_errorlog_fd = util::open_log_file(errorconf.file.get()); if (new_errorlog_fd == -1) { if (lgconf->errorlog_fd != -1) { - LOG(ERROR) << "Failed to open errorlog file " - << get_config()->errorlog_file.get(); + LOG(ERROR) << "Failed to open errorlog file " << errorconf.file.get(); } else { - std::cerr << "Failed to open errorlog file " - << get_config()->errorlog_file.get() << std::endl; + std::cerr << "Failed to open errorlog file " << errorconf.file.get() + << std::endl; } res = -1; @@ -444,8 +445,9 @@ void log_chld(pid_t pid, int rstatus, const char *msg) { void redirect_stderr_to_errorlog() { auto lgconf = log_config(); + auto &errorconf = get_config()->logging.error; - if (get_config()->errorlog_syslog || lgconf->errorlog_fd == -1) { + if (errorconf.syslog || lgconf->errorlog_fd == -1) { return; } From 0402481be40876532ea63b164db60229d9154ac8 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 19 Jan 2016 16:56:12 +0900 Subject: [PATCH 5/7] nghttpx: Organize connection related configuration into struct --- src/shrpx.cc | 277 +++++++++++++---------- src/shrpx_client_handler.cc | 32 +-- src/shrpx_config.cc | 95 ++++---- src/shrpx_config.h | 110 +++++---- src/shrpx_connection.cc | 14 +- src/shrpx_connection.h | 8 +- src/shrpx_connection_handler.cc | 6 +- src/shrpx_downstream.cc | 43 ++-- src/shrpx_http.cc | 2 +- src/shrpx_http2_downstream_connection.cc | 2 +- src/shrpx_http2_session.cc | 14 +- src/shrpx_http2_upstream.cc | 8 +- src/shrpx_http_downstream_connection.cc | 21 +- src/shrpx_memcached_connection.cc | 2 +- src/shrpx_spdy_upstream.cc | 8 +- src/shrpx_ssl.cc | 11 +- src/shrpx_worker.cc | 22 +- src/shrpx_worker_process.cc | 6 +- 18 files changed, 389 insertions(+), 292 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index b8c391a2..f1abba0d 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -299,13 +299,15 @@ void exec_binary(SignalServer *ssv) { std::string fd, fd6, path, port; - if (get_config()->host_unix) { + auto &listenerconf = get_config()->conn.listener; + + if (listenerconf.host_unix) { fd = ENV_UNIX_FD "="; fd += util::utos(ssv->server_fd); envp[envidx++] = &fd[0]; path = ENV_UNIX_PATH "="; - path += get_config()->host.get(); + path += listenerconf.host.get(); envp[envidx++] = &path[0]; } else { if (ssv->server_fd) { @@ -321,7 +323,7 @@ void exec_binary(SignalServer *ssv) { } port = ENV_PORT "="; - port += util::utos(get_config()->port); + port += util::utos(listenerconf.port); envp[envidx++] = &port[0]; } @@ -430,7 +432,9 @@ void worker_process_child_cb(struct ev_loop *loop, ev_child *w, int revents) { namespace { int create_unix_domain_server_socket() { - auto path = get_config()->host.get(); + auto &listenerconf = get_config()->conn.listener; + + auto path = listenerconf.host.get(); auto pathlen = strlen(path); { auto envfd = getenv(ENV_UNIX_FD); @@ -490,7 +494,7 @@ int create_unix_domain_server_socket() { return -1; } - if (listen(fd, get_config()->backlog) != 0) { + if (listen(fd, listenerconf.backlog) != 0) { auto error = errno; LOG(FATAL) << "Failed to listen to UNIX domain socket, error=" << error; close(fd); @@ -505,6 +509,8 @@ int create_unix_domain_server_socket() { namespace { int create_tcp_server_socket(int family) { + auto &listenerconf = get_config()->conn.listener; + { auto envfd = getenv(family == AF_INET ? ENV_LISTENER4_FD : ENV_LISTENER6_FD); @@ -517,14 +523,14 @@ int create_tcp_server_socket(int family) { // Only do this iff NGHTTPX_PORT == get_config()->port. // Otherwise, close fd, and create server socket as usual. - if (port == get_config()->port) { - LOG(NOTICE) << "Listening on port " << get_config()->port; + if (port == listenerconf.port) { + LOG(NOTICE) << "Listening on port " << listenerconf.port; return fd; } LOG(WARN) << "Port was changed between old binary (" << port - << ") and new binary (" << get_config()->port << ")"; + << ") and new binary (" << listenerconf.port << ")"; close(fd); } } @@ -532,7 +538,7 @@ int create_tcp_server_socket(int family) { int fd = -1; int rv; - auto service = util::utos(get_config()->port); + auto service = util::utos(listenerconf.port); addrinfo hints{}; hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; @@ -541,16 +547,16 @@ int create_tcp_server_socket(int family) { hints.ai_flags |= AI_ADDRCONFIG; #endif // AI_ADDRCONFIG - auto node = strcmp("*", get_config()->host.get()) == 0 + auto node = strcmp("*", listenerconf.host.get()) == 0 ? nullptr - : get_config()->host.get(); + : listenerconf.host.get(); addrinfo *res, *rp; rv = getaddrinfo(node, service.c_str(), &hints, &res); if (rv != 0) { if (LOG_ENABLED(INFO)) { LOG(INFO) << "Unable to get IPv" << (family == AF_INET ? "4" : "6") - << " address for " << get_config()->host.get() << ": " + << " address for " << listenerconf.host.get() << ": " << gai_strerror(rv); } return -1; @@ -619,15 +625,15 @@ int create_tcp_server_socket(int family) { continue; } - if (get_config()->fastopen > 0) { - val = get_config()->fastopen; + if (listenerconf.fastopen > 0) { + val = listenerconf.fastopen; if (setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &val, static_cast(sizeof(val))) == -1) { LOG(WARN) << "Failed to set TCP_FASTOPEN option to listener socket"; } } - if (listen(fd, get_config()->backlog) == -1) { + if (listen(fd, listenerconf.backlog) == -1) { auto error = errno; LOG(WARN) << "listen() syscall failed, error=" << error; close(fd); @@ -656,7 +662,7 @@ int create_tcp_server_socket(int family) { return -1; } - LOG(NOTICE) << "Listening on " << host << ", port " << get_config()->port; + LOG(NOTICE) << "Listening on " << host << ", port " << listenerconf.port; return fd; } @@ -794,12 +800,14 @@ int event_loop() { util::make_socket_closeonexec(fd); } - if (get_config()->host_unix) { + auto &listenerconf = get_config()->conn.listener; + + if (listenerconf.host_unix) { close_env_fd({ENV_LISTENER4_FD, ENV_LISTENER6_FD}); auto fd = create_unix_domain_server_socket(); if (fd == -1) { LOG(FATAL) << "Failed to listen on UNIX domain socket " - << get_config()->host.get(); + << listenerconf.host.get(); return -1; } @@ -808,10 +816,10 @@ int event_loop() { if (get_config()->uid != 0) { // fd is not associated to inode, so we cannot use fchown(2) // here. https://lkml.org/lkml/2004/11/1/84 - if (chown_to_running_user(get_config()->host.get()) == -1) { + if (chown_to_running_user(listenerconf.host.get()) == -1) { auto error = errno; LOG(WARN) << "Changing owner of UNIX domain socket " - << get_config()->host.get() << " failed: " << strerror(error); + << listenerconf.host.get() << " failed: " << strerror(error); } } } else { @@ -819,8 +827,8 @@ int event_loop() { auto fd6 = create_tcp_server_socket(AF_INET6); auto fd4 = create_tcp_server_socket(AF_INET); if (fd6 == -1 && fd4 == -1) { - LOG(FATAL) << "Failed to listen on address " << get_config()->host.get() - << ", port " << get_config()->port; + LOG(FATAL) << "Failed to listen on address " << listenerconf.host.get() + << ", port " << listenerconf.port; return -1; } @@ -900,38 +908,8 @@ void fill_default_config() { mod_config()->verbose = false; mod_config()->daemon = false; - mod_config()->host = strcopy("*"); - mod_config()->port = 3000; - - // Read timeout for HTTP2 upstream connection - mod_config()->http2_upstream_read_timeout = 3_min; - - // Read timeout for non-HTTP2 upstream connection - mod_config()->upstream_read_timeout = 1_min; - - // Write timeout for HTTP2/non-HTTP2 upstream connection - mod_config()->upstream_write_timeout = 30.; - - // Read/Write timeouts for downstream connection - mod_config()->downstream_read_timeout = 1_min; - mod_config()->downstream_write_timeout = 30.; - - // Read timeout for HTTP/2 stream - mod_config()->stream_read_timeout = 0.; - - // Write timeout for HTTP/2 stream - mod_config()->stream_write_timeout = 0.; - - // Timeout for pooled (idle) connections - mod_config()->downstream_idle_read_timeout = 2.; - - mod_config()->upstream_no_tls = false; - mod_config()->downstream_no_tls = false; - mod_config()->num_worker = 1; mod_config()->conf_path = strcopy("/etc/nghttpx/nghttpx.conf"); - // Default accept() backlog - mod_config()->backlog = 512; mod_config()->http2_proxy = false; mod_config()->http2_bridge = false; mod_config()->client_proxy = false; @@ -942,29 +920,10 @@ void fill_default_config() { mod_config()->uid = 0; mod_config()->gid = 0; mod_config()->pid = getpid(); - mod_config()->backend_ipv4 = false; - mod_config()->backend_ipv6 = false; - mod_config()->read_rate = 0; - mod_config()->read_burst = 0; - mod_config()->write_rate = 0; - mod_config()->write_burst = 0; - mod_config()->worker_read_rate = 0; - mod_config()->worker_read_burst = 0; - mod_config()->worker_write_rate = 0; - mod_config()->worker_write_burst = 0; mod_config()->padding = 0; - mod_config()->worker_frontend_connections = 0; mod_config()->argc = 0; mod_config()->argv = nullptr; - mod_config()->downstream_connections_per_host = 8; - mod_config()->downstream_connections_per_frontend = 0; - mod_config()->listener_disable_timeout = 0.; - mod_config()->downstream_request_buffer_size = 16_k; - mod_config()->downstream_response_buffer_size = 16_k; - mod_config()->host_unix = false; - mod_config()->downstream_addr_group_catch_all = 0; - mod_config()->fastopen = 0; auto &tlsconf = mod_config()->tls; { @@ -1014,6 +973,15 @@ void fill_default_config() { auto &http2conf = mod_config()->http2; { + auto &timeoutconf = http2conf.timeout; + { + // Read timeout for HTTP/2 stream + timeoutconf.stream_read = 0.; + + // Write timeout for HTTP/2 stream + timeoutconf.stream_write = 0.; + } + auto &upstreamconf = http2conf.upstream; // window bits for HTTP/2 and SPDY upstream connection per // stream. 2**16-1 = 64KiB-1, which is HTTP/2 default. Please note @@ -1054,6 +1022,54 @@ void fill_default_config() { } loggingconf.syslog_facility = LOG_DAEMON; + + auto &connconf = mod_config()->conn; + { + auto &listenerconf = connconf.listener; + { + listenerconf.host = strcopy("*"); + listenerconf.port = 3000; + // Default accept() backlog + listenerconf.backlog = 512; + listenerconf.host_unix = false; + } + } + + { + auto &upstreamconf = connconf.upstream; + { + auto &timeoutconf = upstreamconf.timeout; + // Read timeout for HTTP2 upstream connection + timeoutconf.http2_read = 3_min; + + // Read timeout for non-HTTP2 upstream connection + timeoutconf.read = 1_min; + + // Write timeout for HTTP2/non-HTTP2 upstream connection + timeoutconf.write = 30.; + } + + upstreamconf.no_tls = false; + } + + { + auto &downstreamconf = connconf.downstream; + { + auto &timeoutconf = downstreamconf.timeout; + // Read/Write timeouts for downstream connection + timeoutconf.read = 1_min; + timeoutconf.write = 30.; + // Timeout for pooled (idle) connections + timeoutconf.idle_read = 2.; + } + + downstreamconf.no_tls = false; + downstreamconf.ipv4 = false; + downstreamconf.ipv6 = false; + downstreamconf.connections_per_host = 8; + downstreamconf.request_buffer_size = 16_k; + downstreamconf.response_buffer_size = 16_k; + } } } // namespace @@ -1151,11 +1167,11 @@ Connections: assumes all addresses including both IPv4 and IPv6. UNIX domain socket can be specified by prefixing path name with "unix:" (e.g., unix:/var/run/nghttpx.sock) - Default: )" << get_config()->host.get() << "," - << get_config()->port << R"( + Default: )" << get_config()->conn.listener.host.get() << "," + << get_config()->conn.listener.port << R"( --backlog= Set listen backlog size. - Default: )" << get_config()->backlog << R"( + Default: )" << get_config()->conn.listener.backlog << R"( --backend-ipv4 Resolve backend hostname to IPv4 address only. --backend-ipv6 @@ -1183,45 +1199,50 @@ Performance: --read-rate= Set maximum average read rate on frontend connection. Setting 0 to this option means read rate is unlimited. - Default: )" << get_config()->read_rate << R"( + Default: )" << get_config()->conn.upstream.ratelimit.read.rate + << R"( --read-burst= Set maximum read burst size on frontend connection. Setting 0 to this option means read burst size is unlimited. - Default: )" << get_config()->read_burst << R"( + Default: )" << get_config()->conn.upstream.ratelimit.read.burst + << R"( --write-rate= Set maximum average write rate on frontend connection. Setting 0 to this option means write rate is unlimited. - Default: )" << get_config()->write_rate << R"( + Default: )" << get_config()->conn.upstream.ratelimit.write.rate + << R"( --write-burst= Set maximum write burst size on frontend connection. Setting 0 to this option means write burst size is unlimited. - Default: )" << get_config()->write_burst << R"( + Default: )" << get_config()->conn.upstream.ratelimit.write.burst + << R"( --worker-read-rate= Set maximum average read rate on frontend connection per worker. Setting 0 to this option means read rate is unlimited. Not implemented yet. - Default: )" << get_config()->worker_read_rate << R"( + Default: 0 --worker-read-burst= Set maximum read burst size on frontend connection per worker. Setting 0 to this option means read burst size is unlimited. Not implemented yet. - Default: )" << get_config()->worker_read_burst << R"( + Default: 0 --worker-write-rate= Set maximum average write rate on frontend connection per worker. Setting 0 to this option means write rate is unlimited. Not implemented yet. - Default: )" << get_config()->worker_write_rate << R"( + Default: 0 --worker-write-burst= Set maximum write burst size on frontend connection per worker. Setting 0 to this option means write burst size is unlimited. Not implemented yet. - Default: )" << get_config()->worker_write_burst << R"( + Default: 0 --worker-frontend-connections= Set maximum number of simultaneous connections frontend accepts. Setting 0 means unlimited. - Default: )" << get_config()->worker_frontend_connections << R"( + Default: )" << get_config()->conn.upstream.worker_connections + << R"( --backend-http2-connections-per-worker= Set maximum number of backend HTTP/2 physical connections per worker. If pattern is used in -b @@ -1239,7 +1260,7 @@ Performance: header field for HTTP/2). To limit the number of connections per frontend for default mode, use --backend-http1-connections-per-frontend. - Default: )" << get_config()->downstream_connections_per_host + Default: )" << get_config()->conn.downstream.connections_per_host << R"( --backend-http1-connections-per-frontend= Set maximum number of backend concurrent HTTP/1 @@ -1247,8 +1268,8 @@ Performance: default mode. 0 means unlimited. To limit the number of connections per host for HTTP/2 or SPDY proxy mode (-s option), use --backend-http1-connections-per-host. - Default: )" << get_config()->downstream_connections_per_frontend - << R"( + Default: )" + << get_config()->conn.downstream.connections_per_frontend << R"( --rlimit-nofile= Set maximum number of open files (RLIMIT_NOFILE) to . If 0 is given, nghttpx does not set the limit. @@ -1256,59 +1277,63 @@ Performance: --backend-request-buffer= Set buffer size used to store backend request. Default: )" - << util::utos_unit(get_config()->downstream_request_buffer_size) << R"( + << util::utos_unit(get_config()->conn.downstream.request_buffer_size) + << R"( --backend-response-buffer= Set buffer size used to store backend response. Default: )" - << util::utos_unit(get_config()->downstream_response_buffer_size) << R"( + << util::utos_unit(get_config()->conn.downstream.response_buffer_size) + << R"( --fastopen= Enables "TCP Fast Open" for the listening socket and limits the maximum length for the queue of connections that have not yet completed the three-way handshake. If value is 0 then fast open is disabled. - Default: )" << get_config()->fastopen << R"( + Default: )" << get_config()->conn.listener.fastopen << R"( Timeout: --frontend-http2-read-timeout= Specify read timeout for HTTP/2 and SPDY frontend connection. Default: )" - << util::duration_str(get_config()->http2_upstream_read_timeout) << R"( + << util::duration_str(get_config()->conn.upstream.timeout.http2_read) + << R"( --frontend-read-timeout= Specify read timeout for HTTP/1.1 frontend connection. Default: )" - << util::duration_str(get_config()->upstream_read_timeout) << R"( + << util::duration_str(get_config()->conn.upstream.timeout.read) << R"( --frontend-write-timeout= Specify write timeout for all frontend connections. Default: )" - << util::duration_str(get_config()->upstream_write_timeout) << R"( + << util::duration_str(get_config()->conn.upstream.timeout.write) << R"( --stream-read-timeout= Specify read timeout for HTTP/2 and SPDY streams. 0 means no timeout. Default: )" - << util::duration_str(get_config()->stream_read_timeout) << R"( + << util::duration_str(get_config()->http2.timeout.stream_read) << R"( --stream-write-timeout= Specify write timeout for HTTP/2 and SPDY streams. 0 means no timeout. Default: )" - << util::duration_str(get_config()->stream_write_timeout) << R"( + << util::duration_str(get_config()->http2.timeout.stream_write) << R"( --backend-read-timeout= Specify read timeout for backend connection. Default: )" - << util::duration_str(get_config()->downstream_read_timeout) << R"( + << util::duration_str(get_config()->conn.downstream.timeout.read) << R"( --backend-write-timeout= Specify write timeout for backend connection. Default: )" - << util::duration_str(get_config()->downstream_write_timeout) << R"( + << util::duration_str(get_config()->conn.downstream.timeout.write) << R"( --backend-keep-alive-timeout= Specify keep-alive timeout for backend connection. Default: )" - << util::duration_str(get_config()->downstream_idle_read_timeout) << R"( + << util::duration_str(get_config()->conn.downstream.timeout.idle_read) + << R"( --listener-disable-timeout= After accepting connection failed, connection listener is disabled for a given amount of time. Specifying 0 disables this feature. Default: )" - << util::duration_str(get_config()->listener_disable_timeout) << R"( + << util::duration_str(get_config()->conn.listener.timeout.sleep) << R"( SSL/TLS: --ciphers= @@ -1858,15 +1883,18 @@ void process_options( tlsconf.alpn_prefs = ssl::set_alpn_prefs(tlsconf.npn_list); - if (get_config()->backend_ipv4 && get_config()->backend_ipv6) { + auto &listenerconf = mod_config()->conn.listener; + auto &upstreamconf = mod_config()->conn.upstream; + auto &downstreamconf = mod_config()->conn.downstream; + + if (downstreamconf.ipv4 && downstreamconf.ipv6) { LOG(FATAL) << "--backend-ipv4 and --backend-ipv6 cannot be used at the " << "same time."; exit(EXIT_FAILURE); } - if (get_config()->worker_frontend_connections == 0) { - mod_config()->worker_frontend_connections = - std::numeric_limits::max(); + if (upstreamconf.worker_connections == 0) { + upstreamconf.worker_connections = std::numeric_limits::max(); } if (get_config()->http2_proxy + get_config()->http2_bridge + @@ -1879,23 +1907,23 @@ void process_options( if (get_config()->client || get_config()->client_proxy) { mod_config()->client_mode = true; - mod_config()->upstream_no_tls = true; + upstreamconf.no_tls = true; } if (get_config()->client_mode || get_config()->http2_bridge) { - mod_config()->downstream_proto = PROTO_HTTP2; + downstreamconf.proto = PROTO_HTTP2; } else { - mod_config()->downstream_proto = PROTO_HTTP; + downstreamconf.proto = PROTO_HTTP; } - if (!get_config()->upstream_no_tls && + if (!upstreamconf.no_tls && (!tlsconf.private_key_file || !tlsconf.cert_file)) { print_usage(std::cerr); LOG(FATAL) << "Too few arguments"; exit(EXIT_FAILURE); } - if (!get_config()->upstream_no_tls && !tlsconf.ocsp.disabled) { + if (!upstreamconf.no_tls && !tlsconf.ocsp.disabled) { struct stat buf; if (stat(tlsconf.ocsp.fetch_ocsp_response_file.get(), &buf) != 0) { tlsconf.ocsp.disabled = true; @@ -1905,31 +1933,31 @@ void process_options( } } - if (get_config()->downstream_addr_groups.empty()) { + auto &addr_groups = downstreamconf.addr_groups; + + if (addr_groups.empty()) { DownstreamAddr addr; addr.host = ImmutableString::from_lit(DEFAULT_DOWNSTREAM_HOST); addr.port = DEFAULT_DOWNSTREAM_PORT; DownstreamAddrGroup g("/"); g.addrs.push_back(std::move(addr)); - mod_config()->router.add_route(g.pattern.get(), 1, - get_config()->downstream_addr_groups.size()); - mod_config()->downstream_addr_groups.push_back(std::move(g)); + mod_config()->router.add_route(g.pattern.get(), 1, addr_groups.size()); + addr_groups.push_back(std::move(g)); } else if (get_config()->http2_proxy || get_config()->client_proxy) { // We don't support host mapping in these cases. Move all // non-catch-all patterns to catch-all pattern. DownstreamAddrGroup catch_all("/"); - for (auto &g : mod_config()->downstream_addr_groups) { + for (auto &g : addr_groups) { std::move(std::begin(g.addrs), std::end(g.addrs), std::back_inserter(catch_all.addrs)); } - std::vector().swap( - mod_config()->downstream_addr_groups); + std::vector().swap(addr_groups); // maybe not necessary? mod_config()->router = Router(); mod_config()->router.add_route(catch_all.pattern.get(), 1, - get_config()->downstream_addr_groups.size()); - mod_config()->downstream_addr_groups.push_back(std::move(catch_all)); + addr_groups.size()); + addr_groups.push_back(std::move(catch_all)); } if (LOG_ENABLED(INFO)) { @@ -1937,8 +1965,8 @@ void process_options( } ssize_t catch_all_group = -1; - for (size_t i = 0; i < mod_config()->downstream_addr_groups.size(); ++i) { - auto &g = mod_config()->downstream_addr_groups[i]; + for (size_t i = 0; i < addr_groups.size(); ++i) { + auto &g = addr_groups[i]; if (util::streq(g.pattern.get(), "/")) { catch_all_group = i; } @@ -1956,13 +1984,14 @@ void process_options( LOG(FATAL) << "-b: No catch-all backend address is configured"; exit(EXIT_FAILURE); } - mod_config()->downstream_addr_group_catch_all = catch_all_group; + + downstreamconf.addr_group_catch_all = catch_all_group; if (LOG_ENABLED(INFO)) { LOG(INFO) << "Catch-all pattern is group " << catch_all_group; } - for (auto &g : mod_config()->downstream_addr_groups) { + for (auto &g : addr_groups) { for (auto &addr : g.addrs) { if (addr.host_unix) { @@ -1970,7 +1999,7 @@ void process_options( // hostport. This is used as Host header field to backend and // not going to be passed to any syscalls. addr.hostport = ImmutableString( - util::make_hostport("localhost", get_config()->port)); + util::make_hostport("localhost", listenerconf.port)); auto path = addr.host.c_str(); auto pathlen = addr.host.size(); @@ -1997,9 +2026,9 @@ void process_options( if (resolve_hostname( &addr.addr, addr.host.c_str(), addr.port, - get_config()->backend_ipv4 ? AF_INET : (get_config()->backend_ipv6 - ? AF_INET6 - : AF_UNSPEC)) == -1) { + downstreamconf.ipv4 + ? AF_INET + : (downstreamconf.ipv6 ? AF_INET6 : AF_UNSPEC)) == -1) { exit(EXIT_FAILURE); } } diff --git a/src/shrpx_client_handler.cc b/src/shrpx_client_handler.cc index 1a8822de..1bcd9bd1 100644 --- a/src/shrpx_client_handler.cc +++ b/src/shrpx_client_handler.cc @@ -371,16 +371,16 @@ int ClientHandler::upstream_http1_connhd_read() { ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl, const char *ipaddr, const char *port) : conn_(worker->get_loop(), fd, ssl, worker->get_mcpool(), - get_config()->upstream_write_timeout, - get_config()->upstream_read_timeout, get_config()->write_rate, - get_config()->write_burst, get_config()->read_rate, - get_config()->read_burst, writecb, readcb, timeoutcb, this, - get_config()->tls.dyn_rec.warmup_threshold, + get_config()->conn.upstream.timeout.write, + get_config()->conn.upstream.timeout.read, + get_config()->conn.upstream.ratelimit.write, + get_config()->conn.upstream.ratelimit.read, writecb, readcb, + timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, get_config()->tls.dyn_rec.idle_timeout), pinned_http2sessions_( - get_config()->downstream_proto == PROTO_HTTP2 + get_config()->conn.downstream.proto == PROTO_HTTP2 ? make_unique>( - get_config()->downstream_addr_groups.size(), -1) + get_config()->conn.downstream.addr_groups.size(), -1) : nullptr), ipaddr_(ipaddr), port_(port), worker_(worker), left_connhd_len_(NGHTTP2_CLIENT_MAGIC_LEN), @@ -395,7 +395,7 @@ ClientHandler::ClientHandler(Worker *worker, int fd, SSL *ssl, conn_.rlimit.startw(); ev_timer_again(conn_.loop, &conn_.rt); - if (get_config()->accept_proxy_protocol) { + if (get_config()->conn.upstream.accept_proxy_protocol) { read_ = &ClientHandler::read_clear; write_ = &ClientHandler::noop; on_read_ = &ClientHandler::proxy_protocol_read; @@ -647,8 +647,9 @@ void ClientHandler::remove_downstream_connection(DownstreamConnection *dconn) { std::unique_ptr ClientHandler::get_downstream_connection(Downstream *downstream) { size_t group; - auto &groups = get_config()->downstream_addr_groups; - auto catch_all = get_config()->downstream_addr_group_catch_all; + auto &downstreamconf = get_config()->conn.downstream; + auto &groups = downstreamconf.addr_groups; + auto catch_all = downstreamconf.addr_group_catch_all; const auto &req = downstream->request(); @@ -693,7 +694,7 @@ ClientHandler::get_downstream_connection(Downstream *downstream) { auto dconn_pool = worker_->get_dconn_pool(); - if (get_config()->downstream_proto == PROTO_HTTP2) { + if (downstreamconf.proto == PROTO_HTTP2) { Http2Session *http2session; auto &pinned = (*pinned_http2sessions_)[group]; if (pinned == -1) { @@ -857,8 +858,8 @@ void ClientHandler::write_accesslog(Downstream *downstream) { std::chrono::high_resolution_clock::now(), // request_end_time req.http_major, req.http_minor, resp.http_status, - downstream->response_sent_body_length, port_, get_config()->port, - get_config()->pid, + downstream->response_sent_body_length, port_, + get_config()->conn.listener.port, get_config()->pid, }); } @@ -880,7 +881,8 @@ void ClientHandler::write_accesslog(int major, int minor, unsigned int status, // there a better value? highres_now, // request_end_time major, minor, // major, minor - status, body_bytes_sent, port_, get_config()->port, get_config()->pid, + status, body_bytes_sent, port_, get_config()->conn.listener.port, + get_config()->pid, }); } @@ -1156,7 +1158,7 @@ const std::string &ClientHandler::get_forwarded_by() { local_hostport_ += ':'; } - local_hostport_ += util::utos(get_config()->port); + local_hostport_ += util::utos(get_config()->conn.listener.port); return local_hostport_; } diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 47e8a80c..b22461f9 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -594,6 +594,8 @@ void parse_mapping(const DownstreamAddr &addr, const char *src) { // will append '/' to all patterns, so it becomes catch-all pattern. auto mapping = util::split_config_str_list(src, ':'); assert(!mapping.empty()); + auto &addr_groups = mod_config()->conn.downstream.addr_groups; + for (const auto &raw_pattern : mapping) { auto done = false; std::string pattern; @@ -608,7 +610,7 @@ void parse_mapping(const DownstreamAddr &addr, const char *src) { util::inp_strlower(pattern); pattern += http2::normalize_path(slash, raw_pattern.second); } - for (auto &g : mod_config()->downstream_addr_groups) { + for (auto &g : addr_groups) { if (g.pattern.get() == pattern) { g.addrs.push_back(addr); done = true; @@ -622,9 +624,9 @@ void parse_mapping(const DownstreamAddr &addr, const char *src) { g.addrs.push_back(addr); mod_config()->router.add_route(g.pattern.get(), strlen(g.pattern.get()), - get_config()->downstream_addr_groups.size()); + addr_groups.size()); - mod_config()->downstream_addr_groups.push_back(std::move(g)); + addr_groups.push_back(std::move(g)); } } } // namespace @@ -1419,11 +1421,13 @@ int parse_config(const char *opt, const char *optarg, return 0; } case SHRPX_OPTID_FRONTEND: { + auto &listenerconf = mod_config()->conn.listener; + if (util::istarts_with(optarg, SHRPX_UNIX_PATH_PREFIX)) { auto path = optarg + str_size(SHRPX_UNIX_PATH_PREFIX); - mod_config()->host = strcopy(path); - mod_config()->port = 0; - mod_config()->host_unix = true; + listenerconf.host = strcopy(path); + listenerconf.port = 0; + listenerconf.host_unix = true; return 0; } @@ -1433,9 +1437,9 @@ int parse_config(const char *opt, const char *optarg, return -1; } - mod_config()->host = strcopy(host); - mod_config()->port = port; - mod_config()->host_unix = false; + listenerconf.host = strcopy(host); + listenerconf.port = port; + listenerconf.host_unix = false; return 0; } @@ -1484,20 +1488,26 @@ int parse_config(const char *opt, const char *optarg, return 0; case SHRPX_OPTID_FRONTEND_HTTP2_READ_TIMEOUT: - return parse_duration(&mod_config()->http2_upstream_read_timeout, opt, + return parse_duration(&mod_config()->conn.upstream.timeout.http2_read, opt, optarg); case SHRPX_OPTID_FRONTEND_READ_TIMEOUT: - return parse_duration(&mod_config()->upstream_read_timeout, opt, optarg); + return parse_duration(&mod_config()->conn.upstream.timeout.read, opt, + optarg); case SHRPX_OPTID_FRONTEND_WRITE_TIMEOUT: - return parse_duration(&mod_config()->upstream_write_timeout, opt, optarg); + return parse_duration(&mod_config()->conn.upstream.timeout.write, opt, + optarg); case SHRPX_OPTID_BACKEND_READ_TIMEOUT: - return parse_duration(&mod_config()->downstream_read_timeout, opt, optarg); + return parse_duration(&mod_config()->conn.downstream.timeout.read, opt, + optarg); case SHRPX_OPTID_BACKEND_WRITE_TIMEOUT: - return parse_duration(&mod_config()->downstream_write_timeout, opt, optarg); + return parse_duration(&mod_config()->conn.downstream.timeout.write, opt, + optarg); case SHRPX_OPTID_STREAM_READ_TIMEOUT: - return parse_duration(&mod_config()->stream_read_timeout, opt, optarg); + return parse_duration(&mod_config()->http2.timeout.stream_read, opt, + optarg); case SHRPX_OPTID_STREAM_WRITE_TIMEOUT: - return parse_duration(&mod_config()->stream_write_timeout, opt, optarg); + return parse_duration(&mod_config()->http2.timeout.stream_write, opt, + optarg); case SHRPX_OPTID_ACCESSLOG_FILE: mod_config()->logging.access.file = strcopy(optarg); @@ -1529,12 +1539,12 @@ int parse_config(const char *opt, const char *optarg, return -1; } - mod_config()->fastopen = n; + mod_config()->conn.listener.fastopen = n; return 0; } case SHRPX_OPTID_BACKEND_KEEP_ALIVE_TIMEOUT: - return parse_duration(&mod_config()->downstream_idle_read_timeout, opt, + return parse_duration(&mod_config()->conn.downstream.timeout.idle_read, opt, optarg); case SHRPX_OPTID_FRONTEND_HTTP2_WINDOW_BITS: case SHRPX_OPTID_BACKEND_HTTP2_WINDOW_BITS: { @@ -1593,11 +1603,11 @@ int parse_config(const char *opt, const char *optarg, return 0; } case SHRPX_OPTID_FRONTEND_NO_TLS: - mod_config()->upstream_no_tls = util::strieq(optarg, "yes"); + mod_config()->conn.upstream.no_tls = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_BACKEND_NO_TLS: - mod_config()->downstream_no_tls = util::strieq(optarg, "yes"); + mod_config()->conn.downstream.no_tls = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_BACKEND_TLS_SNI_FIELD: @@ -1676,7 +1686,7 @@ int parse_config(const char *opt, const char *optarg, return -1; } - mod_config()->backlog = n; + mod_config()->conn.listener.backlog = n; return 0; } @@ -1697,11 +1707,11 @@ int parse_config(const char *opt, const char *optarg, return 0; case SHRPX_OPTID_BACKEND_IPV4: - mod_config()->backend_ipv4 = util::strieq(optarg, "yes"); + mod_config()->conn.downstream.ipv4 = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_BACKEND_IPV6: - mod_config()->backend_ipv6 = util::strieq(optarg, "yes"); + mod_config()->conn.downstream.ipv6 = util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_BACKEND_HTTP_PROXY_URI: { @@ -1742,25 +1752,29 @@ int parse_config(const char *opt, const char *optarg, return 0; } case SHRPX_OPTID_READ_RATE: - return parse_uint_with_unit(&mod_config()->read_rate, opt, optarg); + return parse_uint_with_unit( + &mod_config()->conn.upstream.ratelimit.read.rate, opt, optarg); case SHRPX_OPTID_READ_BURST: - return parse_uint_with_unit(&mod_config()->read_burst, opt, optarg); + return parse_uint_with_unit( + &mod_config()->conn.upstream.ratelimit.read.burst, opt, optarg); case SHRPX_OPTID_WRITE_RATE: - return parse_uint_with_unit(&mod_config()->write_rate, opt, optarg); + return parse_uint_with_unit( + &mod_config()->conn.upstream.ratelimit.write.rate, opt, optarg); case SHRPX_OPTID_WRITE_BURST: - return parse_uint_with_unit(&mod_config()->write_burst, opt, optarg); + return parse_uint_with_unit( + &mod_config()->conn.upstream.ratelimit.write.burst, opt, optarg); case SHRPX_OPTID_WORKER_READ_RATE: LOG(WARN) << opt << ": not implemented yet"; - return parse_uint_with_unit(&mod_config()->worker_read_rate, opt, optarg); + return 0; case SHRPX_OPTID_WORKER_READ_BURST: LOG(WARN) << opt << ": not implemented yet"; - return parse_uint_with_unit(&mod_config()->worker_read_burst, opt, optarg); + return 0; case SHRPX_OPTID_WORKER_WRITE_RATE: LOG(WARN) << opt << ": not implemented yet"; - return parse_uint_with_unit(&mod_config()->worker_write_rate, opt, optarg); + return 0; case SHRPX_OPTID_WORKER_WRITE_BURST: LOG(WARN) << opt << ": not implemented yet"; - return parse_uint_with_unit(&mod_config()->worker_write_burst, opt, optarg); + return 0; case SHRPX_OPTID_NPN_LIST: mod_config()->tls.npn_list = util::parse_config_str_list(optarg); @@ -1867,7 +1881,8 @@ int parse_config(const char *opt, const char *optarg, return 0; } case SHRPX_OPTID_WORKER_FRONTEND_CONNECTIONS: - return parse_uint(&mod_config()->worker_frontend_connections, opt, optarg); + return parse_uint(&mod_config()->conn.upstream.worker_connections, opt, + optarg); case SHRPX_OPTID_NO_LOCATION_REWRITE: mod_config()->http.no_location_rewrite = util::strieq(optarg, "yes"); @@ -1892,15 +1907,16 @@ int parse_config(const char *opt, const char *optarg, return -1; } - mod_config()->downstream_connections_per_host = n; + mod_config()->conn.downstream.connections_per_host = n; return 0; } case SHRPX_OPTID_BACKEND_HTTP1_CONNECTIONS_PER_FRONTEND: - return parse_uint(&mod_config()->downstream_connections_per_frontend, opt, - optarg); + return parse_uint(&mod_config()->conn.downstream.connections_per_frontend, + opt, optarg); case SHRPX_OPTID_LISTENER_DISABLE_TIMEOUT: - return parse_duration(&mod_config()->listener_disable_timeout, opt, optarg); + return parse_duration(&mod_config()->conn.listener.timeout.sleep, opt, + optarg); case SHRPX_OPTID_TLS_TICKET_KEY_FILE: mod_config()->tls.ticket.files.push_back(optarg); return 0; @@ -1935,9 +1951,9 @@ int parse_config(const char *opt, const char *optarg, } if (optid == SHRPX_OPTID_BACKEND_REQUEST_BUFFER) { - mod_config()->downstream_request_buffer_size = n; + mod_config()->conn.downstream.request_buffer_size = n; } else { - mod_config()->downstream_response_buffer_size = n; + mod_config()->conn.downstream.response_buffer_size = n; } return 0; @@ -2065,7 +2081,8 @@ int parse_config(const char *opt, const char *optarg, #endif // !HAVE_MRUBY return 0; case SHRPX_OPTID_ACCEPT_PROXY_PROTOCOL: - mod_config()->accept_proxy_protocol = util::strieq(optarg, "yes"); + mod_config()->conn.upstream.accept_proxy_protocol = + util::strieq(optarg, "yes"); return 0; case SHRPX_OPTID_ADD_FORWARDED: { diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 4620227b..7963828f 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -429,6 +429,10 @@ struct Http2Config { size_t connection_window_bits; size_t connections_per_worker; } downstream; + struct { + ev_tstamp stream_read; + ev_tstamp stream_write; + } timeout; size_t max_concurrent_streams; bool no_cookie_crumbling; bool no_server_push; @@ -449,26 +453,77 @@ struct LoggingConfig { int syslog_facility; }; +struct RateLimitConfig { + size_t rate; + size_t burst; +}; + +struct ConnectionConfig { + struct { + struct { + ev_tstamp sleep; + } timeout; + // address of frontend connection. This could be a path to UNIX + // domain socket. In this case, |host_unix| must be true. + std::unique_ptr host; + // frontend listening port. 0 if frontend listens on UNIX domain + // socket, in this case |host_unix| must be true. + uint16_t port; + // true if host contains UNIX domain socket path + bool host_unix; + int backlog; + // TCP fastopen. If this is positive, it is passed to + // setsockopt() along with TCP_FASTOPEN. + int fastopen; + } listener; + + struct { + struct { + ev_tstamp http2_read; + ev_tstamp read; + ev_tstamp write; + } timeout; + struct { + RateLimitConfig read; + RateLimitConfig write; + } ratelimit; + size_t worker_connections; + bool no_tls; + bool accept_proxy_protocol; + } upstream; + + struct { + struct { + ev_tstamp read; + ev_tstamp write; + ev_tstamp idle_read; + } timeout; + std::vector addr_groups; + // The index of catch-all group in downstream_addr_groups. + size_t addr_group_catch_all; + size_t connections_per_host; + size_t connections_per_frontend; + size_t request_buffer_size; + size_t response_buffer_size; + // downstream protocol; this will be determined by given options. + shrpx_proto proto; + bool no_tls; + // true if IPv4 only; ipv4 and ipv6 are mutually exclusive; and + // (ipv4 && ipv6) must be false. + bool ipv4; + // true if IPv6 only + bool ipv6; + } downstream; +}; + struct Config { - std::vector downstream_addr_groups; Router router; HttpProxy downstream_http_proxy; HttpConfig http; Http2Config http2; TLSConfig tls; LoggingConfig logging; - ev_tstamp http2_upstream_read_timeout; - ev_tstamp upstream_read_timeout; - ev_tstamp upstream_write_timeout; - ev_tstamp downstream_read_timeout; - ev_tstamp downstream_write_timeout; - ev_tstamp stream_read_timeout; - ev_tstamp stream_write_timeout; - ev_tstamp downstream_idle_read_timeout; - ev_tstamp listener_disable_timeout; - // address of frontend connection. This could be a path to UNIX - // domain socket. In this case, |host_unix| must be true. - std::unique_ptr host; + ConnectionConfig conn; std::unique_ptr pid_file; std::unique_ptr conf_path; std::unique_ptr user; @@ -477,49 +532,20 @@ struct Config { char **argv; char *cwd; size_t num_worker; - size_t downstream_connections_per_host; - size_t downstream_connections_per_frontend; - size_t read_rate; - size_t read_burst; - size_t write_rate; - size_t write_burst; - size_t worker_read_rate; - size_t worker_read_burst; - size_t worker_write_rate; - size_t worker_write_burst; size_t padding; - size_t worker_frontend_connections; size_t rlimit_nofile; - size_t downstream_request_buffer_size; - size_t downstream_response_buffer_size; - // The index of catch-all group in downstream_addr_groups. - size_t downstream_addr_group_catch_all; - // downstream protocol; this will be determined by given options. - shrpx_proto downstream_proto; - int backlog; int argc; - int fastopen; uid_t uid; gid_t gid; pid_t pid; - // frontend listening port. 0 if frontend listens on UNIX domain - // socket, in this case |host_unix| must be true. - uint16_t port; bool verbose; bool daemon; bool http2_proxy; bool http2_bridge; bool client_proxy; - bool upstream_no_tls; - bool downstream_no_tls; bool client; // true if --client or --client-proxy are enabled. bool client_mode; - bool backend_ipv4; - bool backend_ipv6; - // true if host contains UNIX domain socket path - bool host_unix; - bool accept_proxy_protocol; }; const Config *get_config(); diff --git a/src/shrpx_connection.cc b/src/shrpx_connection.cc index 97d1e0af..5d27d110 100644 --- a/src/shrpx_connection.cc +++ b/src/shrpx_connection.cc @@ -42,15 +42,17 @@ using namespace nghttp2; namespace shrpx { Connection::Connection(struct ev_loop *loop, int fd, SSL *ssl, MemchunkPool *mcpool, ev_tstamp write_timeout, - ev_tstamp read_timeout, size_t write_rate, - size_t write_burst, size_t read_rate, size_t read_burst, - IOCb writecb, IOCb readcb, TimerCb timeoutcb, void *data, + ev_tstamp read_timeout, + const RateLimitConfig &write_limit, + const RateLimitConfig &read_limit, IOCb writecb, + IOCb readcb, TimerCb timeoutcb, void *data, size_t tls_dyn_rec_warmup_threshold, ev_tstamp tls_dyn_rec_idle_timeout) : tls{DefaultMemchunks(mcpool), DefaultPeekMemchunks(mcpool)}, - wlimit(loop, &wev, write_rate, write_burst), - rlimit(loop, &rev, read_rate, read_burst, this), writecb(writecb), - readcb(readcb), timeoutcb(timeoutcb), loop(loop), data(data), fd(fd), + wlimit(loop, &wev, write_limit.rate, write_limit.burst), + rlimit(loop, &rev, read_limit.rate, read_limit.burst, this), + writecb(writecb), readcb(readcb), timeoutcb(timeoutcb), loop(loop), + data(data), fd(fd), tls_dyn_rec_warmup_threshold(tls_dyn_rec_warmup_threshold), tls_dyn_rec_idle_timeout(tls_dyn_rec_idle_timeout) { diff --git a/src/shrpx_connection.h b/src/shrpx_connection.h index 4906a19a..30ff448d 100644 --- a/src/shrpx_connection.h +++ b/src/shrpx_connection.h @@ -73,10 +73,10 @@ using TimerCb = EVCb; struct Connection { Connection(struct ev_loop *loop, int fd, SSL *ssl, MemchunkPool *mcpool, - ev_tstamp write_timeout, ev_tstamp read_timeout, size_t write_rate, - size_t write_burst, size_t read_rate, size_t read_burst, - IOCb writecb, IOCb readcb, TimerCb timeoutcb, void *data, - size_t tls_dyn_rec_warmup_threshold, + ev_tstamp write_timeout, ev_tstamp read_timeout, + const RateLimitConfig &write_limit, + const RateLimitConfig &read_limit, IOCb writecb, IOCb readcb, + TimerCb timeoutcb, void *data, size_t tls_dyn_rec_warmup_threshold, ev_tstamp tls_dyn_rec_idle_timeout); ~Connection(); diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc index ee97437b..34b10996 100644 --- a/src/shrpx_connection_handler.cc +++ b/src/shrpx_connection_handler.cc @@ -300,13 +300,13 @@ int ConnectionHandler::handle_connection(int fd, sockaddr *addr, int addrlen) { } if (get_config()->num_worker == 1) { - + auto &upstreamconf = get_config()->conn.upstream; if (single_worker_->get_worker_stat()->num_connections >= - get_config()->worker_frontend_connections) { + upstreamconf.worker_connections) { if (LOG_ENABLED(INFO)) { LLOG(INFO, this) << "Too many connections >=" - << get_config()->worker_frontend_connections; + << upstreamconf.worker_connections; } close(fd); diff --git a/src/shrpx_downstream.cc b/src/shrpx_downstream.cc index be97814d..20bc9f67 100644 --- a/src/shrpx_downstream.cc +++ b/src/shrpx_downstream.cc @@ -124,14 +124,16 @@ Downstream::Downstream(Upstream *upstream, MemchunkPool *mcpool, chunked_response_(false), expect_final_response_(false), request_pending_(false) { + auto &timeoutconf = get_config()->http2.timeout; + ev_timer_init(&upstream_rtimer_, &upstream_rtimeoutcb, 0., - get_config()->stream_read_timeout); + timeoutconf.stream_read); ev_timer_init(&upstream_wtimer_, &upstream_wtimeoutcb, 0., - get_config()->stream_write_timeout); + timeoutconf.stream_write); ev_timer_init(&downstream_rtimer_, &downstream_rtimeoutcb, 0., - get_config()->stream_read_timeout); + timeoutconf.stream_read); ev_timer_init(&downstream_wtimer_, &downstream_wtimeoutcb, 0., - get_config()->stream_write_timeout); + timeoutconf.stream_write); upstream_rtimer_.data = this; upstream_wtimer_.data = this; @@ -491,7 +493,8 @@ void Downstream::set_chunked_request(bool f) { chunked_request_ = f; } bool Downstream::request_buf_full() { if (dconn_) { - return request_buf_.rleft() >= get_config()->downstream_request_buffer_size; + return request_buf_.rleft() >= + get_config()->conn.downstream.request_buffer_size; } else { return false; } @@ -582,7 +585,7 @@ DefaultMemchunks *Downstream::get_response_buf() { return &response_buf_; } bool Downstream::response_buf_full() { if (dconn_) { return response_buf_.rleft() >= - get_config()->downstream_response_buffer_size; + get_config()->conn.downstream.response_buffer_size; } else { return false; } @@ -764,7 +767,7 @@ void disable_timer(struct ev_loop *loop, ev_timer *w) { } // namespace void Downstream::reset_upstream_rtimer() { - if (get_config()->stream_read_timeout == 0.) { + if (get_config()->http2.timeout.stream_read == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); @@ -773,16 +776,18 @@ void Downstream::reset_upstream_rtimer() { void Downstream::reset_upstream_wtimer() { auto loop = upstream_->get_client_handler()->get_loop(); - if (get_config()->stream_write_timeout != 0.) { + auto &timeoutconf = get_config()->http2.timeout; + + if (timeoutconf.stream_write != 0.) { reset_timer(loop, &upstream_wtimer_); } - if (get_config()->stream_read_timeout != 0.) { + if (timeoutconf.stream_read != 0.) { try_reset_timer(loop, &upstream_rtimer_); } } void Downstream::ensure_upstream_wtimer() { - if (get_config()->stream_write_timeout == 0.) { + if (get_config()->http2.timeout.stream_write == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); @@ -790,7 +795,7 @@ void Downstream::ensure_upstream_wtimer() { } void Downstream::disable_upstream_rtimer() { - if (get_config()->stream_read_timeout == 0.) { + if (get_config()->http2.timeout.stream_read == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); @@ -798,7 +803,7 @@ void Downstream::disable_upstream_rtimer() { } void Downstream::disable_upstream_wtimer() { - if (get_config()->stream_write_timeout == 0.) { + if (get_config()->http2.timeout.stream_write == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); @@ -806,7 +811,7 @@ void Downstream::disable_upstream_wtimer() { } void Downstream::reset_downstream_rtimer() { - if (get_config()->stream_read_timeout == 0.) { + if (get_config()->http2.timeout.stream_read == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); @@ -815,16 +820,18 @@ void Downstream::reset_downstream_rtimer() { void Downstream::reset_downstream_wtimer() { auto loop = upstream_->get_client_handler()->get_loop(); - if (get_config()->stream_write_timeout != 0.) { + auto &timeoutconf = get_config()->http2.timeout; + + if (timeoutconf.stream_write != 0.) { reset_timer(loop, &downstream_wtimer_); } - if (get_config()->stream_read_timeout != 0.) { + if (timeoutconf.stream_read != 0.) { try_reset_timer(loop, &downstream_rtimer_); } } void Downstream::ensure_downstream_wtimer() { - if (get_config()->stream_write_timeout == 0.) { + if (get_config()->http2.timeout.stream_write == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); @@ -832,7 +839,7 @@ void Downstream::ensure_downstream_wtimer() { } void Downstream::disable_downstream_rtimer() { - if (get_config()->stream_read_timeout == 0.) { + if (get_config()->http2.timeout.stream_read == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); @@ -840,7 +847,7 @@ void Downstream::disable_downstream_rtimer() { } void Downstream::disable_downstream_wtimer() { - if (get_config()->stream_write_timeout == 0.) { + if (get_config()->http2.timeout.stream_write == 0.) { return; } auto loop = upstream_->get_client_handler()->get_loop(); diff --git a/src/shrpx_http.cc b/src/shrpx_http.cc index 0a67247b..faf83afb 100644 --- a/src/shrpx_http.cc +++ b/src/shrpx_http.cc @@ -47,7 +47,7 @@ std::string create_error_html(unsigned int status_code) { const auto &server_name = get_config()->http.server_name; res.append(server_name.c_str(), server_name.size()); res += " at port "; - res += util::utos(get_config()->port); + res += util::utos(get_config()->conn.listener.port); res += "
"; return res; } diff --git a/src/shrpx_http2_downstream_connection.cc b/src/shrpx_http2_downstream_connection.cc index c39a722c..4cf0f538 100644 --- a/src/shrpx_http2_downstream_connection.cc +++ b/src/shrpx_http2_downstream_connection.cc @@ -268,7 +268,7 @@ int Http2DownstreamConnection::push_request_headers() { auto addr_idx = http2session_->get_addr_idx(); auto group = http2session_->get_group(); const auto &downstream_hostport = - get_config()->downstream_addr_groups[group].addrs[addr_idx].hostport; + get_config()->conn.downstream.addr_groups[group].addrs[addr_idx].hostport; // For HTTP/1.0 request, there is no authority in request. In that // case, we use backend server's host nonetheless. diff --git a/src/shrpx_http2_session.cc b/src/shrpx_http2_session.cc index 24cb4ce1..0e732677 100644 --- a/src/shrpx_http2_session.cc +++ b/src/shrpx_http2_session.cc @@ -146,8 +146,8 @@ Http2Session::Http2Session(struct ev_loop *loop, SSL_CTX *ssl_ctx, ConnectBlocker *connect_blocker, Worker *worker, size_t group, size_t idx) : conn_(loop, -1, nullptr, worker->get_mcpool(), - get_config()->downstream_write_timeout, - get_config()->downstream_read_timeout, 0, 0, 0, 0, writecb, readcb, + get_config()->conn.downstream.timeout.write, + get_config()->conn.downstream.timeout.read, {}, {}, writecb, readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, get_config()->tls.dyn_rec.idle_timeout), worker_(worker), connect_blocker_(connect_blocker), ssl_ctx_(ssl_ctx), @@ -240,13 +240,13 @@ int Http2Session::disconnect(bool hard) { int Http2Session::check_cert() { return ssl::check_cert( conn_.tls.ssl, - &get_config()->downstream_addr_groups[group_].addrs[addr_idx_]); + &get_config()->conn.downstream.addr_groups[group_].addrs[addr_idx_]); } int Http2Session::initiate_connection() { int rv = 0; - auto &addrs = get_config()->downstream_addr_groups[group_].addrs; + auto &addrs = get_config()->conn.downstream.addr_groups[group_].addrs; if (state_ == DISCONNECTED) { if (connect_blocker_->blocked()) { @@ -509,7 +509,7 @@ int Http2Session::downstream_connect_proxy() { SSLOG(INFO, this) << "Connected to the proxy"; } auto &downstream_addr = - get_config()->downstream_addr_groups[group_].addrs[addr_idx_]; + get_config()->conn.downstream.addr_groups[group_].addrs[addr_idx_]; std::string req = "CONNECT "; req.append(downstream_addr.hostport.c_str(), downstream_addr.hostport.size()); @@ -1351,7 +1351,7 @@ int Http2Session::connection_made() { } } - auto must_terminate = !get_config()->downstream_no_tls && + auto must_terminate = !get_config()->conn.downstream.no_tls && !nghttp2::ssl::check_http2_requirement(conn_.tls.ssl); if (must_terminate) { @@ -1719,7 +1719,7 @@ int Http2Session::tls_handshake() { SSLOG(INFO, this) << "SSL/TLS handshake completed"; } - if (!get_config()->downstream_no_tls && !get_config()->tls.insecure && + if (!get_config()->conn.downstream.no_tls && !get_config()->tls.insecure && check_cert() != 0) { return -1; } diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index f152434a..309100cd 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -852,9 +852,9 @@ nghttp2_session_callbacks *create_http2_upstream_callbacks() { Http2Upstream::Http2Upstream(ClientHandler *handler) : downstream_queue_( get_config()->http2_proxy - ? get_config()->downstream_connections_per_host - : get_config()->downstream_proto == PROTO_HTTP - ? get_config()->downstream_connections_per_frontend + ? get_config()->conn.downstream.connections_per_host + : get_config()->conn.downstream.proto == PROTO_HTTP + ? get_config()->conn.downstream.connections_per_frontend : 0, !get_config()->http2_proxy), pending_response_buf_(handler->get_worker()->get_mcpool()), @@ -914,7 +914,7 @@ Http2Upstream::Http2Upstream(ClientHandler *handler) ev_prepare_start(handler_->get_loop(), &prep_); handler_->reset_upstream_read_timeout( - get_config()->http2_upstream_read_timeout); + get_config()->conn.upstream.timeout.http2_read); handler_->signal_write(); } diff --git a/src/shrpx_http_downstream_connection.cc b/src/shrpx_http_downstream_connection.cc index 3f86ad6c..46d9a058 100644 --- a/src/shrpx_http_downstream_connection.cc +++ b/src/shrpx_http_downstream_connection.cc @@ -113,8 +113,9 @@ void connectcb(struct ev_loop *loop, ev_io *w, int revents) { HttpDownstreamConnection::HttpDownstreamConnection( DownstreamConnectionPool *dconn_pool, size_t group, struct ev_loop *loop) : DownstreamConnection(dconn_pool), - conn_(loop, -1, nullptr, nullptr, get_config()->downstream_write_timeout, - get_config()->downstream_read_timeout, 0, 0, 0, 0, connectcb, + conn_(loop, -1, nullptr, nullptr, + get_config()->conn.downstream.timeout.write, + get_config()->conn.downstream.timeout.read, {}, {}, connectcb, readcb, timeoutcb, this, get_config()->tls.dyn_rec.warmup_threshold, get_config()->tls.dyn_rec.idle_timeout), ioctrl_(&conn_.rlimit), response_htp_{0}, group_(group), addr_idx_(0), @@ -127,6 +128,8 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { DCLOG(INFO, this) << "Attaching to DOWNSTREAM:" << downstream; } + auto &downstreamconf = get_config()->conn.downstream; + if (conn_.fd == -1) { auto connect_blocker = client_handler_->get_connect_blocker(); @@ -141,7 +144,7 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { auto worker = client_handler_->get_worker(); auto &next_downstream = worker->get_dgrp(group_)->next; auto end = next_downstream; - auto &addrs = get_config()->downstream_addr_groups[group_].addrs; + auto &addrs = downstreamconf.addr_groups[group_].addrs; for (;;) { auto &addr = addrs[next_downstream]; auto i = next_downstream; @@ -196,7 +199,7 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { ev_timer_again(conn_.loop, &conn_.wt); } else { // we may set read timer cb to idle_timeoutcb. Reset again. - conn_.rt.repeat = get_config()->downstream_read_timeout; + conn_.rt.repeat = downstreamconf.timeout.read; ev_set_cb(&conn_.rt, timeoutcb); ev_timer_again(conn_.loop, &conn_.rt); ev_set_cb(&conn_.rev, readcb); @@ -211,8 +214,10 @@ int HttpDownstreamConnection::attach_downstream(Downstream *downstream) { } int HttpDownstreamConnection::push_request_headers() { - const auto &downstream_hostport = - get_config()->downstream_addr_groups[group_].addrs[addr_idx_].hostport; + const auto &downstream_hostport = get_config() + ->conn.downstream.addr_groups[group_] + .addrs[addr_idx_] + .hostport; const auto &req = downstream_->request(); auto connect_method = req.method == HTTP_CONNECT; @@ -479,7 +484,7 @@ void HttpDownstreamConnection::detach_downstream(Downstream *downstream) { ev_set_cb(&conn_.rev, idle_readcb); ioctrl_.force_resume_read(); - conn_.rt.repeat = get_config()->downstream_idle_read_timeout; + conn_.rt.repeat = get_config()->conn.downstream.timeout.idle_read; ev_set_cb(&conn_.rt, idle_timeoutcb); ev_timer_again(conn_.loop, &conn_.rt); @@ -494,7 +499,7 @@ void HttpDownstreamConnection::pause_read(IOCtrlReason reason) { int HttpDownstreamConnection::resume_read(IOCtrlReason reason, size_t consumed) { if (downstream_->get_response_buf()->rleft() <= - get_config()->downstream_request_buffer_size / 2) { + get_config()->conn.downstream.request_buffer_size / 2) { ioctrl_.resume_read(reason); } diff --git a/src/shrpx_memcached_connection.cc b/src/shrpx_memcached_connection.cc index e5e831ec..8bb01aab 100644 --- a/src/shrpx_memcached_connection.cc +++ b/src/shrpx_memcached_connection.cc @@ -92,7 +92,7 @@ constexpr ev_tstamp read_timeout = 10.; MemcachedConnection::MemcachedConnection(const Address *addr, struct ev_loop *loop) - : conn_(loop, -1, nullptr, nullptr, write_timeout, read_timeout, 0, 0, 0, 0, + : conn_(loop, -1, nullptr, nullptr, write_timeout, read_timeout, {}, {}, connectcb, readcb, timeoutcb, this, 0, 0.), parse_state_{}, addr_(addr), sendsum_(0), connected_(false) {} diff --git a/src/shrpx_spdy_upstream.cc b/src/shrpx_spdy_upstream.cc index 4d9cfa56..7fcdeba9 100644 --- a/src/shrpx_spdy_upstream.cc +++ b/src/shrpx_spdy_upstream.cc @@ -494,9 +494,9 @@ uint32_t infer_upstream_rst_stream_status_code(uint32_t downstream_error_code) { SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler) : downstream_queue_( get_config()->http2_proxy - ? get_config()->downstream_connections_per_host - : get_config()->downstream_proto == PROTO_HTTP - ? get_config()->downstream_connections_per_frontend + ? get_config()->conn.downstream.connections_per_host + : get_config()->conn.downstream.proto == PROTO_HTTP + ? get_config()->conn.downstream.connections_per_frontend : 0, !get_config()->http2_proxy), handler_(handler), session_(nullptr) { @@ -558,7 +558,7 @@ SpdyUpstream::SpdyUpstream(uint16_t version, ClientHandler *handler) } handler_->reset_upstream_read_timeout( - get_config()->http2_upstream_read_timeout); + get_config()->conn.upstream.timeout.http2_read); handler_->signal_write(); } diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 412071fd..b35f31cc 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -1214,7 +1214,7 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, neverbleed_t *nb #endif // HAVE_NEVERBLEED ) { - if (get_config()->upstream_no_tls) { + if (get_config()->conn.upstream.no_tls) { return nullptr; } @@ -1266,11 +1266,13 @@ SSL_CTX *setup_server_ssl_context(std::vector &all_ssl_ctx, } bool downstream_tls_enabled() { + auto no_tls = get_config()->conn.downstream.no_tls; + if (get_config()->client_mode) { - return !get_config()->downstream_no_tls; + return !no_tls; } - return get_config()->http2_bridge && !get_config()->downstream_no_tls; + return get_config()->http2_bridge && !no_tls; } SSL_CTX *setup_client_ssl_context( @@ -1290,7 +1292,8 @@ SSL_CTX *setup_client_ssl_context( } CertLookupTree *create_cert_lookup_tree() { - if (get_config()->upstream_no_tls || get_config()->tls.subcerts.empty()) { + if (get_config()->conn.upstream.no_tls || + get_config()->tls.subcerts.empty()) { return nullptr; } return new ssl::CertLookupTree(); diff --git a/src/shrpx_worker.cc b/src/shrpx_worker.cc index 65103c92..d6282d37 100644 --- a/src/shrpx_worker.cc +++ b/src/shrpx_worker.cc @@ -69,9 +69,10 @@ std::random_device rd; Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, ssl::CertLookupTree *cert_tree, const std::shared_ptr &ticket_keys) - : randgen_(rd()), dconn_pool_(get_config()->downstream_addr_groups.size()), - worker_stat_(get_config()->downstream_addr_groups.size()), - dgrps_(get_config()->downstream_addr_groups.size()), loop_(loop), + : randgen_(rd()), + dconn_pool_(get_config()->conn.downstream.addr_groups.size()), + worker_stat_(get_config()->conn.downstream.addr_groups.size()), + dgrps_(get_config()->conn.downstream.addr_groups.size()), loop_(loop), sv_ssl_ctx_(sv_ssl_ctx), cl_ssl_ctx_(cl_ssl_ctx), cert_tree_(cert_tree), ticket_keys_(ticket_keys), connect_blocker_(make_unique(loop_)), @@ -90,13 +91,15 @@ Worker::Worker(struct ev_loop *loop, SSL_CTX *sv_ssl_ctx, SSL_CTX *cl_ssl_ctx, &session_cacheconf.memcached.addr, loop); } - if (get_config()->downstream_proto == PROTO_HTTP2) { + auto &downstreamconf = get_config()->conn.downstream; + + if (downstreamconf.proto == PROTO_HTTP2) { auto n = get_config()->http2.downstream.connections_per_worker; size_t group = 0; for (auto &dgrp : dgrps_) { auto m = n; if (m == 0) { - m = get_config()->downstream_addr_groups[group].addrs.size(); + m = downstreamconf.addr_groups[group].addrs.size(); } for (size_t idx = 0; idx < m; ++idx) { dgrp.http2sessions.push_back(make_unique( @@ -151,6 +154,9 @@ void Worker::process_events() { std::lock_guard g(m_); q.swap(q_); } + + auto worker_connections = get_config()->conn.upstream.worker_connections; + for (auto &wev : q) { switch (wev.type) { case NEW_CONNECTION: { @@ -159,12 +165,10 @@ void Worker::process_events() { << ", addrlen=" << wev.client_addrlen; } - if (worker_stat_.num_connections >= - get_config()->worker_frontend_connections) { + if (worker_stat_.num_connections >= worker_connections) { if (LOG_ENABLED(INFO)) { - WLOG(INFO, this) << "Too many connections >= " - << get_config()->worker_frontend_connections; + WLOG(INFO, this) << "Too many connections >= " << worker_connections; } close(wev.client_fd); diff --git a/src/shrpx_worker_process.cc b/src/shrpx_worker_process.cc index 181e4c96..61e44cb0 100644 --- a/src/shrpx_worker_process.cc +++ b/src/shrpx_worker_process.cc @@ -423,8 +423,10 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { #endif // HAVE_NEVERBLEED + auto &upstreamconf = get_config()->conn.upstream; + ev_timer renew_ticket_key_timer; - if (!get_config()->upstream_no_tls) { + if (!upstreamconf.no_tls) { auto &ticketconf = get_config()->tls.ticket; if (ticketconf.memcached.host) { @@ -514,7 +516,7 @@ int worker_process_event_loop(WorkerProcessConfig *wpconf) { ipcev.data = &conn_handler; ev_io_start(loop, &ipcev); - if (!get_config()->upstream_no_tls && !get_config()->tls.ocsp.disabled) { + if (!upstreamconf.no_tls && !get_config()->tls.ocsp.disabled) { conn_handler.proceed_next_cert_ocsp(); } From 2f9946327a9c41b880b1b86f6ac98d1ff57585f4 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 19 Jan 2016 17:03:01 +0900 Subject: [PATCH 6/7] nghttpx: Fix bug that --listener-disable-timeout option is not used --- src/shrpx.cc | 1 + src/shrpx_accept_handler.cc | 2 +- src/shrpx_connection_handler.cc | 2 +- src/shrpx_connection_handler.h | 2 +- src/template.h | 8 ++++++++ 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index f1abba0d..ed6cf1f7 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -1032,6 +1032,7 @@ void fill_default_config() { // Default accept() backlog listenerconf.backlog = 512; listenerconf.host_unix = false; + listenerconf.timeout.sleep = 30_s; } } diff --git a/src/shrpx_accept_handler.cc b/src/shrpx_accept_handler.cc index 0d448306..418a3bf0 100644 --- a/src/shrpx_accept_handler.cc +++ b/src/shrpx_accept_handler.cc @@ -87,7 +87,7 @@ void AcceptHandler::accept_connection() { case ENFILE: LOG(WARN) << "acceptor: running out file descriptor; disable acceptor " "temporarily"; - conn_hnr_->disable_acceptor_temporary(30.); + conn_hnr_->sleep_acceptor(get_config()->conn.listener.timeout.sleep); break; } diff --git a/src/shrpx_connection_handler.cc b/src/shrpx_connection_handler.cc index 34b10996..1e6504b0 100644 --- a/src/shrpx_connection_handler.cc +++ b/src/shrpx_connection_handler.cc @@ -385,7 +385,7 @@ void ConnectionHandler::disable_acceptor() { } } -void ConnectionHandler::disable_acceptor_temporary(ev_tstamp t) { +void ConnectionHandler::sleep_acceptor(ev_tstamp t) { if (t == 0. || ev_is_active(&disable_acceptor_timer_)) { return; } diff --git a/src/shrpx_connection_handler.h b/src/shrpx_connection_handler.h index 36c5e40b..deec78c8 100644 --- a/src/shrpx_connection_handler.h +++ b/src/shrpx_connection_handler.h @@ -98,7 +98,7 @@ public: AcceptHandler *get_acceptor6() const; void enable_acceptor(); void disable_acceptor(); - void disable_acceptor_temporary(ev_tstamp t); + void sleep_acceptor(ev_tstamp t); void accept_pending_connection(); void graceful_shutdown_worker(); void set_graceful_shutdown(bool f); diff --git a/src/template.h b/src/template.h index feef5784..7eb11e71 100644 --- a/src/template.h +++ b/src/template.h @@ -171,10 +171,18 @@ constexpr unsigned long long operator"" _g(unsigned long long g) { // User-defined literals for time, converted into double in seconds +// hours constexpr double operator"" _h(unsigned long long h) { return h * 60 * 60; } +// minutes constexpr double operator"" _min(unsigned long long min) { return min * 60; } +// seconds +constexpr double operator"" _s(unsigned long long s) { return s; } + +// milliseconds +constexpr double operator"" _ms(unsigned long long ms) { return ms / 1000.; } + // Returns a copy of NULL-terminated string [first, last). template std::unique_ptr strcopy(InputIt first, InputIt last) { From f3946ae3143d7bc1d68156641c16d6cf56f112bc Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Tue, 19 Jan 2016 17:27:56 +0900 Subject: [PATCH 7/7] nghttpx: Remove assignment of zero values Because of zero initialization, these assignments are unnecessary. --- src/shrpx.cc | 60 +++++----------------------------------------------- 1 file changed, 5 insertions(+), 55 deletions(-) diff --git a/src/shrpx.cc b/src/shrpx.cc index ed6cf1f7..7529e09f 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -905,31 +905,14 @@ namespace { void fill_default_config() { *mod_config() = {}; - mod_config()->verbose = false; - mod_config()->daemon = false; - mod_config()->num_worker = 1; mod_config()->conf_path = strcopy("/etc/nghttpx/nghttpx.conf"); - mod_config()->http2_proxy = false; - mod_config()->http2_bridge = false; - mod_config()->client_proxy = false; - mod_config()->client = false; - mod_config()->client_mode = false; - mod_config()->pid_file = nullptr; - mod_config()->user = nullptr; - mod_config()->uid = 0; - mod_config()->gid = 0; mod_config()->pid = getpid(); - mod_config()->padding = 0; - - mod_config()->argc = 0; - mod_config()->argv = nullptr; auto &tlsconf = mod_config()->tls; { auto &ticketconf = tlsconf.ticket; ticketconf.cipher = EVP_aes_128_cbc(); - ticketconf.cipher_given = false; { auto &memcachedconf = ticketconf.memcached; @@ -943,45 +926,22 @@ void fill_default_config() { ocspconf.update_interval = 4_h; ocspconf.fetch_ocsp_response_file = strcopy(PKGDATADIR "/fetch-ocsp-response"); - ocspconf.disabled = false; - - auto &client_verify = tlsconf.client_verify; - client_verify.enabled = false; auto &dyn_recconf = tlsconf.dyn_rec; dyn_recconf.warmup_threshold = 1_m; - dyn_recconf.idle_timeout = 1.; + dyn_recconf.idle_timeout = 1_s; tlsconf.session_timeout = std::chrono::hours(12); - tlsconf.tls_proto_mask = 0; - tlsconf.insecure = false; } auto &httpconf = mod_config()->http; - { - auto &xffconf = httpconf.xff; - xffconf.add = false; - xffconf.strip_incoming = false; - } - httpconf.server_name = "nghttpx nghttp2/" NGHTTP2_VERSION; - httpconf.no_via = false; - httpconf.no_location_rewrite = false; httpconf.no_host_rewrite = true; httpconf.header_field_buffer = 64_k; httpconf.max_header_fields = 100; auto &http2conf = mod_config()->http2; { - auto &timeoutconf = http2conf.timeout; - { - // Read timeout for HTTP/2 stream - timeoutconf.stream_read = 0.; - - // Write timeout for HTTP/2 stream - timeoutconf.stream_write = 0.; - } - auto &upstreamconf = http2conf.upstream; // window bits for HTTP/2 and SPDY upstream connection per // stream. 2**16-1 = 64KiB-1, which is HTTP/2 default. Please note @@ -995,11 +955,11 @@ void fill_default_config() { nghttp2_option_set_no_auto_window_update(upstreamconf.option, 1); nghttp2_option_set_no_recv_client_magic(upstreamconf.option, 1); } + { auto &downstreamconf = http2conf.downstream; downstreamconf.window_bits = 16; downstreamconf.connection_window_bits = 16; - downstreamconf.connections_per_worker = 0; nghttp2_option_new(&downstreamconf.option); nghttp2_option_set_no_auto_window_update(downstreamconf.option, 1); @@ -1007,18 +967,14 @@ void fill_default_config() { } http2conf.max_concurrent_streams = 100; - http2conf.no_cookie_crumbling = false; - http2conf.no_server_push = false; auto &loggingconf = mod_config()->logging; { auto &accessconf = loggingconf.access; - accessconf.syslog = false; accessconf.format = parse_log_format(DEFAULT_ACCESSLOG_FORMAT); auto &errorconf = loggingconf.error; errorconf.file = strcopy("/dev/stderr"); - errorconf.syslog = false; } loggingconf.syslog_facility = LOG_DAEMON; @@ -1031,7 +987,6 @@ void fill_default_config() { listenerconf.port = 3000; // Default accept() backlog listenerconf.backlog = 512; - listenerconf.host_unix = false; listenerconf.timeout.sleep = 30_s; } } @@ -1047,10 +1002,8 @@ void fill_default_config() { timeoutconf.read = 1_min; // Write timeout for HTTP2/non-HTTP2 upstream connection - timeoutconf.write = 30.; + timeoutconf.write = 30_s; } - - upstreamconf.no_tls = false; } { @@ -1059,14 +1012,11 @@ void fill_default_config() { auto &timeoutconf = downstreamconf.timeout; // Read/Write timeouts for downstream connection timeoutconf.read = 1_min; - timeoutconf.write = 30.; + timeoutconf.write = 30_s; // Timeout for pooled (idle) connections - timeoutconf.idle_read = 2.; + timeoutconf.idle_read = 2_s; } - downstreamconf.no_tls = false; - downstreamconf.ipv4 = false; - downstreamconf.ipv6 = false; downstreamconf.connections_per_host = 8; downstreamconf.request_buffer_size = 16_k; downstreamconf.response_buffer_size = 16_k;