Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tcp_proxy: ignore transfer encoding in HTTP/1.1 CONNECT responses #14623

Merged
merged 8 commits into from
Jan 12, 2021
Merged
Show file tree
Hide file tree
Changes from 5 commits
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 docs/root/intro/arch_overview/http/upgrades.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ will synthesize 200 response headers, and then forward the TCP data as the HTTP
For an example of proxying connect, please see :repo:`configs/proxy_connect.yaml <configs/proxy_connect.yaml>`
For an example of terminating connect, please see :repo:`configs/terminate_connect.yaml <configs/terminate_connect.yaml>`

.. _tunneling-tcp-over-http:

Tunneling TCP over HTTP
^^^^^^^^^^^^^^^^^^^^^^^
Envoy also has support for tunneling raw TCP over HTTP CONNECT requests. Find
Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ New Features
* signal: added an extension point for custom actions to run on the thread that has encountered a fatal error. Actions are configurable via :ref:`fatal_actions <envoy_v3_api_field_config.bootstrap.v3.Bootstrap.fatal_actions>`.
* start_tls: :ref:`transport socket<envoy_v3_api_msg_extensions.transport_sockets.starttls.v3.StartTlsConfig>` which starts in clear-text but may programatically be converted to use tls.
* tcp: added a new :ref:`envoy.overload_actions.reject_incoming_connections <config_overload_manager_overload_actions>` action to reject incoming TCP connections.
* tcp_proxy: add support for converting raw TCP streams into HTTP/1.1 CONNECT requests. See :ref:`upgrade documentation <tunneling-tcp-over-http>` for details.
mattklein123 marked this conversation as resolved.
Show resolved Hide resolved
* thrift_proxy: added a new :ref: `payload_passthrough <envoy_v3_api_field_extensions.filters.network.thrift_proxy.v3.ThriftProxy.payload_passthrough>` option to skip decoding body in the Thrift message.
* tls: added support for RSA certificates with 4096-bit keys in FIPS mode.
* tracing: added SkyWalking tracer.
Expand Down
7 changes: 3 additions & 4 deletions source/common/http/http1/codec_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1213,13 +1213,12 @@ Envoy::StatusOr<int> ClientConnectionImpl::onHeadersComplete() {
ENVOY_CONN_LOG(trace, "codec entering upgrade mode for CONNECT response.", connection_);
handling_upgrade_ = true;

// For responses to connect requests, do not accept the chunked
// encoding header: https://tools.ietf.org/html/rfc7231#section-4.3.6
// For responses to connect requests, ignore the chunked encoding header:
// https://tools.ietf.org/html/rfc7231#section-4.3.6
if (headers->TransferEncoding() &&
absl::EqualsIgnoreCase(headers->TransferEncoding()->value().getStringView(),
Headers::get().TransferEncodingValues.Chunked)) {
RETURN_IF_ERROR(sendProtocolError(Http1ResponseCodeDetails::get().InvalidTransferEncoding));
return codecProtocolError("http/1.1 protocol error: unsupported transfer encoding");
ENVOY_CONN_LOG(trace, "ignoring transfer-encoding in CONNECT response.", connection_);
mattklein123 marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
11 changes: 7 additions & 4 deletions test/integration/integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1645,10 +1645,13 @@ TEST_P(IntegrationTest, ConnectWithChunkedBody) {
EXPECT_FALSE(absl::StrContains(data, "onnection")) << data;
ASSERT_TRUE(fake_upstream_connection->write(
"HTTP/1.1 200 OK\r\ntransfer-encoding: chunked\r\n\r\nb\r\nHello World\r\n0\r\n\r\n"));
// The response will be rejected because chunked headers are not allowed with CONNECT upgrades.
// Envoy will send a local reply due to the invalid upstream response.
tcp_client->waitForDisconnect(false);
EXPECT_TRUE(absl::StartsWith(tcp_client->data(), "HTTP/1.1 503 Service Unavailable\r\n"));
tcp_client->waitForData("\r\n\r\n", false);
EXPECT_TRUE(absl::StartsWith(tcp_client->data(), "HTTP/1.1 200 OK\r\n")) << tcp_client->data();
// Make sure the following payload is proxied without chunks or any other modifications.
ASSERT_TRUE(fake_upstream_connection->waitForData(
FakeRawConnection::waitForInexactMatch("\r\n\r\npayload"), &data));

tcp_client->close();
ASSERT_TRUE(fake_upstream_connection->waitForDisconnect());
}

Expand Down
16 changes: 9 additions & 7 deletions test/integration/tcp_tunneling_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -792,9 +792,7 @@ TEST_P(TcpTunnelingIntegrationTest, ContentLengthHeaderIgnoredHttp1) {
ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
}

// TODO(irozzo): temporarily disabled as a protocol error is thrown when
// transfer-encoding header is received in CONNECT responses.
TEST_P(TcpTunnelingIntegrationTest, DISABLED_TransferEncodingHeaderIgnoredHttp1) {
TEST_P(TcpTunnelingIntegrationTest, TransferEncodingHeaderIgnoredHttp1) {
if (upstreamProtocol() == FakeHttpConnection::Type::HTTP2) {
return;
}
Expand All @@ -812,13 +810,17 @@ TEST_P(TcpTunnelingIntegrationTest, DISABLED_TransferEncodingHeaderIgnoredHttp1)

// Send upgrade headers downstream, fully establishing the connection.
ASSERT_TRUE(
fake_upstream_connection->write("HTTP/1.1 299 OK\r\nTransfer-encoding: chunked\r\n\r\n"));
fake_upstream_connection->write("HTTP/1.1 200 OK\r\nTransfer-encoding: chunked\r\n\r\n"));

// Now send some data and close the TCP client.
ASSERT_TRUE(tcp_client->write("hello", false));
ASSERT_TRUE(tcp_client->write("hello"));
ASSERT_TRUE(
fake_upstream_connection->waitForData(FakeRawConnection::waitForInexactMatch("hello")));

// Close connections.
ASSERT_TRUE(fake_upstream_connection->close());
ASSERT_TRUE(fake_upstream_connection->waitForDisconnect());
tcp_client->close();
ASSERT_TRUE(upstream_request_->waitForData(*dispatcher_, 5));
ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
}

TEST_P(TcpTunnelingIntegrationTest, DeferTransmitDataUntilSuccessConnectResponseIsReceived) {
Expand Down