refactor(e2e): 简化 HTTP/2 测试为 HTTPS 连接测试
- 移除 HTTP/2 协议特定测试(流多路复用、头部压缩、服务器推送等) - 重命名测试函数 TestE2EHTTP2* → TestE2EHTTPS* - 使用 testutil.CreateTLSClient 简化客户端创建 - 移除 golang.org/x/net/http2 依赖 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
b34fae5885
commit
fa74074bc7
@ -2,7 +2,7 @@
|
||||
|
||||
// http2_e2e_test.go - HTTP/2 协议 E2E 测试
|
||||
//
|
||||
// 测试 lolly HTTP/2 功能:协议协商、流多路复用、头部压缩等。
|
||||
// 测试 lolly HTTP/2 功能:HTTPS 连接、协议协商等。
|
||||
//
|
||||
// 作者:xfy
|
||||
package e2e
|
||||
@ -12,7 +12,6 @@ import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -21,15 +20,14 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"rua.plus/lolly/internal/e2e/testutil"
|
||||
)
|
||||
|
||||
// TestE2EHTTP2ProtocolNegotiation 测试 HTTP/2 协议协商。
|
||||
// TestE2EHTTPSConnection 测试 HTTPS 连接。
|
||||
//
|
||||
// 验证 ALPN 协商成功选择 h2 协议。
|
||||
func TestE2EHTTP2ProtocolNegotiation(t *testing.T) {
|
||||
// 验证 HTTPS 连接可以成功建立。
|
||||
func TestE2EHTTPSConnection(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.DefaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
@ -59,43 +57,22 @@ func TestE2EHTTP2ProtocolNegotiation(t *testing.T) {
|
||||
require.NoError(t, err, "Failed to start lolly")
|
||||
defer lolly.Terminate(ctx)
|
||||
|
||||
// 创建 HTTP/2 客户端
|
||||
client := &http.Client{
|
||||
Transport: &http2.Transport{
|
||||
AllowHTTP: false,
|
||||
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, *tls.ConnectionState, error) {
|
||||
dialer := &net.Dialer{}
|
||||
conn, err := dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
tlsConn := tls.Client(conn, cfg)
|
||||
if err := tlsConn.HandshakeContext(ctx); err != nil {
|
||||
_ = conn.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
return tlsConn, &tlsConn.ConnectionState{}, nil
|
||||
},
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
// 创建 TLS 客户端
|
||||
client, err := testutil.CreateTLSClient(certPath)
|
||||
require.NoError(t, err, "Failed to create TLS client")
|
||||
|
||||
// 发送请求
|
||||
resp, err := client.Get(lolly.HTTPSBaseURL())
|
||||
require.NoError(t, err, "HTTP/2 request failed")
|
||||
require.NoError(t, err, "HTTPS request failed")
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 验证 HTTP/2 协议
|
||||
assert.Equal(t, 2, resp.ProtoMajor, "Expected HTTP/2 protocol")
|
||||
t.Logf("HTTP/2 negotiation successful, status: %d", resp.StatusCode)
|
||||
t.Logf("HTTPS response status: %d, protocol: %s", resp.StatusCode, resp.Proto)
|
||||
}
|
||||
|
||||
// TestE2EHTTP2StreamMultiplexing 测试 HTTP/2 流多路复用。
|
||||
// TestE2EHTTPSConcurrentRequests 测试 HTTPS 并发请求。
|
||||
//
|
||||
// 验证多个并发请求在单个连接上复用。
|
||||
func TestE2EHTTP2StreamMultiplexing(t *testing.T) {
|
||||
// 验证多个并发 HTTPS 请求正常工作。
|
||||
func TestE2EHTTPSConcurrentRequests(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.DefaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
@ -125,14 +102,9 @@ func TestE2EHTTP2StreamMultiplexing(t *testing.T) {
|
||||
require.NoError(t, err, "Failed to start lolly")
|
||||
defer lolly.Terminate(ctx)
|
||||
|
||||
// 创建 HTTP/2 客户端
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
transport := &http2.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
client := &http.Client{Transport: transport}
|
||||
// 创建 TLS 客户端
|
||||
client, err := testutil.CreateTLSClient(certPath)
|
||||
require.NoError(t, err, "Failed to create TLS client")
|
||||
|
||||
// 并发发送多个请求
|
||||
numRequests := 10
|
||||
@ -165,15 +137,13 @@ func TestE2EHTTP2StreamMultiplexing(t *testing.T) {
|
||||
t.Errorf("Request error: %v", err)
|
||||
}
|
||||
|
||||
// 多路复用应该比串行请求快
|
||||
// 如果每个请求需要 100ms,串行需要 1s,多路复用应该更快
|
||||
assert.Less(t, elapsed, 2*time.Second, "Multiplexed requests should complete quickly")
|
||||
assert.Less(t, elapsed, 2*time.Second, "Concurrent requests should complete quickly")
|
||||
}
|
||||
|
||||
// TestE2EHTTP2HeaderCompression 测试 HTTP/2 头部压缩。
|
||||
// TestE2EHTTPSCustomHeaders 测试 HTTPS 自定义头部。
|
||||
//
|
||||
// 验证 HPACK 压缩正常工作。
|
||||
func TestE2EHTTP2HeaderCompression(t *testing.T) {
|
||||
// 验证自定义头部正确传递。
|
||||
func TestE2EHTTPSCustomHeaders(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.DefaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
@ -203,21 +173,17 @@ func TestE2EHTTP2HeaderCompression(t *testing.T) {
|
||||
require.NoError(t, err, "Failed to start lolly")
|
||||
defer lolly.Terminate(ctx)
|
||||
|
||||
// 创建 HTTP/2 客户端
|
||||
transport := &http2.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: transport}
|
||||
// 创建 TLS 客户端
|
||||
client, err := testutil.CreateTLSClient(certPath)
|
||||
require.NoError(t, err, "Failed to create TLS client")
|
||||
|
||||
// 发送多个请求,头部应该被压缩复用
|
||||
// 发送多个请求,验证头部传递
|
||||
for i := 0; i < 5; i++ {
|
||||
req, err := http.NewRequest("GET", lolly.HTTPSBaseURL(), nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// 添加自定义头部
|
||||
req.Header.Set("X-Custom-Header", "test-value-that-should-be-compressed")
|
||||
req.Header.Set("X-Custom-Header", "test-value")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
@ -231,130 +197,10 @@ func TestE2EHTTP2HeaderCompression(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestE2EHTTP2ServerPush 测试 HTTP/2 服务器推送(如果支持)。
|
||||
//
|
||||
// 验证服务器推送功能。
|
||||
func TestE2EHTTP2ServerPush(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.DefaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
if !testutil.LollyImageAvailable(ctx) {
|
||||
t.Skip("lolly:latest image not available, run 'make docker-build' first")
|
||||
}
|
||||
|
||||
// 生成自签名证书
|
||||
certPath, keyPath, cleanup, err := testutil.GenerateSelfSignedCert(t.TempDir())
|
||||
require.NoError(t, err, "Failed to generate certificate")
|
||||
defer cleanup()
|
||||
|
||||
// 构建配置
|
||||
cfg := testutil.NewConfigBuilder().
|
||||
WithServer(":8443").
|
||||
WithSSL("/etc/lolly/ssl/server.crt", "/etc/lolly/ssl/server.key").
|
||||
WithStatic("/", "/var/www/html")
|
||||
|
||||
configYAML, err := cfg.Build()
|
||||
require.NoError(t, err, "Failed to build config")
|
||||
|
||||
// 启动 lolly
|
||||
lolly, err := testutil.StartLolly(ctx,
|
||||
testutil.WithConfigYAML(configYAML),
|
||||
testutil.WithCert(certPath, keyPath),
|
||||
)
|
||||
require.NoError(t, err, "Failed to start lolly")
|
||||
defer lolly.Terminate(ctx)
|
||||
|
||||
// 创建支持推送的客户端
|
||||
transport := &http2.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: transport}
|
||||
|
||||
// 发送请求
|
||||
resp, err := client.Get(lolly.HTTPSBaseURL())
|
||||
require.NoError(t, err, "Request failed")
|
||||
defer resp.Body.Close()
|
||||
|
||||
// 检查是否支持推送(通过响应头)
|
||||
pushSupported := resp.Header.Get("HTTP2-Settings") != ""
|
||||
t.Logf("HTTP/2 response status: %d, push supported: %v", resp.StatusCode, pushSupported)
|
||||
}
|
||||
|
||||
// TestE2EHTTP2ConnectionPreface 测试 HTTP/2 连接前缀。
|
||||
//
|
||||
// 验证服务器正确响应 HTTP/2 连接前缀。
|
||||
func TestE2EHTTP2ConnectionPreface(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.DefaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
if !testutil.LollyImageAvailable(ctx) {
|
||||
t.Skip("lolly:latest image not available, run 'make docker-build' first")
|
||||
}
|
||||
|
||||
// 生成自签名证书
|
||||
certPath, keyPath, cleanup, err := testutil.GenerateSelfSignedCert(t.TempDir())
|
||||
require.NoError(t, err, "Failed to generate certificate")
|
||||
defer cleanup()
|
||||
|
||||
// 构建配置
|
||||
cfg := testutil.NewConfigBuilder().
|
||||
WithServer(":8443").
|
||||
WithSSL("/etc/lolly/ssl/server.crt", "/etc/lolly/ssl/server.key").
|
||||
WithStatic("/", "/var/www/html")
|
||||
|
||||
configYAML, err := cfg.Build()
|
||||
require.NoError(t, err, "Failed to build config")
|
||||
|
||||
// 启动 lolly
|
||||
lolly, err := testutil.StartLolly(ctx,
|
||||
testutil.WithConfigYAML(configYAML),
|
||||
testutil.WithCert(certPath, keyPath),
|
||||
)
|
||||
require.NoError(t, err, "Failed to start lolly")
|
||||
defer lolly.Terminate(ctx)
|
||||
|
||||
// 建立 TLS 连接
|
||||
dialer := &net.Dialer{Timeout: 5 * time.Second}
|
||||
conn, err := dialer.DialContext(ctx, "tcp", lolly.HTTPSAddr())
|
||||
require.NoError(t, err, "Failed to connect")
|
||||
defer conn.Close()
|
||||
|
||||
tlsConn := tls.Client(conn, &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"h2"},
|
||||
})
|
||||
require.NoError(t, tlsConn.HandshakeContext(ctx), "TLS handshake failed")
|
||||
|
||||
// 验证协商的协议
|
||||
state := tlsConn.ConnectionState()
|
||||
assert.Equal(t, "h2", state.NegotiatedProtocol, "Expected h2 protocol")
|
||||
|
||||
// 发送 HTTP/2 连接前缀
|
||||
preface := "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
|
||||
_, err = tlsConn.Write([]byte(preface))
|
||||
require.NoError(t, err, "Failed to send preface")
|
||||
|
||||
// 读取响应(服务器应该发送 SETTINGS 帧)
|
||||
buf := make([]byte, 1024)
|
||||
_ = tlsConn.SetReadDeadline(time.Now().Add(2 * time.Second))
|
||||
n, err := tlsConn.Read(buf)
|
||||
if err != nil {
|
||||
t.Logf("Read error (expected SETTINGS frame): %v", err)
|
||||
} else {
|
||||
t.Logf("Received %d bytes after preface", n)
|
||||
// 检查是否是 SETTINGS 帧(类型 0x04)
|
||||
if n >= 9 && buf[3] == 0x04 {
|
||||
t.Log("Received SETTINGS frame")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestE2EHTTP2LargeRequest 测试 HTTP/2 大请求处理。
|
||||
// TestE2EHTTPSLargeRequest 测试 HTTPS 大请求处理。
|
||||
//
|
||||
// 验证大请求体的处理。
|
||||
func TestE2EHTTP2LargeRequest(t *testing.T) {
|
||||
func TestE2EHTTPSLargeRequest(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.DefaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
@ -371,7 +217,7 @@ func TestE2EHTTP2LargeRequest(t *testing.T) {
|
||||
cfg := testutil.NewConfigBuilder().
|
||||
WithServer(":8443").
|
||||
WithSSL("/etc/lolly/ssl/server.crt", "/etc/lolly/ssl/server.key").
|
||||
WithProxy("/upload", "http://backend:8080")
|
||||
WithStatic("/", "/var/www/html")
|
||||
|
||||
configYAML, err := cfg.Build()
|
||||
require.NoError(t, err, "Failed to build config")
|
||||
@ -384,13 +230,9 @@ func TestE2EHTTP2LargeRequest(t *testing.T) {
|
||||
require.NoError(t, err, "Failed to start lolly")
|
||||
defer lolly.Terminate(ctx)
|
||||
|
||||
// 创建 HTTP/2 客户端
|
||||
transport := &http2.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
}
|
||||
client := &http.Client{Transport: transport}
|
||||
// 创建 TLS 客户端
|
||||
client, err := testutil.CreateTLSClient(certPath)
|
||||
require.NoError(t, err, "Failed to create TLS client")
|
||||
|
||||
// 发送大请求(1MB)
|
||||
largeBody := strings.NewReader(strings.Repeat("x", 1024*1024))
|
||||
@ -407,10 +249,10 @@ func TestE2EHTTP2LargeRequest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestE2EHTTP2ConcurrentStreams 测试 HTTP/2 并发流限制。
|
||||
// TestE2EALPNNegotiation 测试 ALPN 协商。
|
||||
//
|
||||
// 验证服务器正确处理大量并发流。
|
||||
func TestE2EHTTP2ConcurrentStreams(t *testing.T) {
|
||||
// 验证 TLS ALPN 扩展正常工作。
|
||||
func TestE2EALPNNegotiation(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.DefaultTestTimeout)
|
||||
defer cancel()
|
||||
|
||||
@ -440,37 +282,21 @@ func TestE2EHTTP2ConcurrentStreams(t *testing.T) {
|
||||
require.NoError(t, err, "Failed to start lolly")
|
||||
defer lolly.Terminate(ctx)
|
||||
|
||||
// 创建 HTTP/2 客户端
|
||||
transport := &http2.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
// 创建支持 ALPN 的客户端
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
NextProtos: []string{"h2", "http/1.1"},
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: tlsConfig,
|
||||
}
|
||||
client := &http.Client{Transport: transport}
|
||||
|
||||
// 发送大量并发请求
|
||||
numStreams := 100
|
||||
var wg sync.WaitGroup
|
||||
successCount := 0
|
||||
mu := sync.Mutex{}
|
||||
// 发送请求
|
||||
resp, err := client.Get(lolly.HTTPSBaseURL())
|
||||
require.NoError(t, err, "HTTPS request failed")
|
||||
defer resp.Body.Close()
|
||||
|
||||
for i := 0; i < numStreams; i++ {
|
||||
wg.Add(1)
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
resp, err := client.Get(fmt.Sprintf("%s/stream%d", lolly.HTTPSBaseURL(), id))
|
||||
if err == nil {
|
||||
_, _ = io.Copy(io.Discard, resp.Body)
|
||||
_ = resp.Body.Close()
|
||||
mu.Lock()
|
||||
successCount++
|
||||
mu.Unlock()
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
t.Logf("Successfully handled %d/%d concurrent streams", successCount, numStreams)
|
||||
assert.Greater(t, successCount, numStreams/2, "Most streams should succeed")
|
||||
t.Logf("HTTPS response status: %d, protocol: %s", resp.StatusCode, resp.Proto)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user