Skip to content

feat(interceptor): add HTTP/2 h2c support for cleartext connections#1394

Open
starlightromero wants to merge 1 commit intokedacore:mainfrom
starlightromero:feat/http2-h2c-support
Open

feat(interceptor): add HTTP/2 h2c support for cleartext connections#1394
starlightromero wants to merge 1 commit intokedacore:mainfrom
starlightromero:feat/http2-h2c-support

Conversation

@starlightromero
Copy link

@starlightromero starlightromero commented Dec 11, 2025

Description

This PR adds HTTP/2 cleartext (h2c) protocol support to the interceptor proxy server, enabling AWS Application Load Balancers and other clients to communicate using HTTP/2 over plain HTTP.

Changes

  • Wrapped non-TLS handlers with to support HTTP/2 cleartext connections
  • Added dependency golang.org/x/net/http2 for h2c support
  • Comprehensive test coverage in pkg/http/h2c_test.go for both HTTP/1.1 and HTTP/2 h2c
  • HTTP/1.1 clients continue to work without any changes

Motivation

When using the AWS Load Balancer Controller Gateway API implementation, setting appProtocol: kubernetes.io/h2c on a Kubernetes Service instructs the controller to create ALB target groups with ProtocolVersion: HTTP2. However, the KEDA HTTP Add-on interceptor did not support h2c, causing health checks and traffic to fail.

This change enables:

  • ALB → Interceptor communication using HTTP/2 for improved performance
  • Support for appProtocol: kubernetes.io/h2c on Kubernetes Services
  • Better efficiency with HTTP/2 multiplexing and header compression

How It Works

The implementation wraps non-TLS handlers with h2c.NewHandler, which automatically:

  • Handles HTTP/2 connections with prior knowledge (what ALBs use)
  • Falls back to HTTP/1.1 for standard requests
  • Requires no configuration changes in Kubernetes manifests

TLS connections remain unchanged and continue to use native HTTP/2 over TLS.

Testing

  • ✅ All existing unit tests pass
  • ✅ New test TestHTTP2H2CSupport validates both HTTP/1.1 and HTTP/2 h2c functionality
  • ✅ Tested in live cluster with AWS ALB successfully using HTTP/2 target groups

Checklist

  • Commits are signed (DCO)
  • Tests pass locally (make test)
  • Code follows project style (pre-commit checks)
  • Backwards compatible with HTTP/1.1
  • Documentation included in commit message

Fixes #1397

Enable HTTP/2 cleartext (h2c) protocol support for non-TLS connections in the
interceptor proxy server. This allows AWS Application Load Balancers and other
clients to communicate with the interceptor using HTTP/2 over plain HTTP.

When tlsConfig is nil, the handler is wrapped with h2c.NewHandler to support
both HTTP/2 prior knowledge and HTTP/1.1 connections. TLS connections remain
unchanged and continue to support native HTTP/2 over TLS.

This change enables the use of appProtocol: kubernetes.io/h2c on Kubernetes
Services, which instructs the AWS Load Balancer Controller to create target
groups with ProtocolVersion: HTTP2 for improved performance and efficiency.

Changes:
- Wrap non-TLS handlers with h2c.NewHandler in pkg/http/server.go
- Add golang.org/x/net/http2 dependency for h2c support
- Add comprehensive test coverage for both HTTP/1.1 and HTTP/2 h2c
- Maintain backwards compatibility with HTTP/1.1 clients

Fixes: Enables HTTP/2 h2c support from ALB to interceptor pods
Signed-off-by: Starlight Romero <[email protected]>
@snyk-io
Copy link

snyk-io bot commented Dec 11, 2025

⚠️ Snyk checks are incomplete.

Status Scanner Critical High Medium Low Total (0)
⚠️ Open Source Security 0 0 0 0 See details

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copy link
Contributor

@linkvt linkvt left a comment

Choose a reason for hiding this comment

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

Hi @starlightromero ,

thanks for the PR! I left a few comments but also think that we would benefit from a written design in an issue here in the repo - at least in my opinion.

I'm not a maintainer of this project though.

Comment on lines +16 to +19
if tlsConfig == nil {
h2s := &http2.Server{}
hdl = h2c.NewHandler(hdl, h2s)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

golang has HTTP2 support for server and clients in its standard library, they added it quite recently: https://go.dev/doc/go1.24#nethttppkgnethttp

I would prefer using that over the extended standard library as they already plan to deprecate the golang.org/x/net/http2/client subpackage: golang/go#72039

Handler: hdl,
Addr: addr,
TLSConfig: tlsConfig,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

How do we handle:

  1. HTTP1 connections to services responding with h2c
  2. h2c connections to services responding with HTTP1?

Comment on lines +69 to +72
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
// Use standard dialer for h2c (cleartext HTTP/2)
return net.Dial(network, addr)
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this actually needed/used during an non HTTPS request?

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

serverAddr := "127.0.0.1:18888"
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should not expect a specific port on a system to be free for us to use during a test. I know that the httptest package can pick a selected port, not sure if we can use it here, but maybe there is another way to get a port thats always free to use.

@@ -0,0 +1,90 @@
package http
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO we should have e2e tests covering the different scenarios here (client uses http1/h2c, user application supports http1/h2c/both) as unit tests would not cover all cases.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds HTTP/2 cleartext (h2c) protocol support to the KEDA HTTP Add-on interceptor proxy server, enabling AWS Application Load Balancers and other clients to communicate using HTTP/2 over plain HTTP connections. This addresses the compatibility issue when using appProtocol: kubernetes.io/h2c on Kubernetes Services with AWS Load Balancer Controller.

Changes:

  • Added h2c support by wrapping non-TLS handlers with golang.org/x/net/http2/h2c package
  • Introduced comprehensive test coverage for both HTTP/1.1 and HTTP/2 h2c protocols
  • Updated dependency golang.org/x/net to support h2c functionality

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 3 comments.

File Description
pkg/http/server.go Wraps non-TLS handlers with h2c.NewHandler to enable HTTP/2 cleartext support while preserving HTTP/1.1 compatibility
pkg/http/h2c_test.go New test file validating both HTTP/1.1 and HTTP/2 h2c protocol handling
go.mod Updates golang.org/x/net dependency and related transitive dependencies to support h2c
go.sum Checksums for updated dependencies

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}
defer resp.Body.Close()

body, _ := io.ReadAll(resp.Body)
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The test ignores errors from ReadAll. While this is in a test and the error is unlikely, it's better to be consistent with error handling. Consider checking the error or using an explicit underscore to indicate intentional ignoring.

Copilot uses AI. Check for mistakes.
}
defer resp.Body.Close()

body, _ := io.ReadAll(resp.Body)
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The test ignores errors from ReadAll. While this is in a test and the error is unlikely, it's better to be consistent with error handling. Consider checking the error or using an explicit underscore to indicate intentional ignoring.

Copilot uses AI. Check for mistakes.
Comment on lines +35 to +36
// Wait for server to start
time.Sleep(500 * time.Millisecond)
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

Using a hardcoded sleep to wait for server startup is flaky and can cause intermittent test failures. Consider using a retry loop with a timeout to check if the server is accepting connections, or implement a readiness signal from the server goroutine.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Design Document: HTTP/2 h2c Support Implementation (PR #1394)

3 participants