Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ extensions/filters/common/original_src @snowp @klarose
/*/extensions/transport_sockets/common @alyssawilk @wez470
# starttls transport socket
/*/extensions/transport_sockets/starttls @cpakulski @lizan
# proxy transport socket
/*extensions/transport_sockets/http_11_proxy @alyssawilk @ryantheoptimist
# sni_cluster extension
/*/extensions/filters/network/sni_cluster @rshriram @lizan
# sni_dynamic_forward_proxy extension
Expand Down
12 changes: 12 additions & 0 deletions api/envoy/extensions/transport_sockets/http_11_proxy/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# DO NOT EDIT. This file is generated by tools/proto_format/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/config/core/v3:pkg",
"@com_github_cncf_udpa//udpa/annotations:pkg",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
syntax = "proto3";

package envoy.extensions.transport_sockets.http_11_proxy.v3;

import "envoy/config/core/v3/base.proto";

import "udpa/annotations/status.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.http_11_proxy.v3";
option java_outer_classname = "UpstreamHttp11ConnectProto";
option java_multiple_files = true;
option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/http_11_proxy/v3;http_11_proxyv3";
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Upstream HTTP/1.1 Proxy]
// [#extension: envoy.transport_sockets.http_11_proxy]

// Configuration for HTTP/1.1 proxy transport sockets.
// If this is configured and an intermediate filter adds proxy metadata to the
// stream info then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify what "proxy metadata" means in this context?

// - Upstream connections will be directed to the specified proxy address rather
// than the host's address
Comment thread
RyanTheOptimist marked this conversation as resolved.
Outdated
// - Upstream TLS connections will have a raw HTTP/1.1 CONNECT header prefaced
// to the payload, and 200 response stripped
// - Plaintext HTTP/1.1 connections will be sent with a fully qualified URL.
// This transport socket is not compatible with HTTP/3, plaintext HTTP/2, or raw TCP.
message Http11ProxyUpstreamTransport {
// The underlying transport socket being wrapped.
config.core.v3.TransportSocket transport_socket = 1 [(validate.rules).message = {required: true}];
}
1 change: 1 addition & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ proto_library(
"//envoy/extensions/stat_sinks/graphite_statsd/v3:pkg",
"//envoy/extensions/stat_sinks/wasm/v3:pkg",
"//envoy/extensions/transport_sockets/alts/v3:pkg",
"//envoy/extensions/transport_sockets/http_11_proxy/v3:pkg",
"//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg",
"//envoy/extensions/transport_sockets/quic/v3:pkg",
"//envoy/extensions/transport_sockets/raw_buffer/v3:pkg",
Expand Down
16 changes: 16 additions & 0 deletions envoy/network/transport_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,22 @@ class TransportSocketOptions {
*/
virtual absl::optional<Network::ProxyProtocolData> proxyProtocolOptions() const PURE;

// Information for use by the http_11_proxy transport socket.
struct ProxyInfo {
Comment thread
RyanTheOptimist marked this conversation as resolved.
Outdated
ProxyInfo(std::string hostname, Network::Address::InstanceConstSharedPtr address)
: hostname(hostname), proxy_address(address) {}
// The hostname of the original request, to be used in CONNECT request if
// the underlying transport is TLS.
std::string hostname;
// The address of the proxy, where connections should be routed to.
Network::Address::InstanceConstSharedPtr proxy_address;
};

/**
* @return any proxy information if sending to an intermediate proxy over HTTP/1.1.
*/
virtual const std::unique_ptr<const ProxyInfo>& proxyInfo() const PURE;
Comment thread
RyanTheOptimist marked this conversation as resolved.
Outdated
Comment thread
RyanTheOptimist marked this conversation as resolved.
Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not keep this private to the extension? E.g. per #21700, would be another private hashable object.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it can be because Envoy needs to use it for hashing and connection creation.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These objects are also hashed in transport options and passed along during connection creation to the upstream connection stream info. Maybe I'm missing something?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see how I could handle hashing but I don't see how I could handle the createConnection in
source/common/upstream/upstream_impl.cc properly without explicit visibility of the addresses?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I suppose we could split the two since address() override seems more generic than CONNECT (you could do passthrough and all sorts of things with it), but it's up to you.


/**
* @return filter state from the downstream request or connection.
*/
Expand Down
24 changes: 16 additions & 8 deletions source/common/http/codec_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -168,22 +168,30 @@ void CodecClient::onData(Buffer::Instance& data) {
CodecClientProd::CodecClientProd(CodecType type, Network::ClientConnectionPtr&& connection,
Upstream::HostDescriptionConstSharedPtr host,
Event::Dispatcher& dispatcher,
Random::RandomGenerator& random_generator)
: NoConnectCodecClientProd(type, std::move(connection), host, dispatcher, random_generator) {
Random::RandomGenerator& random_generator,
const Network::TransportSocketOptionsConstSharedPtr& options)
: NoConnectCodecClientProd(type, std::move(connection), host, dispatcher, random_generator,
options) {
connect();
}

NoConnectCodecClientProd::NoConnectCodecClientProd(CodecType type,
Network::ClientConnectionPtr&& connection,
Upstream::HostDescriptionConstSharedPtr host,
Event::Dispatcher& dispatcher,
Random::RandomGenerator& random_generator)
NoConnectCodecClientProd::NoConnectCodecClientProd(
CodecType type, Network::ClientConnectionPtr&& connection,
Upstream::HostDescriptionConstSharedPtr host, Event::Dispatcher& dispatcher,
Random::RandomGenerator& random_generator,
const Network::TransportSocketOptionsConstSharedPtr& options)
: CodecClient(type, std::move(connection), host, dispatcher) {
switch (type) {
case CodecType::HTTP1: {
// If the transport socket indicates this is being proxied, inform the HTTP/1.1 codec. It will
// send fully qualified URLs iff the underlying transport is plaintext.
bool proxied = false;
if (options && options->proxyInfo()) {
proxied = true;
}
codec_ = std::make_unique<Http1::ClientConnectionImpl>(
*connection_, host->cluster().http1CodecStats(), *this, host->cluster().http1Settings(),
host->cluster().maxResponseHeadersCount());
host->cluster().maxResponseHeadersCount(), proxied);
break;
}
case CodecType::HTTP2: {
Expand Down
7 changes: 4 additions & 3 deletions source/common/http/codec_client.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,8 +281,8 @@ class NoConnectCodecClientProd : public CodecClient {
public:
NoConnectCodecClientProd(CodecType type, Network::ClientConnectionPtr&& connection,
Upstream::HostDescriptionConstSharedPtr host,
Event::Dispatcher& dispatcher,
Random::RandomGenerator& random_generator);
Event::Dispatcher& dispatcher, Random::RandomGenerator& random_generator,
const Network::TransportSocketOptionsConstSharedPtr& options);
};

/**
Expand All @@ -292,7 +292,8 @@ class CodecClientProd : public NoConnectCodecClientProd {
public:
CodecClientProd(CodecType type, Network::ClientConnectionPtr&& connection,
Upstream::HostDescriptionConstSharedPtr host, Event::Dispatcher& dispatcher,
Random::RandomGenerator& random_generator);
Random::RandomGenerator& random_generator,
const Network::TransportSocketOptionsConstSharedPtr& options);
};

} // namespace Http
Expand Down
6 changes: 4 additions & 2 deletions source/common/http/http1/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1325,13 +1325,15 @@ void ServerConnectionImpl::ActiveRequest::dumpState(std::ostream& os, int indent

ClientConnectionImpl::ClientConnectionImpl(Network::Connection& connection, CodecStats& stats,
ConnectionCallbacks&, const Http1Settings& settings,
const uint32_t max_response_headers_count)
const uint32_t max_response_headers_count,
bool passing_through_proxy)
: ConnectionImpl(connection, stats, settings, MessageType::Response, MAX_RESPONSE_HEADERS_KB,
max_response_headers_count),
owned_output_buffer_(connection.dispatcher().getWatermarkFactory().createBuffer(
[&]() -> void { this->onBelowLowWatermark(); },
[&]() -> void { this->onAboveHighWatermark(); },
[]() -> void { /* TODO(adisuissa): handle overflow watermark */ })) {
[]() -> void { /* TODO(adisuissa): handle overflow watermark */ })),
passing_through_proxy_(passing_through_proxy) {
owned_output_buffer_->setWatermarks(connection.bufferLimit());
// Inform parent
output_buffer_ = owned_output_buffer_.get();
Expand Down
17 changes: 15 additions & 2 deletions source/common/http/http1/codec_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ class ConnectionImpl : public virtual Connection,
* @return Network::Connection& the backing network connection.
*/
Network::Connection& connection() { return connection_; }
const Network::Connection& connection() const { return connection_; }

/**
* Called when the active encoder has completed encoding the outbound half of the stream.
Expand Down Expand Up @@ -230,7 +231,7 @@ class ConnectionImpl : public virtual Connection,
virtual void maybeAddSentinelBufferFragment(Buffer::Instance&) {}
CodecStats& stats() { return stats_; }
bool enableTrailers() const { return codec_settings_.enable_trailers_; }
bool sendFullyQualifiedUrl() const { return codec_settings_.send_fully_qualified_url_; }
virtual bool sendFullyQualifiedUrl() const { return codec_settings_.send_fully_qualified_url_; }
HeaderKeyFormatterOptConstRef formatter() const {
return makeOptRefFromPtr(encode_only_header_key_formatter_.get());
}
Expand Down Expand Up @@ -563,7 +564,8 @@ class ClientConnectionImpl : public ClientConnection, public ConnectionImpl {
public:
ClientConnectionImpl(Network::Connection& connection, CodecStats& stats,
ConnectionCallbacks& callbacks, const Http1Settings& settings,
const uint32_t max_response_headers_count);
const uint32_t max_response_headers_count,
bool passing_through_proxy = false);
// Http::ClientConnection
RequestEncoder& newStream(ResponseDecoder& response_decoder) override;

Expand All @@ -578,6 +580,13 @@ class ClientConnectionImpl : public ClientConnection, public ConnectionImpl {

bool cannotHaveBody();

bool sendFullyQualifiedUrl() const override {
// Send fully qualified URLs either if the parent connection is configured to do so or this
// stream is passing through a proxy and the underlying transport is plaintext.
return ConnectionImpl::sendFullyQualifiedUrl() ||
(passing_through_proxy_ && !connection().ssl());
}

// ParserCallbacks.
Status onUrlBase(const char*, size_t) override { return okStatus(); }
Status onStatusBase(const char* data, size_t length) override;
Expand Down Expand Up @@ -649,6 +658,10 @@ class ClientConnectionImpl : public ClientConnection, public ConnectionImpl {

// The default limit of 80 KiB is the vanilla http_parser behaviour.
static constexpr uint32_t MAX_RESPONSE_HEADERS_KB = 80;

// True if the upstream connection is pointed at an HTTP/1.1 proxy, and
// plaintext HTTP should be sent with fully qualified URLs.
bool passing_through_proxy_ = false;
};

} // namespace Http1
Expand Down
6 changes: 3 additions & 3 deletions source/common/http/http1/conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ allocateConnPool(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_
return std::make_unique<ActiveClient>(*pool, absl::nullopt);
},
[](Upstream::Host::CreateConnectionData& data, HttpConnPoolImplBase* pool) {
CodecClientPtr codec{new CodecClientProd(CodecType::HTTP1, std::move(data.connection_),
data.host_description_, pool->dispatcher(),
pool->randomGenerator())};
CodecClientPtr codec{new CodecClientProd(
CodecType::HTTP1, std::move(data.connection_), data.host_description_,
pool->dispatcher(), pool->randomGenerator(), pool->transportSocketOptions())};
return codec;
},
std::vector<Protocol>{Protocol::Http11}, absl::nullopt, nullptr);
Expand Down
6 changes: 3 additions & 3 deletions source/common/http/http2/conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ allocateConnPool(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_
return std::make_unique<ActiveClient>(*pool, absl::nullopt);
},
[](Upstream::Host::CreateConnectionData& data, HttpConnPoolImplBase* pool) {
CodecClientPtr codec{new CodecClientProd(CodecType::HTTP2, std::move(data.connection_),
data.host_description_, pool->dispatcher(),
pool->randomGenerator())};
CodecClientPtr codec{new CodecClientProd(
CodecType::HTTP2, std::move(data.connection_), data.host_description_,
pool->dispatcher(), pool->randomGenerator(), pool->transportSocketOptions())};
return codec;
},
std::vector<Protocol>{Protocol::Http2}, origin, cache);
Expand Down
8 changes: 4 additions & 4 deletions source/common/http/http3/conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,10 @@ allocateConnPool(Event::Dispatcher& dispatcher, Random::RandomGenerator& random_
"envoy.reloadable_features.postpone_h3_client_connect_to_next_loop")
? std::make_unique<NoConnectCodecClientProd>(
CodecType::HTTP3, std::move(data.connection_), data.host_description_,
pool->dispatcher(), pool->randomGenerator())
: std::make_unique<CodecClientProd>(CodecType::HTTP3, std::move(data.connection_),
data.host_description_, pool->dispatcher(),
pool->randomGenerator());
pool->dispatcher(), pool->randomGenerator(), pool->transportSocketOptions())
: std::make_unique<CodecClientProd>(
CodecType::HTTP3, std::move(data.connection_), data.host_description_,
pool->dispatcher(), pool->randomGenerator(), pool->transportSocketOptions());
return codec;
},
std::vector<Protocol>{Protocol::Http3}, connect_callback, quic_info);
Expand Down
3 changes: 2 additions & 1 deletion source/common/http/mixed_conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ CodecClientPtr
HttpConnPoolImplMixed::createCodecClient(Upstream::Host::CreateConnectionData& data) {
auto protocol = protocol_ == Protocol::Http11 ? CodecType::HTTP1 : CodecType::HTTP2;
CodecClientPtr codec{new CodecClientProd(protocol, std::move(data.connection_),
data.host_description_, dispatcher_, random_generator_)};
data.host_description_, dispatcher_, random_generator_,
transportSocketOptions())};
return codec;
}

Expand Down
12 changes: 12 additions & 0 deletions source/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ envoy_cc_library(
hdrs = ["transport_socket_options_impl.h"],
deps = [
":application_protocol_lib",
":filter_state_proxy_info_lib",
":proxy_protocol_filter_state_lib",
":upstream_server_name_lib",
":upstream_subject_alt_names_lib",
Expand All @@ -450,6 +451,17 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "filter_state_proxy_info_lib",
srcs = ["filter_state_proxy_info.cc"],
hdrs = ["filter_state_proxy_info.h"],
deps = [
"//envoy/network:address_interface",
"//envoy/stream_info:filter_state_interface",
"//source/common/common:macros",
],
)

envoy_cc_library(
name = "upstream_server_name_lib",
srcs = ["upstream_server_name.cc"],
Expand Down
11 changes: 11 additions & 0 deletions source/common/network/filter_state_proxy_info.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "source/common/network/filter_state_proxy_info.h"

namespace Envoy {
namespace Network {

const std::string& FilterStateProxyInfo::key() {
CONSTRUCT_ON_FIRST_USE(std::string, "envoy.network.transport_socket.http_11_proxy.info");
}

} // namespace Network
} // namespace Envoy
31 changes: 31 additions & 0 deletions source/common/network/filter_state_proxy_info.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include "envoy/network/address.h"
#include "envoy/stream_info/filter_state.h"

#include "absl/strings/string_view.h"

namespace Envoy {
namespace Network {

/**
* Information which filters can add if they detect the stream should go
* upstream through an HTTP/1.1 proxy.
*/
class FilterStateProxyInfo : public StreamInfo::FilterState::Object {
Comment thread
RyanTheOptimist marked this conversation as resolved.
Outdated
public:
static const std::string& key();
Comment thread
RyanTheOptimist marked this conversation as resolved.

FilterStateProxyInfo(absl::string_view hostname, Network::Address::InstanceConstSharedPtr address)
: hostname_(hostname), address_(address) {}
Network::Address::InstanceConstSharedPtr address() const { return address_; }
Comment thread
RyanTheOptimist marked this conversation as resolved.

private:
// The hostname of this individual request.
const std::string hostname_;
Comment thread
RyanTheOptimist marked this conversation as resolved.
// The address of the proxy.
const Network::Address::InstanceConstSharedPtr address_;
};

} // namespace Network
} // namespace Envoy
10 changes: 9 additions & 1 deletion source/common/network/transport_socket_options_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "source/common/common/scalar_to_byte_vector.h"
#include "source/common/common/utility.h"
#include "source/common/network/application_protocol.h"
#include "source/common/network/filter_state_proxy_info.h"
#include "source/common/network/proxy_protocol_filter_state.h"
#include "source/common/network/upstream_server_name.h"
#include "source/common/network/upstream_subject_alt_names.h"
Expand Down Expand Up @@ -52,6 +53,7 @@ TransportSocketOptionsConstSharedPtr TransportSocketOptionsUtility::fromFilterSt
std::vector<std::string> subject_alt_names;
std::vector<std::string> alpn_fallback;
absl::optional<Network::ProxyProtocolData> proxy_protocol_options;
std::unique_ptr<const TransportSocketOptions::ProxyInfo> proxy_info;

if (auto typed_data =
filter_state->getDataReadOnly<UpstreamServerName>(UpstreamServerName::key());
Expand All @@ -77,9 +79,15 @@ TransportSocketOptionsConstSharedPtr TransportSocketOptionsUtility::fromFilterSt
proxy_protocol_options.emplace(typed_data->value());
}

if (auto typed_data =
filter_state->getDataReadOnly<FilterStateProxyInfo>(FilterStateProxyInfo::key());
typed_data != nullptr) {
proxy_info = std::make_unique<TransportSocketOptions::ProxyInfo>("host", typed_data->address());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, since this is passing in "host" I'm guessing it's an unused argument?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion to use well-known endpoint metadata ("tunnel.address" or something). We've been doing that for the internal listener effectively.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using the hostname() sorry for missing this as I fixed things up.

}

return std::make_shared<Network::TransportSocketOptionsImpl>(
server_name, std::move(subject_alt_names), std::move(application_protocols),
std::move(alpn_fallback), proxy_protocol_options, filter_state);
std::move(alpn_fallback), proxy_protocol_options, filter_state, std::move(proxy_info));
}

} // namespace Network
Expand Down
Loading