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/hash_policy.h"
16
#include "source/common/http/http1/settings.h"
17
#include "source/common/http/utility.h"
18
#include "source/common/protobuf/utility.h"
19
#include "source/common/router/config_impl.h"
20
#include "source/common/router/retry_policy_impl.h"
21

            
22
namespace Envoy {
23
namespace Extensions {
24
namespace Upstreams {
25
namespace Http {
26
namespace {
27

            
28
const envoy::config::core::v3::Http1ProtocolOptions&
29
13446
getHttpOptions(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
30
13446
  if (options.has_use_downstream_protocol_config()) {
31
9
    return options.use_downstream_protocol_config().http_protocol_options();
32
9
  }
33
13437
  if (options.has_auto_config()) {
34
68
    return options.auto_config().http_protocol_options();
35
68
  }
36
13369
  return options.explicit_http_config().http_protocol_options();
37
13437
}
38

            
39
const envoy::config::core::v3::Http2ProtocolOptions&
40
13456
getHttp2Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
41
13456
  if (options.has_use_downstream_protocol_config()) {
42
12
    return options.use_downstream_protocol_config().http2_protocol_options();
43
12
  }
44
13444
  if (options.has_auto_config()) {
45
71
    return options.auto_config().http2_protocol_options();
46
71
  }
47
13373
  return options.explicit_http_config().http2_protocol_options();
48
13444
}
49

            
50
const envoy::config::core::v3::Http3ProtocolOptions&
51
13446
getHttp3Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
52
13446
  if (options.has_use_downstream_protocol_config() &&
53
13446
      options.use_downstream_protocol_config().has_http3_protocol_options()) {
54
4
    return options.use_downstream_protocol_config().http3_protocol_options();
55
4
  }
56
13442
  if (options.has_auto_config()) {
57
68
    return options.auto_config().http3_protocol_options();
58
68
  }
59
13374
  return options.explicit_http_config().http3_protocol_options();
60
13442
}
61

            
62
13446
bool useHttp2(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
63
13446
  if (options.has_explicit_http_config() &&
64
13446
      options.explicit_http_config().has_http2_protocol_options()) {
65
9208
    return true;
66
11115
  } else if (options.has_use_downstream_protocol_config() &&
67
4238
             options.use_downstream_protocol_config().has_http2_protocol_options()) {
68
1
    return true;
69
4237
  } else if (options.has_auto_config()) {
70
68
    return true;
71
68
  }
72
4169
  return false;
73
13446
}
74

            
75
13446
bool useHttp3(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
76
13446
  if (options.has_explicit_http_config() &&
77
13446
      options.explicit_http_config().has_http3_protocol_options()) {
78
1024
    return true;
79
12591
  } else if (options.has_use_downstream_protocol_config() &&
80
12422
             options.use_downstream_protocol_config().has_http3_protocol_options()) {
81
4
    return true;
82
12418
  } else if (options.has_auto_config() && options.auto_config().has_http3_protocol_options()) {
83
34
    return true;
84
34
  }
85
12384
  return false;
86
13446
}
87

            
88
absl::StatusOr<absl::optional<const envoy::config::core::v3::AlternateProtocolsCacheOptions>>
89
getAlternateProtocolsCacheOptions(
90
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
91
13455
    Server::Configuration::ServerFactoryContext& server_context) {
92
13455
  if (options.has_auto_config() && options.auto_config().has_http3_protocol_options()) {
93
36
    if (!options.auto_config().has_alternate_protocols_cache_options()) {
94
1
      return absl::InvalidArgumentError(
95
1
          fmt::format("alternate protocols cache must be configured when HTTP/3 "
96
1
                      "is enabled with auto_config"));
97
1
    }
98
35
    auto cache_options = options.auto_config().alternate_protocols_cache_options();
99
35
    if (cache_options.has_key_value_store_config() && server_context.options().concurrency() != 1) {
100
1
      return absl::InvalidArgumentError(
101
1
          fmt::format("options has key value store but Envoy has concurrency = {} : {}",
102
1
                      server_context.options().concurrency(), cache_options.DebugString()));
103
1
    }
104

            
105
34
    return cache_options;
106
35
  }
107
13419
  return absl::nullopt;
108
13455
}
109

            
110
absl::StatusOr<Envoy::Http::HeaderValidatorFactoryPtr> createHeaderValidatorFactory(
111
    [[maybe_unused]] const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
112
13453
    [[maybe_unused]] Server::Configuration::ServerFactoryContext& server_context) {
113

            
114
13453
  Envoy::Http::HeaderValidatorFactoryPtr header_validator_factory;
115
#ifdef ENVOY_ENABLE_UHV
116
  if (!Runtime::runtimeFeatureEnabled(
117
          "envoy.reloadable_features.enable_universal_header_validator")) {
118
    // This will cause codecs to use legacy header validation and path normalization
119
    return nullptr;
120
  }
121
  ::envoy::config::core::v3::TypedExtensionConfig legacy_header_validator_config;
122
  if (!options.has_header_validation_config()) {
123
    // If header validator is not configured ensure that the defaults match Envoy's original
124
    // behavior.
125
    ::envoy::extensions::http::header_validators::envoy_default::v3::HeaderValidatorConfig
126
        uhv_config;
127
    uhv_config.mutable_http1_protocol_options()->set_allow_chunked_length(
128
        getHttpOptions(options).allow_chunked_length());
129
    legacy_header_validator_config.set_name("default_envoy_uhv_from_legacy_settings");
130
    legacy_header_validator_config.mutable_typed_config()->PackFrom(uhv_config);
131
  }
132

            
133
  const ::envoy::config::core::v3::TypedExtensionConfig& header_validator_config =
134
      options.has_header_validation_config() ? options.header_validation_config()
135
                                             : legacy_header_validator_config;
136

            
137
  auto* factory = Envoy::Config::Utility::getFactory<Envoy::Http::HeaderValidatorFactoryConfig>(
138
      header_validator_config);
139
  if (!factory) {
140
    return absl::InvalidArgumentError(
141
        fmt::format("Header validator extension not found: '{}'", header_validator_config.name()));
142
  }
143

            
144
  header_validator_factory =
145
      factory->createFromProto(header_validator_config.typed_config(), server_context);
146
  if (!header_validator_factory) {
147
    return absl::InvalidArgumentError(fmt::format(
148
        "Header validator extension could not be created: '{}'", header_validator_config.name()));
149
  }
150
#else
151
13453
  if (options.has_header_validation_config()) {
152
2
    return absl::InvalidArgumentError(
153
2
        fmt::format("This Envoy binary does not support header validator extensions: '{}'",
154
2
                    options.header_validation_config().name()));
155
2
  }
156

            
157
13451
  if (Runtime::runtimeFeatureEnabled(
158
13451
          "envoy.reloadable_features.enable_universal_header_validator")) {
159
5
    return absl::InvalidArgumentError(
160
5
        "Header validator can not be enabled since this Envoy binary does not support it.");
161
5
  }
162
13446
#endif
163
13446
  return header_validator_factory;
164
13451
}
165

            
166
} // namespace
167

            
168
uint64_t ProtocolOptionsConfigImpl::parseFeatures(const envoy::config::cluster::v3::Cluster& config,
169
18135
                                                  const ProtocolOptionsConfigImpl& options) {
170
18135
  uint64_t features = 0;
171

            
172
18135
  if (options.use_http2_) {
173
9282
    features |= Upstream::ClusterInfo::Features::HTTP2;
174
9282
  }
175
18135
  if (options.use_http3_) {
176
1061
    features |= Upstream::ClusterInfo::Features::HTTP3;
177
1061
  }
178
18135
  if (options.use_downstream_protocol_) {
179
12
    features |= Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL;
180
12
  }
181
18135
  if (options.use_alpn_) {
182
66
    features |= Upstream::ClusterInfo::Features::USE_ALPN;
183
66
  }
184
18135
  if (config.close_connections_on_host_health_failure()) {
185
3
    features |= Upstream::ClusterInfo::Features::CLOSE_CONNECTIONS_ON_HOST_HEALTH_FAILURE;
186
3
  }
187
18135
  return features;
188
18135
}
189

            
190
absl::StatusOr<std::vector<Envoy::Router::ShadowPolicyPtr>>
191
ProtocolOptionsConfigImpl::buildShadowPolicies(
192
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
193
13446
    Server::Configuration::ServerFactoryContext& server_context) {
194
13446
  std::vector<Envoy::Router::ShadowPolicyPtr> policies;
195
13446
  policies.reserve(options.request_mirror_policies().size());
196
13446
  for (const auto& mirror_policy_config : options.request_mirror_policies()) {
197
8
    auto policy_or_error =
198
8
        Envoy::Router::ShadowPolicyImpl::create(mirror_policy_config, server_context);
199
8
    RETURN_IF_NOT_OK_REF(policy_or_error.status());
200
8
    policies.push_back(std::move(policy_or_error.value()));
201
8
  }
202
13446
  return policies;
203
13446
}
204

            
205
absl::StatusOr<std::shared_ptr<const Envoy::Router::RetryPolicy>>
206
ProtocolOptionsConfigImpl::buildRetryPolicy(
207
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
208
    ProtobufMessage::ValidationVisitor& validation_visitor,
209
13446
    Server::Configuration::ServerFactoryContext& server_context) {
210
13446
  if (!options.has_retry_policy()) {
211
13433
    return nullptr;
212
13433
  }
213
13
  return Envoy::Router::RetryPolicyImpl::create(options.retry_policy(), validation_visitor,
214
13
                                                server_context);
215
13446
}
216

            
217
absl::StatusOr<std::unique_ptr<Envoy::Http::HashPolicy>> ProtocolOptionsConfigImpl::buildHashPolicy(
218
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
219
13446
    Server::Configuration::ServerFactoryContext& server_context) {
220
13446
  if (options.hash_policy().empty()) {
221
13440
    return nullptr;
222
13440
  }
223
6
  return Envoy::Http::HashPolicyImpl::create(options.hash_policy(), server_context.regexEngine());
224
13446
}
225

            
226
absl::StatusOr<std::shared_ptr<ProtocolOptionsConfigImpl>>
227
ProtocolOptionsConfigImpl::createProtocolOptionsConfig(
228
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
229
13456
    Server::Configuration::ServerFactoryContext& server_context) {
230
13456
  auto options_or_error = Http2::Utility::initializeAndValidateOptions(getHttp2Options(options));
231
13456
  RETURN_IF_NOT_OK_REF(options_or_error.status());
232

            
233
13456
  if (options_or_error.value().has_max_header_field_size_kb() &&
234
13456
      options.common_http_protocol_options().has_max_response_headers_kb() &&
235
13456
      options_or_error.value().max_header_field_size_kb().value() >
236
2
          options.common_http_protocol_options().max_response_headers_kb().value()) {
237
1
    return absl::InvalidArgumentError(
238
1
        "max_header_field_size_kb must not exceed max_response_headers_kb");
239
1
  }
240

            
241
13455
  auto cache_options_or_error = getAlternateProtocolsCacheOptions(options, server_context);
242
13455
  RETURN_IF_NOT_OK_REF(cache_options_or_error.status());
243
13453
  auto validator_factory_or_error = createHeaderValidatorFactory(options, server_context);
244
13453
  RETURN_IF_NOT_OK_REF(validator_factory_or_error.status());
245
13446
  auto shadow_policies_or_error = buildShadowPolicies(options, server_context);
246
13446
  RETURN_IF_NOT_OK_REF(shadow_policies_or_error.status());
247
13446
  auto retry_policy_or_error =
248
13446
      buildRetryPolicy(options, server_context.messageValidationVisitor(), server_context);
249
13446
  RETURN_IF_NOT_OK_REF(retry_policy_or_error.status());
250
13446
  auto hash_policy_or_error = buildHashPolicy(options, server_context);
251
13446
  RETURN_IF_NOT_OK_REF(hash_policy_or_error.status());
252
13446
  return std::shared_ptr<ProtocolOptionsConfigImpl>(new ProtocolOptionsConfigImpl(
253
13446
      options, options_or_error.value(), std::move(validator_factory_or_error.value()),
254
13446
      cache_options_or_error.value(), std::move(shadow_policies_or_error.value()),
255
13446
      std::move(retry_policy_or_error.value()), std::move(hash_policy_or_error.value()),
256
13446
      server_context));
257
13446
}
258

            
259
absl::StatusOr<std::shared_ptr<ProtocolOptionsConfigImpl>>
260
ProtocolOptionsConfigImpl::createProtocolOptionsConfig(
261
    const envoy::config::core::v3::Http1ProtocolOptions& http1_settings,
262
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
263
    const envoy::config::core::v3::HttpProtocolOptions& common_options,
264
    const absl::optional<envoy::config::core::v3::UpstreamHttpProtocolOptions> upstream_options,
265
    bool use_downstream_protocol, bool use_http2,
266
    Server::Configuration::ServerFactoryContext& server_context,
267
4697
    ProtobufMessage::ValidationVisitor& validation_visitor) {
268
4697
  auto options_or_error = Http2::Utility::initializeAndValidateOptions(http2_options);
269
4697
  RETURN_IF_NOT_OK_REF(options_or_error.status());
270

            
271
4695
  if (options_or_error.value().has_max_header_field_size_kb() &&
272
4695
      common_options.has_max_response_headers_kb() &&
273
4695
      options_or_error.value().max_header_field_size_kb().value() >
274
          common_options.max_response_headers_kb().value()) {
275
    return absl::InvalidArgumentError(
276
        "max_header_field_size_kb must not exceed max_response_headers_kb");
277
  }
278

            
279
4695
  return std::shared_ptr<ProtocolOptionsConfigImpl>(new ProtocolOptionsConfigImpl(
280
4695
      http1_settings, options_or_error.value(), common_options, upstream_options,
281
4695
      use_downstream_protocol, use_http2, server_context, validation_visitor));
282
4695
}
283

            
284
ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl(
285
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
286
    envoy::config::core::v3::Http2ProtocolOptions http2_options,
287
    Envoy::Http::HeaderValidatorFactoryPtr&& header_validator_factory,
288
    absl::optional<const envoy::config::core::v3::AlternateProtocolsCacheOptions> cache_options,
289
    std::vector<Envoy::Router::ShadowPolicyPtr>&& shadow_policies,
290
    std::shared_ptr<const Envoy::Router::RetryPolicy>&& retry_policy,
291
    std::unique_ptr<Envoy::Http::HashPolicy>&& hash_policy,
292
    Server::Configuration::ServerFactoryContext& server_context)
293
13446
    : http1_settings_(Envoy::Http::Http1::parseHttp1Settings(
294
13446
          getHttpOptions(options), server_context, server_context.messageValidationVisitor())),
295
13446
      http2_options_(std::move(http2_options)), http3_options_(getHttp3Options(options)),
296
13446
      common_http_protocol_options_(options.common_http_protocol_options()),
297
      upstream_http_protocol_options_(
298
13446
          options.has_upstream_http_protocol_options()
299
13446
              ? absl::make_optional<envoy::config::core::v3::UpstreamHttpProtocolOptions>(
300
1282
                    options.upstream_http_protocol_options())
301
13446
              : absl::nullopt),
302
13446
      http_filters_(options.http_filters()),
303
13446
      alternate_protocol_cache_options_(std::move(cache_options)),
304
13446
      header_validator_factory_(std::move(header_validator_factory)),
305
13446
      use_downstream_protocol_(options.has_use_downstream_protocol_config()),
306
13446
      use_http2_(useHttp2(options)), use_http3_(useHttp3(options)),
307
13446
      use_alpn_(options.has_auto_config()), shadow_policies_(std::move(shadow_policies)),
308
13446
      retry_policy_(std::move(retry_policy)), hash_policy_(std::move(hash_policy)) {
309
13446
  ASSERT(Http2::Utility::initializeAndValidateOptions(http2_options_).status().ok());
310
  // Build outlier detection config.
311

            
312
13446
  if (options.has_outlier_detection()) {
313
12
    buildMatcher(options.outlier_detection().error_matcher(), outlier_detection_http_error_matcher_,
314
12
                 server_context);
315
12
  }
316
13446
}
317

            
318
ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl(
319
    const envoy::config::core::v3::Http1ProtocolOptions& http1_settings,
320
    const envoy::config::core::v3::Http2ProtocolOptions& validated_http2_options,
321
    const envoy::config::core::v3::HttpProtocolOptions& common_options,
322
    const absl::optional<envoy::config::core::v3::UpstreamHttpProtocolOptions> upstream_options,
323
    bool use_downstream_protocol, bool use_http2,
324
    Server::Configuration::ServerFactoryContext& server_context,
325
    ProtobufMessage::ValidationVisitor& validation_visitor)
326
4695
    : http1_settings_(Envoy::Http::Http1::parseHttp1Settings(http1_settings, server_context,
327
4695
                                                             validation_visitor)),
328
4695
      http2_options_(validated_http2_options), common_http_protocol_options_(common_options),
329
4695
      upstream_http_protocol_options_(upstream_options),
330
4695
      use_downstream_protocol_(use_downstream_protocol), use_http2_(use_http2) {
331
4695
  ASSERT(Http2::Utility::initializeAndValidateOptions(validated_http2_options).status().ok());
332
4695
}
333

            
334
LEGACY_REGISTER_FACTORY(ProtocolOptionsConfigFactory, Server::Configuration::ProtocolOptionsFactory,
335
                        "envoy.upstreams.http.http_protocol_options");
336
} // namespace Http
337
} // namespace Upstreams
338
} // namespace Extensions
339
} // namespace Envoy