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
13453
getHttpOptions(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
30
13453
  if (options.has_use_downstream_protocol_config()) {
31
9
    return options.use_downstream_protocol_config().http_protocol_options();
32
9
  }
33
13444
  if (options.has_auto_config()) {
34
68
    return options.auto_config().http_protocol_options();
35
68
  }
36
13376
  return options.explicit_http_config().http_protocol_options();
37
13444
}
38

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

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

            
62
13453
bool useHttp2(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
63
13453
  if (options.has_explicit_http_config() &&
64
13453
      options.explicit_http_config().has_http2_protocol_options()) {
65
9215
    return true;
66
11122
  } 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
13453
}
74

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

            
88
absl::StatusOr<absl::optional<const envoy::config::core::v3::AlternateProtocolsCacheOptions>>
89
getAlternateProtocolsCacheOptions(
90
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
91
13462
    Server::Configuration::ServerFactoryContext& server_context) {
92
13462
  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
13426
  return absl::nullopt;
108
13462
}
109

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

            
114
13460
  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
13460
  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
13458
  if (Runtime::runtimeFeatureEnabled(
158
13458
          "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
13453
#endif
163
13453
  return header_validator_factory;
164
13458
}
165

            
166
} // namespace
167

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

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

            
190
absl::StatusOr<std::vector<Envoy::Router::ShadowPolicyPtr>>
191
ProtocolOptionsConfigImpl::buildShadowPolicies(
192
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
193
13453
    Server::Configuration::ServerFactoryContext& server_context) {
194
13453
  std::vector<Envoy::Router::ShadowPolicyPtr> policies;
195
13453
  policies.reserve(options.request_mirror_policies().size());
196
13453
  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
13453
  return policies;
203
13453
}
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
13453
    Server::Configuration::ServerFactoryContext& server_context) {
210
13453
  if (!options.has_retry_policy()) {
211
13440
    return nullptr;
212
13440
  }
213
13
  return Envoy::Router::RetryPolicyImpl::create(options.retry_policy(), validation_visitor,
214
13
                                                server_context);
215
13453
}
216

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

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

            
233
13463
  if (options_or_error.value().has_max_header_field_size_kb() &&
234
13463
      options.common_http_protocol_options().has_max_response_headers_kb() &&
235
13463
      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
13462
  auto cache_options_or_error = getAlternateProtocolsCacheOptions(options, server_context);
242
13462
  RETURN_IF_NOT_OK_REF(cache_options_or_error.status());
243
13460
  auto validator_factory_or_error = createHeaderValidatorFactory(options, server_context);
244
13460
  RETURN_IF_NOT_OK_REF(validator_factory_or_error.status());
245
13453
  auto shadow_policies_or_error = buildShadowPolicies(options, server_context);
246
13453
  RETURN_IF_NOT_OK_REF(shadow_policies_or_error.status());
247
13453
  auto retry_policy_or_error =
248
13453
      buildRetryPolicy(options, server_context.messageValidationVisitor(), server_context);
249
13453
  RETURN_IF_NOT_OK_REF(retry_policy_or_error.status());
250
13453
  auto hash_policy_or_error = buildHashPolicy(options, server_context);
251
13453
  RETURN_IF_NOT_OK_REF(hash_policy_or_error.status());
252
13453
  return std::shared_ptr<ProtocolOptionsConfigImpl>(new ProtocolOptionsConfigImpl(
253
13453
      options, options_or_error.value(), std::move(validator_factory_or_error.value()),
254
13453
      cache_options_or_error.value(), std::move(shadow_policies_or_error.value()),
255
13453
      std::move(retry_policy_or_error.value()), std::move(hash_policy_or_error.value()),
256
13453
      server_context));
257
13453
}
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
4735
    ProtobufMessage::ValidationVisitor& validation_visitor) {
268
4735
  auto options_or_error = Http2::Utility::initializeAndValidateOptions(http2_options);
269
4735
  RETURN_IF_NOT_OK_REF(options_or_error.status());
270

            
271
4733
  if (options_or_error.value().has_max_header_field_size_kb() &&
272
4733
      common_options.has_max_response_headers_kb() &&
273
4733
      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
4733
  return std::shared_ptr<ProtocolOptionsConfigImpl>(new ProtocolOptionsConfigImpl(
280
4733
      http1_settings, options_or_error.value(), common_options, upstream_options,
281
4733
      use_downstream_protocol, use_http2, server_context, validation_visitor));
282
4733
}
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
13453
    : http1_settings_(Envoy::Http::Http1::parseHttp1Settings(
294
13453
          getHttpOptions(options), server_context, server_context.messageValidationVisitor())),
295
13453
      http2_options_(std::move(http2_options)), http3_options_(getHttp3Options(options)),
296
13453
      common_http_protocol_options_(options.common_http_protocol_options()),
297
      upstream_http_protocol_options_(
298
13453
          options.has_upstream_http_protocol_options()
299
13453
              ? absl::make_optional<envoy::config::core::v3::UpstreamHttpProtocolOptions>(
300
1282
                    options.upstream_http_protocol_options())
301
13453
              : absl::nullopt),
302
13453
      http_filters_(options.http_filters()),
303
13453
      alternate_protocol_cache_options_(std::move(cache_options)),
304
13453
      header_validator_factory_(std::move(header_validator_factory)),
305
13453
      use_downstream_protocol_(options.has_use_downstream_protocol_config()),
306
13453
      use_http2_(useHttp2(options)), use_http3_(useHttp3(options)),
307
13453
      use_alpn_(options.has_auto_config()), shadow_policies_(std::move(shadow_policies)),
308
13453
      retry_policy_(std::move(retry_policy)), hash_policy_(std::move(hash_policy)) {
309
13453
  ASSERT(Http2::Utility::initializeAndValidateOptions(http2_options_).status().ok());
310
  // Build outlier detection config.
311

            
312
13453
  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
13453
}
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
4733
    : http1_settings_(Envoy::Http::Http1::parseHttp1Settings(http1_settings, server_context,
327
4733
                                                             validation_visitor)),
328
4733
      http2_options_(validated_http2_options), common_http_protocol_options_(common_options),
329
4733
      upstream_http_protocol_options_(upstream_options),
330
4733
      use_downstream_protocol_(use_downstream_protocol), use_http2_(use_http2) {
331
4733
  ASSERT(Http2::Utility::initializeAndValidateOptions(validated_http2_options).status().ok());
332
4733
}
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