Coverage Report

Created: 2024-09-19 09:45

/proc/self/cwd/source/extensions/upstreams/http/config.cc
Line
Count
Source (jump to first uncovered line)
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
1.54k
getHttpOptions(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
27
1.54k
  if (options.has_use_downstream_protocol_config()) {
28
0
    return options.use_downstream_protocol_config().http_protocol_options();
29
0
  }
30
1.54k
  if (options.has_auto_config()) {
31
0
    return options.auto_config().http_protocol_options();
32
0
  }
33
1.54k
  return options.explicit_http_config().http_protocol_options();
34
1.54k
}
35
36
const envoy::config::core::v3::Http2ProtocolOptions&
37
1.54k
getHttp2Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
38
1.54k
  if (options.has_use_downstream_protocol_config()) {
39
0
    return options.use_downstream_protocol_config().http2_protocol_options();
40
0
  }
41
1.54k
  if (options.has_auto_config()) {
42
0
    return options.auto_config().http2_protocol_options();
43
0
  }
44
1.54k
  return options.explicit_http_config().http2_protocol_options();
45
1.54k
}
46
47
const envoy::config::core::v3::Http3ProtocolOptions&
48
1.54k
getHttp3Options(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
49
1.54k
  if (options.has_use_downstream_protocol_config() &&
50
1.54k
      options.use_downstream_protocol_config().has_http3_protocol_options()) {
51
0
    return options.use_downstream_protocol_config().http3_protocol_options();
52
0
  }
53
1.54k
  if (options.has_auto_config()) {
54
0
    return options.auto_config().http3_protocol_options();
55
0
  }
56
1.54k
  return options.explicit_http_config().http3_protocol_options();
57
1.54k
}
58
59
1.54k
bool useHttp2(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
60
1.54k
  if (options.has_explicit_http_config() &&
61
1.54k
      options.explicit_http_config().has_http2_protocol_options()) {
62
1.54k
    return true;
63
1.54k
  } 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
1.54k
}
71
72
1.54k
bool useHttp3(const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options) {
73
1.54k
  if (options.has_explicit_http_config() &&
74
1.54k
      options.explicit_http_config().has_http3_protocol_options()) {
75
0
    return true;
76
1.54k
  } else if (options.has_use_downstream_protocol_config() &&
77
1.54k
             options.use_downstream_protocol_config().has_http3_protocol_options()) {
78
0
    return true;
79
1.54k
  } else if (options.has_auto_config() && options.auto_config().has_http3_protocol_options()) {
80
0
    return true;
81
0
  }
82
1.54k
  return false;
83
1.54k
}
84
85
absl::StatusOr<absl::optional<const envoy::config::core::v3::AlternateProtocolsCacheOptions>>
86
getAlternateProtocolsCacheOptions(
87
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
88
1.54k
    Server::Configuration::ServerFactoryContext& server_context) {
89
1.54k
  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
      return absl::InvalidArgumentError(
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
      return absl::InvalidArgumentError(
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
1.54k
  return absl::nullopt;
105
1.54k
}
106
107
absl::StatusOr<Envoy::Http::HeaderValidatorFactoryPtr> createHeaderValidatorFactory(
108
    [[maybe_unused]] const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
109
1.54k
    [[maybe_unused]] Server::Configuration::ServerFactoryContext& server_context) {
110
111
1.54k
  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
    return absl::InvalidArgumentError(
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
    return absl::InvalidArgumentError(fmt::format(
145
        "Header validator extension could not be created: '{}'", header_validator_config.name()));
146
  }
147
#else
148
1.54k
  if (options.has_header_validation_config()) {
149
0
    return absl::InvalidArgumentError(
150
0
        fmt::format("This Envoy binary does not support header validator extensions: '{}'",
151
0
                    options.header_validation_config().name()));
152
0
  }
153
154
1.54k
  if (Runtime::runtimeFeatureEnabled(
155
1.54k
          "envoy.reloadable_features.enable_universal_header_validator")) {
156
0
    return absl::InvalidArgumentError(
157
0
        "Header validator can not be enabled since this Envoy binary does not support it.");
158
0
  }
159
1.54k
#endif
160
1.54k
  return header_validator_factory;
161
1.54k
}
162
163
} // namespace
164
165
uint64_t ProtocolOptionsConfigImpl::parseFeatures(const envoy::config::cluster::v3::Cluster& config,
166
2.40k
                                                  const ProtocolOptionsConfigImpl& options) {
167
2.40k
  uint64_t features = 0;
168
169
2.40k
  if (options.use_http2_) {
170
1.54k
    features |= Upstream::ClusterInfo::Features::HTTP2;
171
1.54k
  }
172
2.40k
  if (options.use_http3_) {
173
0
    features |= Upstream::ClusterInfo::Features::HTTP3;
174
0
  }
175
2.40k
  if (options.use_downstream_protocol_) {
176
0
    features |= Upstream::ClusterInfo::Features::USE_DOWNSTREAM_PROTOCOL;
177
0
  }
178
2.40k
  if (options.use_alpn_) {
179
0
    features |= Upstream::ClusterInfo::Features::USE_ALPN;
180
0
  }
181
2.40k
  if (config.close_connections_on_host_health_failure()) {
182
0
    features |= Upstream::ClusterInfo::Features::CLOSE_CONNECTIONS_ON_HOST_HEALTH_FAILURE;
183
0
  }
184
2.40k
  return features;
185
2.40k
}
186
187
absl::StatusOr<std::shared_ptr<ProtocolOptionsConfigImpl>>
188
ProtocolOptionsConfigImpl::createProtocolOptionsConfig(
189
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
190
1.54k
    Server::Configuration::ServerFactoryContext& server_context) {
191
1.54k
  auto options_or_error = Http2::Utility::initializeAndValidateOptions(getHttp2Options(options));
192
1.54k
  RETURN_IF_NOT_OK_REF(options_or_error.status());
193
1.54k
  auto cache_options_or_error = getAlternateProtocolsCacheOptions(options, server_context);
194
1.54k
  RETURN_IF_NOT_OK_REF(cache_options_or_error.status());
195
1.54k
  auto validator_factory_or_error = createHeaderValidatorFactory(options, server_context);
196
1.54k
  RETURN_IF_NOT_OK_REF(validator_factory_or_error.status());
197
1.54k
  return std::shared_ptr<ProtocolOptionsConfigImpl>(new ProtocolOptionsConfigImpl(
198
1.54k
      options, options_or_error.value(), std::move(validator_factory_or_error.value()),
199
1.54k
      cache_options_or_error.value(), server_context));
200
1.54k
}
201
202
absl::StatusOr<std::shared_ptr<ProtocolOptionsConfigImpl>>
203
ProtocolOptionsConfigImpl::createProtocolOptionsConfig(
204
    const envoy::config::core::v3::Http1ProtocolOptions& http1_settings,
205
    const envoy::config::core::v3::Http2ProtocolOptions& http2_options,
206
    const envoy::config::core::v3::HttpProtocolOptions& common_options,
207
    const absl::optional<envoy::config::core::v3::UpstreamHttpProtocolOptions> upstream_options,
208
    bool use_downstream_protocol, bool use_http2,
209
862
    ProtobufMessage::ValidationVisitor& validation_visitor) {
210
862
  auto options_or_error = Http2::Utility::initializeAndValidateOptions(http2_options);
211
862
  RETURN_IF_NOT_OK_REF(options_or_error.status());
212
862
  return std::shared_ptr<ProtocolOptionsConfigImpl>(new ProtocolOptionsConfigImpl(
213
862
      http1_settings, options_or_error.value(), common_options, upstream_options,
214
862
      use_downstream_protocol, use_http2, validation_visitor));
215
862
}
216
217
ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl(
218
    const envoy::extensions::upstreams::http::v3::HttpProtocolOptions& options,
219
    envoy::config::core::v3::Http2ProtocolOptions http2_options,
220
    Envoy::Http::HeaderValidatorFactoryPtr&& header_validator_factory,
221
    absl::optional<const envoy::config::core::v3::AlternateProtocolsCacheOptions> cache_options,
222
    Server::Configuration::ServerFactoryContext& server_context)
223
    : http1_settings_(Envoy::Http::Http1::parseHttp1Settings(
224
          getHttpOptions(options), server_context.messageValidationVisitor())),
225
      http2_options_(std::move(http2_options)), http3_options_(getHttp3Options(options)),
226
      common_http_protocol_options_(options.common_http_protocol_options()),
227
      upstream_http_protocol_options_(
228
          options.has_upstream_http_protocol_options()
229
              ? absl::make_optional<envoy::config::core::v3::UpstreamHttpProtocolOptions>(
230
                    options.upstream_http_protocol_options())
231
              : absl::nullopt),
232
      http_filters_(options.http_filters()),
233
      alternate_protocol_cache_options_(std::move(cache_options)),
234
      header_validator_factory_(std::move(header_validator_factory)),
235
      use_downstream_protocol_(options.has_use_downstream_protocol_config()),
236
      use_http2_(useHttp2(options)), use_http3_(useHttp3(options)),
237
1.54k
      use_alpn_(options.has_auto_config()) {
238
1.54k
  ASSERT(Http2::Utility::initializeAndValidateOptions(http2_options_).status().ok());
239
1.54k
}
240
241
ProtocolOptionsConfigImpl::ProtocolOptionsConfigImpl(
242
    const envoy::config::core::v3::Http1ProtocolOptions& http1_settings,
243
    const envoy::config::core::v3::Http2ProtocolOptions& validated_http2_options,
244
    const envoy::config::core::v3::HttpProtocolOptions& common_options,
245
    const absl::optional<envoy::config::core::v3::UpstreamHttpProtocolOptions> upstream_options,
246
    bool use_downstream_protocol, bool use_http2,
247
    ProtobufMessage::ValidationVisitor& validation_visitor)
248
    : http1_settings_(Envoy::Http::Http1::parseHttp1Settings(http1_settings, validation_visitor)),
249
      http2_options_(validated_http2_options), common_http_protocol_options_(common_options),
250
      upstream_http_protocol_options_(upstream_options),
251
862
      use_downstream_protocol_(use_downstream_protocol), use_http2_(use_http2) {
252
862
  ASSERT(Http2::Utility::initializeAndValidateOptions(validated_http2_options).status().ok());
253
862
}
254
255
LEGACY_REGISTER_FACTORY(ProtocolOptionsConfigFactory, Server::Configuration::ProtocolOptionsFactory,
256
                        "envoy.upstreams.http.http_protocol_options");
257
} // namespace Http
258
} // namespace Upstreams
259
} // namespace Extensions
260
} // namespace Envoy