diff --git a/src/shrpx.cc b/src/shrpx.cc index 2ff9f241..b06938d8 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -318,6 +318,14 @@ bool conf_exists(const char *path) } } // namespace +namespace { +const char *DEFAULT_NPN_LIST = NGHTTP2_PROTO_VERSION_ID "," +#ifdef HAVE_SPDYLAY + "spdy/3,spdy/2," +#endif // HAVE_SPDYLAY + "http/1.1"; +} // namespace + namespace { void fill_default_config() { @@ -405,6 +413,7 @@ void fill_default_config() mod_config()->read_burst = 4*1024*1024; mod_config()->write_rate = 0; mod_config()->write_burst = 0; + mod_config()->npn_list = nullptr; } } // namespace @@ -573,7 +582,14 @@ void print_help(std::ostream& out) << " --dh-param-file=\n" << " Path to file that contains DH parameters in\n" << " PEM format. Without this option, DHE cipher\n" - << " suites are not available." + << " suites are not available.\n" + << " --npn-list= Comma delimited list of NPN protocol sorted\n" + << " in the order of preference. That means\n" + << " most desirable protocol comes first.\n" + << " The parameter must be delimited by a single\n" + << " comma only and any white spaces are treated\n" + << " as a part of protocol string.\n" + << " Default: " << DEFAULT_NPN_LIST << "\n" << "\n" << " HTTP/2.0 and SPDY:\n" << " -c, --spdy-max-concurrent-streams=\n" @@ -710,6 +726,7 @@ int main(int argc, char **argv) {"read-burst", required_argument, &flag, 35}, {"write-rate", required_argument, &flag, 36}, {"write-burst", required_argument, &flag, 37}, + {"npn-list", required_argument, &flag, 38}, {nullptr, 0, nullptr, 0 } }; int option_index = 0; @@ -909,6 +926,10 @@ int main(int argc, char **argv) // --write-burst cmdcfgs.push_back(std::make_pair(SHRPX_OPT_WRITE_BURST, optarg)); break; + case 38: + // --npn-list + cmdcfgs.push_back(std::make_pair(SHRPX_OPT_NPN_LIST, optarg)); + break; default: break; } @@ -947,6 +968,10 @@ int main(int argc, char **argv) } } + if(!get_config()->npn_list) { + parse_config_npn_list(DEFAULT_NPN_LIST); + } + if(get_config()->cert_file && get_config()->private_key_file) { mod_config()->default_ssl_ctx = ssl::create_ssl_context(get_config()->private_key_file, diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 1f2d36df..a4560d6b 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -99,6 +99,7 @@ const char SHRPX_OPT_READ_RATE[] = "read-rate"; const char SHRPX_OPT_READ_BURST[] = "read-burst"; const char SHRPX_OPT_WRITE_RATE[] = "write-rate"; const char SHRPX_OPT_WRITE_BURST[] = "write-burst"; +const char SHRPX_OPT_NPN_LIST[] = "npn-list"; namespace { Config *config = 0; @@ -194,6 +195,31 @@ void set_config_str(char **destp, const char *val) *destp = strdup(val); } +int parse_config_npn_list(const char *s) +{ + delete [] mod_config()->npn_list; + size_t len = 1; + for(const char *first = s, *p = nullptr; (p = strchr(first, ',')); + ++len, first = p + 1); + auto list = new char*[len]; + auto t = strdup(s); + auto first = t; + len = 0; + for(;;) { + auto p = strchr(first, ','); + if(p == nullptr) { + break; + } + list[len++] = first; + *p = '\0'; + first = p + 1; + } + list[len++] = first; + mod_config()->npn_list = list; + mod_config()->npn_list_len = len; + return 0; +} + int parse_config(const char *opt, const char *optarg) { char host[NI_MAXHOST]; @@ -385,6 +411,8 @@ int parse_config(const char *opt, const char *optarg) mod_config()->write_rate = strtoul(optarg, nullptr, 10); } else if(util::strieq(opt, SHRPX_OPT_WRITE_BURST)) { mod_config()->write_burst = strtoul(optarg, nullptr, 10); + } else if(util::strieq(opt, SHRPX_OPT_NPN_LIST)) { + parse_config_npn_list(optarg); } else if(util::strieq(opt, "conf")) { LOG(WARNING) << "conf is ignored"; } else { diff --git a/src/shrpx_config.h b/src/shrpx_config.h index 141d169f..91e323da 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -90,6 +90,7 @@ extern const char SHRPX_OPT_READ_RATE[]; extern const char SHRPX_OPT_READ_BURST[]; extern const char SHRPX_OPT_WRITE_RATE[]; extern const char SHRPX_OPT_WRITE_BURST[]; +extern const char SHRPX_OPT_NPN_LIST[]; union sockaddr_union { sockaddr sa; @@ -178,6 +179,11 @@ struct Config { size_t read_burst; size_t write_rate; size_t write_burst; + // Comma delimited list of NPN protocol strings in the order of + // preference. + char **npn_list; + // The number of elements in npn_list + size_t npn_list_len; }; const Config* get_config(); @@ -197,6 +203,14 @@ int load_config(const char *filename); // Read passwd from |filename| std::string read_passwd_from_file(const char *filename); +// Parses NPN protocol strings in |s| and stores the protocols list in +// mod_config()->npn_list and assigns the number of elements in +// mod_config()->npn_list_len. The |s| must be comma delimited list of +// protocol strings. The strings must be delimited by a single command +// and any white spaces around it are treated as a part of protocol +// strings. This function always succeeds and returns 0. +int parse_config_npn_list(const char *s); + // Copies NULL-terminated string |val| to |*destp|. If |*destp| is not // NULL, it is freed before copying. void set_config_str(char **destp, const char *val); diff --git a/src/shrpx_ssl.cc b/src/shrpx_ssl.cc index 15bbbf46..2aac7677 100644 --- a/src/shrpx_ssl.cc +++ b/src/shrpx_ssl.cc @@ -80,7 +80,7 @@ int verify_callback(int preverify_ok, X509_STORE_CTX *ctx) } // namespace namespace { -size_t set_npn_prefs(unsigned char *out, const char **protos, size_t len) +size_t set_npn_prefs(unsigned char *out, char **protos, size_t len) { unsigned char *ptr = out; size_t listlen = 0; @@ -220,14 +220,8 @@ SSL_CTX* create_ssl_context(const char *private_key_file, } SSL_CTX_set_tlsext_servername_callback(ssl_ctx, servername_callback); - - const char *protos[] = { NGHTTP2_PROTO_VERSION_ID, -#ifdef HAVE_SPDYLAY - "spdy/3", "spdy/2", -#endif // HAVE_SPDYLAY - "http/1.1" }; - auto proto_list_len = set_npn_prefs(proto_list, protos, - sizeof(protos)/sizeof(protos[0])); + auto proto_list_len = set_npn_prefs(proto_list, get_config()->npn_list, + get_config()->npn_list_len); next_proto.first = proto_list; next_proto.second = proto_list_len; SSL_CTX_set_next_protos_advertised_cb(ssl_ctx, next_proto_cb, &next_proto);