diff --git a/src/shrpx.cc b/src/shrpx.cc index 26ec33fe..a6eec5ce 100644 --- a/src/shrpx.cc +++ b/src/shrpx.cc @@ -444,14 +444,6 @@ void fill_default_config() mod_config()->upstream_frame_debug = false; mod_config()->padding = 0; - mod_config()->altsvc_port = 0; - mod_config()->altsvc_protocol_id = nullptr; - mod_config()->altsvc_protocol_id_len = 0; - mod_config()->altsvc_host = nullptr; - mod_config()->altsvc_host_len = 0; - mod_config()->altsvc_origin = nullptr; - mod_config()->altsvc_origin_len = 0; - nghttp2_option_new(&mod_config()->http2_option); nghttp2_option_set_no_auto_stream_window_update @@ -764,19 +756,13 @@ Misc: downstream request. --no-via Don't append to Via header field. If Via header field is received, it is left unaltered. - --altsvc-port= - Port number of alternative service advertised in - alt-svc header field or HTTP/2 ALTSVC frame. - --altsvc-protocol-id= - ALPN protocol identifier of alternative service - advertised in alt-svc header field or HTTP/2 - ALTSVC frame. - --altsvc-host= - Host name that alternative service is available - upon, which is advertised in HTTP/2 ALTSVC frame. - --altsvc-origin= - Origin that alternative service is applicable to, - which is advertised in HTTP/2 ALTSVC frame. + --altsvc= + Specify protocol ID, port, host and origin of + alternative service. and are + optional. They are advertised in alt-svc header + field or HTTP/2 ALTSVC frame. This option can be + used multiple times to specify multiple + alternative services. Example: --altsvc=h2,443 --frontend-http2-dump-request-header= Dumps request headers received by HTTP/2 frontend to the file denoted in . The output is @@ -883,10 +869,7 @@ int main(int argc, char **argv) {"worker-read-burst", required_argument, &flag, 51}, {"worker-write-rate", required_argument, &flag, 52}, {"worker-write-burst", required_argument, &flag, 53}, - {"altsvc-port", required_argument, &flag, 54}, - {"altsvc-protocol-id", required_argument, &flag, 55}, - {"altsvc-host", required_argument, &flag, 56}, - {"altsvc-origin", required_argument, &flag, 57}, + {"altsvc", required_argument, &flag, 54}, {nullptr, 0, nullptr, 0 } }; @@ -1144,20 +1127,8 @@ int main(int argc, char **argv) cmdcfgs.emplace_back(SHRPX_OPT_WORKER_WRITE_BURST, optarg); break; case 54: - // --altsvc-port - cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC_PORT, optarg); - break; - case 55: - // --altsvc-protocol-id - cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC_PROTOCOL_ID, optarg); - break; - case 56: - // --altsvc-host - cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC_HOST, optarg); - break; - case 57: - // --altsvc-origin - cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC_ORIGIN, optarg); + // --altsvc + cmdcfgs.emplace_back(SHRPX_OPT_ALTSVC, optarg); break; default: break; diff --git a/src/shrpx_config.cc b/src/shrpx_config.cc index 54a73d52..b9305782 100644 --- a/src/shrpx_config.cc +++ b/src/shrpx_config.cc @@ -121,10 +121,7 @@ const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[] = const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[] = "http2-no-cookie-crumbling"; const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[] = "frontend-frame-debug"; const char SHRPX_OPT_PADDING[] = "padding"; -const char SHRPX_OPT_ALTSVC_PORT[] = "altsvc-port"; -const char SHRPX_OPT_ALTSVC_PROTOCOL_ID[] = "altsvc-protocol-id"; -const char SHRPX_OPT_ALTSVC_HOST[] = "altsvc-host"; -const char SHRPX_OPT_ALTSVC_ORIGIN[] = "altsvc-origin"; +const char SHRPX_OPT_ALTSVC[] = "altsvc"; namespace { Config *config = nullptr; @@ -502,32 +499,50 @@ int parse_config(const char *opt, const char *optarg) mod_config()->upstream_frame_debug = util::strieq(optarg, "yes"); } else if(util::strieq(opt, SHRPX_OPT_PADDING)) { mod_config()->padding = strtoul(optarg, nullptr, 10); - } else if(util::strieq(opt, SHRPX_OPT_ALTSVC_PORT)) { - errno = 0; + } else if(util::strieq(opt, SHRPX_OPT_ALTSVC)) { + size_t len; - auto port = strtoul(optarg, nullptr, 10); + auto tokens = parse_config_str_list(&len, optarg); - if(errno == 0 && - 1 <= port && port <= std::numeric_limits::max()) { - - mod_config()->altsvc_port = port; - } else { - LOG(ERROR) << "altsvc-port is invalid: " << optarg; + if(len < 2) { + // Requires at least protocol_id and port + LOG(ERROR) << "altsvc: too few parameters: " << optarg; return -1; } - } else if(util::strieq(opt, SHRPX_OPT_ALTSVC_PROTOCOL_ID)) { - set_config_str(&mod_config()->altsvc_protocol_id, optarg); - mod_config()->altsvc_protocol_id_len = - strlen(get_config()->altsvc_protocol_id); - } else if(util::strieq(opt, SHRPX_OPT_ALTSVC_HOST)) { - set_config_str(&mod_config()->altsvc_host, optarg); + if(len > 4) { + // We only need protocol_id, port, host and origin + LOG(ERROR) << "altsvc: too many parameters: " << optarg; + return -1; + } - mod_config()->altsvc_host_len = strlen(get_config()->altsvc_host); - } else if(util::strieq(opt, SHRPX_OPT_ALTSVC_ORIGIN)) { - set_config_str(&mod_config()->altsvc_origin, optarg); + errno = 0; + auto port = strtoul(tokens[1], nullptr, 10); + + if(errno != 0 || port < 1 || port > std::numeric_limits::max()) { + LOG(ERROR) << "altsvc: port is invalid: " << tokens[1]; + return -1; + } + + AltSvc altsvc; + + altsvc.port = port; + + altsvc.protocol_id = tokens[0]; + altsvc.protocol_id_len = strlen(altsvc.protocol_id); + + if(len > 2) { + altsvc.host = tokens[2]; + altsvc.host_len = strlen(altsvc.host); + + if(len > 3) { + altsvc.origin = tokens[3]; + altsvc.origin_len = strlen(altsvc.origin); + } + } + + mod_config()->altsvcs.push_back(std::move(altsvc)); - mod_config()->altsvc_origin_len = strlen(get_config()->altsvc_origin); } 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 79a2f63c..31e6ef10 100644 --- a/src/shrpx_config.h +++ b/src/shrpx_config.h @@ -110,10 +110,7 @@ extern const char SHRPX_OPT_FRONTEND_HTTP2_DUMP_RESPONSE_HEADER[]; extern const char SHRPX_OPT_HTTP2_NO_COOKIE_CRUMBLING[]; extern const char SHRPX_OPT_FRONTEND_FRAME_DEBUG[]; extern const char SHRPX_OPT_PADDING[]; -extern const char SHRPX_OPT_ALTSVC_PORT[]; -extern const char SHRPX_OPT_ALTSVC_PROTOCOL_ID[]; -extern const char SHRPX_OPT_ALTSVC_HOST[]; -extern const char SHRPX_OPT_ALTSVC_ORIGIN[]; +extern const char SHRPX_OPT_ALTSVC[]; union sockaddr_union { sockaddr sa; @@ -127,9 +124,32 @@ enum shrpx_proto { PROTO_HTTP }; +struct AltSvc { + AltSvc() + : protocol_id(nullptr), + host(nullptr), + origin(nullptr), + protocol_id_len(0), + host_len(0), + origin_len(0), + port(0) + {} + + char *protocol_id; + char *host; + char *origin; + + size_t protocol_id_len; + size_t host_len; + size_t origin_len; + + uint16_t port; +}; + struct Config { // The list of (private key file, certificate file) pair std::vector> subcerts; + std::vector altsvcs; sockaddr_union downstream_addr; // binary form of http proxy host and port sockaddr_union downstream_http_proxy_addr; @@ -176,9 +196,6 @@ struct Config { char *client_cert_file; FILE *http2_upstream_dump_request_header; FILE *http2_upstream_dump_response_header; - char *altsvc_protocol_id; - char *altsvc_host; - char *altsvc_origin; nghttp2_option *http2_option; size_t downstream_addrlen; size_t num_worker; @@ -202,9 +219,6 @@ struct Config { // The number of elements in tls_proto_list size_t tls_proto_list_len; size_t padding; - size_t altsvc_protocol_id_len; - size_t altsvc_host_len; - size_t altsvc_origin_len; // downstream protocol; this will be determined by given options. shrpx_proto downstream_proto; int syslog_facility; @@ -215,7 +229,6 @@ struct Config { uint16_t downstream_port; // port in http proxy URI uint16_t downstream_http_proxy_port; - uint16_t altsvc_port; bool verbose; bool daemon; bool verify_client; diff --git a/src/shrpx_http2_upstream.cc b/src/shrpx_http2_upstream.cc index 4eef635d..da9eadaf 100644 --- a/src/shrpx_http2_upstream.cc +++ b/src/shrpx_http2_upstream.cc @@ -547,23 +547,25 @@ Http2Upstream::Http2Upstream(ClientHandler *handler) } } - if(get_config()->altsvc_port != 0 && get_config()->altsvc_protocol_id) { + if(!get_config()->altsvcs.empty()) { // Set max_age to 24hrs, which is default for alt-svc header // field. - rv = nghttp2_submit_altsvc - (session_, NGHTTP2_FLAG_NONE, 0, - 86400, - get_config()->altsvc_port, - reinterpret_cast(get_config()->altsvc_protocol_id), - get_config()->altsvc_protocol_id_len, - reinterpret_cast(get_config()->altsvc_host), - get_config()->altsvc_host_len, - reinterpret_cast(get_config()->altsvc_origin), - get_config()->altsvc_origin_len); + for(auto& altsvc : get_config()->altsvcs) { + rv = nghttp2_submit_altsvc + (session_, NGHTTP2_FLAG_NONE, 0, + 86400, + altsvc.port, + reinterpret_cast(altsvc.protocol_id), + altsvc.protocol_id_len, + reinterpret_cast(altsvc.host), + altsvc.host_len, + reinterpret_cast(altsvc.origin), + altsvc.origin_len); - if(rv != 0) { - ULOG(ERROR, this) << "nghttp2_submit_altsvc() returned error: " - << nghttp2_strerror(rv); + if(rv != 0) { + ULOG(ERROR, this) << "nghttp2_submit_altsvc() returned error: " + << nghttp2_strerror(rv); + } } } } diff --git a/src/shrpx_https_upstream.cc b/src/shrpx_https_upstream.cc index 97fd6c1b..f7a606fd 100644 --- a/src/shrpx_https_upstream.cc +++ b/src/shrpx_https_upstream.cc @@ -681,12 +681,18 @@ int HttpsUpstream::on_downstream_header_complete(Downstream *downstream) if(downstream->get_norm_response_header("alt-svc") == end_headers) { // We won't change or alter alt-svc from backend at the moment. - if(get_config()->altsvc_port != 0 && get_config()->altsvc_protocol_id) { + if(!get_config()->altsvcs.empty()) { hdrs += "Alt-Svc: "; - hdrs += util::percent_encode_token(get_config()->altsvc_protocol_id); - hdrs += "="; - hdrs += util::utos(get_config()->altsvc_port); - hdrs += "\r\n"; + + for(auto& altsvc : get_config()->altsvcs) { + hdrs += util::percent_encode_token(altsvc.protocol_id); + hdrs += "="; + hdrs += util::utos(altsvc.port); + hdrs += ", "; + } + + hdrs[hdrs.size() - 2] = '\r'; + hdrs[hdrs.size() - 1] = '\n'; } }