Line data Source code
1 : #include "source/extensions/upstreams/http/config.h" 2 : 3 : #include <chrono> 4 : #include <memory> 5 : #include <string> 6 : #include <vector> 7 : 8 : #include "envoy/config/cluster/v3/cluster.pb.h" 9 : #include "envoy/config/core/v3/base.pb.h" 10 : #include "envoy/extensions/http/header_validators/envoy_default/v3/header_validator.pb.h" 11 : #include "envoy/http/header_validator_factory.h" 12 : #include "envoy/upstream/upstream.h" 13 : 14 : #include "source/common/config/utility.h" 15 : #include "source/common/http/http1/settings.h" 16 : #include "source/common/http/utility.h" 17 : #include "source/common/protobuf/utility.h" 18 : 19 : namespace Envoy { 20 : namespace Extensions { 21 : namespace Upstreams { 22 : namespace Http { 23 : namespace { 24 : 25 : const envoy::config::core::v3::Http1ProtocolOptions& 26 119 : getHttpOptions(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) { 27 119 : if (options.has_use_downstream_protocol_config()) { 28 0 : return options.use_downstream_protocol_config().http_protocol_options(); 29 0 : } 30 119 : if (options.has_auto_config()) { 31 0 : return options.auto_config().http_protocol_options(); 32 0 : } 33 119 : return options.explicit_http_config().http_protocol_options(); 34 119 : } 35 : 36 : const envoy::config::core::v3::Http2ProtocolOptions& 37 119 : getHttp2Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) { 38 119 : if (options.has_use_downstream_protocol_config()) { 39 0 : return options.use_downstream_protocol_config().http2_protocol_options(); 40 0 : } 41 119 : if (options.has_auto_config()) { 42 0 : return options.auto_config().http2_protocol_options(); 43 0 : } 44 119 : return options.explicit_http_config().http2_protocol_options(); 45 119 : } 46 : 47 : const envoy::config::core::v3::Http3ProtocolOptions& 48 119 : getHttp3Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) { 49 119 : if (options.has_use_downstream_protocol_config() && 50 119 : options.use_downstream_protocol_config().has_http3_protocol_options()) { 51 0 : return options.use_downstream_protocol_config().http3_protocol_options(); 52 0 : } 53 119 : if (options.has_auto_config()) { 54 0 : return options.auto_config().http3_protocol_options(); 55 0 : } 56 119 : return options.explicit_http_config().http3_protocol_options(); 57 119 : } 58 : 59 119 : bool useHttp2(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) { 60 119 : if (options.has_explicit_http_config() && 61 119 : options.explicit_http_config().has_http2_protocol_options()) { 62 119 : return true; 63 119 : } else if (options.has_use_downstream_protocol_config() && 64 0 : options.use_downstream_protocol_config().has_http2_protocol_options()) { 65 0 : return true; 66 0 : } else if (options.has_auto_config()) { 67 0 : return true; 68 0 : } 69 0 : return false; 70 119 : } 71 : 72 119 : bool useHttp3(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) { 73 119 : if (options.has_explicit_http_config() && 74 119 : options.explicit_http_config().has_http3_protocol_options()) { 75 0 : return true; 76 119 : } else if (options.has_use_downstream_protocol_config() && 77 119 : options.use_downstream_protocol_config().has_http3_protocol_options()) { 78 0 : return true; 79 119 : } else if (options.has_auto_config() && options.auto_config().has_http3_protocol_options()) { 80 0 : return true; 81 0 : } 82 119 : return false; 83 119 : } 84 : 85 : absl::optional<const envoy::config::core::v3::AlternateProtocolsCacheOptions> 86 : getAlternateProtocolsCacheOptions( 87 : const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options, 88 119 : Server::Configuration::ServerFactoryContext& server_context) { 89 119 : if (options.has_auto_config() && options.auto_config().has_http3_protocol_options()) { 90 0 : if (!options.auto_config().has_alternate_protocols_cache_options()) { 91 0 : throwEnvoyExceptionOrPanic( 92 0 : fmt::format("alternate protocols cache must be configured when HTTP/3 " 93 0 : "is enabled with auto_config")); 94 0 : } 95 0 : auto cache_options = options.auto_config().alternate_protocols_cache_options(); 96 0 : if (cache_options.has_key_value_store_config() && server_context.options().concurrency() != 1) { 97 0 : throwEnvoyExceptionOrPanic( 98 0 : fmt::format("options has key value store but Envoy has concurrency = {} : {}", 99 0 : server_context.options().concurrency(), cache_options.DebugString())); 100 0 : } 101 : 102 0 : return cache_options; 103 0 : } 104 119 : return absl::nullopt; 105 119 : } 106 : 107 : Envoy::Http::HeaderValidatorFactoryPtr createHeaderValidatorFactory( 108 : [[maybe_unused]] const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options, 109 119 : [[maybe_unused]] Server::Configuration::ServerFactoryContext& server_context) { 110 : 111 119 : Envoy::Http::HeaderValidatorFactoryPtr header_validator_factory; 112 : #ifdef ENVOY_ENABLE_UHV 113 : if (!Runtime::runtimeFeatureEnabled( 114 : "envoy.reloadable_features.enable_universal_header_validator")) { 115 : // This will cause codecs to use legacy header validation and path normalization 116 : return nullptr; 117 : } 118 : ::envoy::config::core::v3::TypedExtensionConfig legacy_header_validator_config; 119 : if (!options.has_header_validation_config()) { 120 : // If header validator is not configured ensure that the defaults match Envoy's original 121 : // behavior. 122 : ::envoy::extensions::http::header_validators::envoy_default::v3::HeaderValidatorConfig 123 : uhv_config; 124 : uhv_config.mutable_http1_protocol_options()->set_allow_chunked_length( 125 : getHttpOptions(options).allow_chunked_length()); 126 : legacy_header_validator_config.set_name("default_envoy_uhv_from_legacy_settings"); 127 : legacy_header_validator_config.mutable_typed_config()->PackFrom(uhv_config); 128 : } 129 : 130 : const ::envoy::config::core::v3::TypedExtensionConfig& header_validator_config = 131 : options.has_header_validation_config() ? options.header_validation_config() 132 : : legacy_header_validator_config; 133 : 134 : auto* factory = Envoy::Config::Utility::getFactory<Envoy::Http::HeaderValidatorFactoryConfig>( 135 : header_validator_config); 136 : if (!factory) { 137 : throwEnvoyExceptionOrPanic( 138 : fmt::format("Header validator extension not found: '{}'", header_validator_config.name())); 139 : } 140 : 141 : header_validator_factory = 142 : factory->createFromProto(header_validator_config.typed_config(), server_context); 143 : if (!header_validator_factory) { 144 : throwEnvoyExceptionOrPanic(fmt::format("Header validator extension could not be created: '{}'", 145 : header_validator_config.name())); 146 : } 147 : #else 148 119 : if (options.has_header_validation_config()) { 149 0 : throwEnvoyExceptionOrPanic( 150 0 : fmt::format("This Envoy binary does not support header validator extensions: '{}'", 151 0 : options.header_validation_config().name())); 152 0 : } 153 : 154 119 : if (Runtime::runtimeFeatureEnabled( 155 119 : "envoy.reloadable_features.enable_universal_header_validator")) { 156 0 : throwEnvoyExceptionOrPanic( 157 0 : "Header validator can not be enabled since this Envoy binary does not support it."); 158 0 : } 159 119 : #endif 160 119 : return header_validator_factory; 161 119 : } 162 : 163 : } // namespace 164 : 165 : uint64_t ProtocolOptionsConfigImpl::parseFeatures(const envoy::config::cluster::v3::Cluster& config, 166 159 : const ProtocolOptionsConfigImpl& options) { 167 159 : uint64_t features = 0; 168 : 169 159 : if (options.use_http2_) { 170 119 : features |= Upstream::ClusterInfo::Features::HTTP2; 171 119 : } 172 159 : if (options.use_http3_) { 173 0 : features |= Upstream::ClusterInfo::Features::HTTP3; 174 0 : } 175 159 : if (options.use_downstream_protocol_) { 176 0 : features |= Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL; 177 0 : } 178 159 : if (options.use_alpn_) { 179 0 : features |= Upstream::ClusterInfo::Features::USE_ALPN; 180 0 : } 181 159 : if (config.close_connections_on_host_health_failure()) { 182 0 : features |= Upstream::ClusterInfo::Features::CLOSE_CONNECTIONS_ON_HOST_HEALTH_FAILURE; 183 0 : } 184 159 : return features; 185 159 : } 186 : 187 : ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl( 188 : const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options, 189 : Server::Configuration::ServerFactoryContext& server_context) 190 : : http1_settings_(Envoy::Http::Http1::parseHttp1Settings( 191 : getHttpOptions(options), server_context.messageValidationVisitor())), 192 : http2_options_(Http2::Utility::initializeAndValidateOptions(getHttp2Options(options))), 193 : http3_options_(getHttp3Options(options)), 194 : common_http_protocol_options_(options.common_http_protocol_options()), 195 : upstream_http_protocol_options_( 196 : options.has_upstream_http_protocol_options() 197 : ? absl::make_optional<envoy::config::core::v3::UpstreamHttpProtocolOptions>( 198 : options.upstream_http_protocol_options()) 199 : : absl::nullopt), 200 : http_filters_(options.http_filters()), 201 : alternate_protocol_cache_options_(getAlternateProtocolsCacheOptions(options, server_context)), 202 : header_validator_factory_(createHeaderValidatorFactory(options, server_context)), 203 : use_downstream_protocol_(options.has_use_downstream_protocol_config()), 204 : use_http2_(useHttp2(options)), use_http3_(useHttp3(options)), 205 119 : use_alpn_(options.has_auto_config()) {} 206 : 207 : ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl( 208 : const envoy::config::core::v3::Http1ProtocolOptions& http1_settings, 209 : const envoy::config::core::v3::Http2ProtocolOptions& http2_options, 210 : const envoy::config::core::v3::HttpProtocolOptions& common_options, 211 : const absl::optional<envoy::config::core::v3::UpstreamHttpProtocolOptions> upstream_options, 212 : bool use_downstream_protocol, bool use_http2, 213 : ProtobufMessage::ValidationVisitor& validation_visitor) 214 : : http1_settings_(Envoy::Http::Http1::parseHttp1Settings(http1_settings, validation_visitor)), 215 : http2_options_(Http2::Utility::initializeAndValidateOptions(http2_options)), 216 : common_http_protocol_options_(common_options), 217 : upstream_http_protocol_options_(upstream_options), 218 40 : use_downstream_protocol_(use_downstream_protocol), use_http2_(use_http2) {} 219 : 220 : LEGACY_REGISTER_FACTORY(ProtocolOptionsConfigFactory, Server::Configuration::ProtocolOptionsFactory, 221 : "envoy.upstreams.http.http_protocol_options"); 222 : } // namespace Http 223 : } // namespace Upstreams 224 : } // namespace Extensions 225 : } // namespace Envoy