lolly/internal/ssl/ssl_bench_test.go

439 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package ssl 提供 SSL/TLS 性能基准测试。
//
// 测试覆盖:
// - TLS 握手性能(模拟客户端握手)
// - 证书加载性能
// - 重新协商开销
// - OCSP 装订性能
// - 会话恢复性能(使用 session ticket
//
// 作者xfy
package ssl
import (
"crypto/tls"
"os"
"testing"
"time"
"rua.plus/lolly/internal/config"
"rua.plus/lolly/internal/sslutil"
)
// BenchmarkTLSHandshake 基准测试 TLS 握手性能。
//
// 使用本地 TCP 环回连接模拟真实客户端握手,
// 测量完整 TLS 握手(包括密钥交换和证书验证)的开销。
func BenchmarkTLSHandshake(b *testing.B) {
tmpDir := b.TempDir()
certPEM, keyPEM := generateTestCert(&testing.T{})
certPath := tmpDir + "/cert.pem"
keyPath := tmpDir + "/key.pem"
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
b.Fatalf("write cert: %v", err)
}
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
b.Fatalf("write key: %v", err)
}
cfg := &config.SSLConfig{
Cert: certPath,
Key: keyPath,
Protocols: []string{"TLSv1.2", "TLSv1.3"},
}
manager, err := NewTLSManager(cfg)
if err != nil {
b.Fatalf("NewTLSManager() failed: %v", err)
}
defer manager.Close()
serverTLS := manager.GetTLSConfig()
listener, err := tls.Listen("tcp", "127.0.0.1:0", serverTLS)
if err != nil {
b.Fatalf("tls.Listen failed: %v", err)
}
defer listener.Close()
// 后台接受连接
go func() {
for {
conn, err := listener.Accept()
if err != nil {
return
}
// 读取少量数据以完成握手
buf := make([]byte, 1)
_, _ = conn.Read(buf)
_ = conn.Close()
}
}()
clientTLS := &tls.Config{
InsecureSkipVerify: true,
MinVersion: tls.VersionTLS12,
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
conn, err := tls.Dial("tcp", listener.Addr().String(), clientTLS)
if err != nil {
b.Error(err)
continue
}
_, _ = conn.Write([]byte{0})
_ = conn.Close()
}
})
}
// BenchmarkTLSHandshake_TLS13Only 基准测试仅 TLS 1.3 的握手性能。
//
// TLS 1.3 握手比 TLS 1.2 更快1-RTT测量两者的差异。
func BenchmarkTLSHandshake_TLS13Only(b *testing.B) {
tmpDir := b.TempDir()
certPEM, keyPEM := generateTestCert(&testing.T{})
certPath := tmpDir + "/cert.pem"
keyPath := tmpDir + "/key.pem"
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
b.Fatalf("write cert: %v", err)
}
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
b.Fatalf("write key: %v", err)
}
cfg := &config.SSLConfig{
Cert: certPath,
Key: keyPath,
Protocols: []string{"TLSv1.3"},
}
manager, err := NewTLSManager(cfg)
if err != nil {
b.Fatalf("NewTLSManager() failed: %v", err)
}
defer manager.Close()
serverTLS := manager.GetTLSConfig()
listener, err := tls.Listen("tcp", "127.0.0.1:0", serverTLS)
if err != nil {
b.Fatalf("tls.Listen failed: %v", err)
}
defer listener.Close()
go func() {
for {
conn, err := listener.Accept()
if err != nil {
return
}
buf := make([]byte, 1)
_, _ = conn.Read(buf)
_ = conn.Close()
}
}()
clientTLS := &tls.Config{
InsecureSkipVerify: true,
MinVersion: tls.VersionTLS13,
MaxVersion: tls.VersionTLS13,
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
conn, err := tls.Dial("tcp", listener.Addr().String(), clientTLS)
if err != nil {
b.Error(err)
continue
}
_, _ = conn.Write([]byte{0})
_ = conn.Close()
}
})
}
// BenchmarkTLSCertificateLoad 基准测试证书加载性能。
//
// 测量从磁盘加载 X509 密钥对的开销,包括 PEM 解码和
// 私钥解析。
func BenchmarkTLSCertificateLoad(b *testing.B) {
tmpDir := b.TempDir()
certPEM, keyPEM := generateTestCert(&testing.T{})
certPath := tmpDir + "/cert.pem"
keyPath := tmpDir + "/key.pem"
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
b.Fatalf("write cert: %v", err)
}
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
b.Fatalf("write key: %v", err)
}
b.ResetTimer()
for b.Loop() {
_, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
b.Fatal(err)
}
}
}
// BenchmarkTLSCertificateLoad_InMemory 基准测试内存中证书加载性能。
//
// 不经过磁盘 I/O直接测量 PEM 解析和证书构建的开销。
func BenchmarkTLSCertificateLoad_InMemory(b *testing.B) {
certPEM, keyPEM := generateTestCert(&testing.T{})
b.ResetTimer()
for b.Loop() {
_, err := tls.X509KeyPair(certPEM, keyPEM)
if err != nil {
b.Fatal(err)
}
}
}
// BenchmarkTLSCertificateLoad_Parallel 基准测试并发证书加载性能。
//
// 模拟多协程同时加载证书的场景,验证线程安全性。
func BenchmarkTLSCertificateLoad_Parallel(b *testing.B) {
tmpDir := b.TempDir()
certPEM, keyPEM := generateTestCert(&testing.T{})
certPath := tmpDir + "/cert.pem"
keyPath := tmpDir + "/key.pem"
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
b.Fatalf("write cert: %v", err)
}
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
b.Fatalf("write key: %v", err)
}
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
b.Error(err)
}
}
})
}
// BenchmarkTLSRenegotiation 基准测试 TLS 重新协商开销。
//
// TLS 1.3 已移除重新协商,该测试测量 TLS 1.2 下的
// 重新协商成本,用于评估是否值得启用重新协商。
// 通过建立 TLS 1.2 连接并检查握手统计来测量开销。
func BenchmarkTLSRenegotiation(b *testing.B) {
tmpDir := b.TempDir()
certPEM, keyPEM := generateTestCert(&testing.T{})
certPath := tmpDir + "/cert.pem"
keyPath := tmpDir + "/key.pem"
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
b.Fatalf("write cert: %v", err)
}
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
b.Fatalf("write key: %v", err)
}
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
b.Fatalf("tls.LoadX509KeyPair failed: %v", err)
}
serverTLS := &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12,
}
listener, err := tls.Listen("tcp", "127.0.0.1:0", serverTLS)
if err != nil {
b.Fatalf("tls.Listen failed: %v", err)
}
defer listener.Close()
// 服务端接受连接
acceptDone := make(chan struct{})
go func() {
defer close(acceptDone)
for {
conn, err := listener.Accept()
if err != nil {
return
}
// 读取数据完成握手后关闭
buf := make([]byte, 1)
_, _ = conn.Read(buf)
_ = conn.Close()
}
}()
clientTLS := &tls.Config{
InsecureSkipVerify: true,
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12,
Renegotiation: tls.RenegotiateOnceAsClient,
}
b.ResetTimer()
for b.Loop() {
conn, err := tls.Dial("tcp", listener.Addr().String(), clientTLS)
if err != nil {
b.Fatalf("Dial failed: %v", err)
}
_, _ = conn.Write([]byte{0})
_ = conn.Close()
}
b.StopTimer()
listener.Close()
<-acceptDone
}
// BenchmarkOCSPStapling 基准测试 OCSP 装订性能。
//
// 测量启用 OCSP Stapling 后GetConfigForClient 回调中
// 获取和附加 OCSP 响应的开销。
func BenchmarkOCSPStapling(b *testing.B) {
// 直接测试 OCSPManager 的响应获取性能
// (自签证书没有 OCSP Server URL无法端到端测试
ocspMgr := NewOCSPManager(DefaultOCSPConfig())
ocspMgr.Start()
defer ocspMgr.Stop()
// 注册一个模拟序列号的状态
testSerial := "1234567890"
ocspMgr.mu.Lock()
ocspMgr.responses[testSerial] = &ocspResponse{
response: make([]byte, 1024),
thisUpdate: time.Now(),
nextUpdate: time.Now().Add(time.Hour),
status: statusValid,
fetchedAt: time.Now(),
errors: 0,
}
ocspMgr.mu.Unlock()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
resp := ocspMgr.GetOCSPResponse(testSerial)
if resp == nil {
b.Error("expected non-nil OCSP response")
}
}
})
}
// BenchmarkOCSPStapling_Miss 基准测试 OCSP 未命中性能。
//
// 测量查询不存在序列号时的开销,验证优雅降级不影响性能。
func BenchmarkOCSPStapling_Miss(b *testing.B) {
ocspMgr := NewOCSPManager(DefaultOCSPConfig())
ocspMgr.Start()
defer ocspMgr.Stop()
nonExistentSerial := "nonexistent"
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
resp := ocspMgr.GetOCSPResponse(nonExistentSerial)
if resp != nil {
b.Error("expected nil OCSP response for non-existent serial")
}
}
})
}
// BenchmarkOCSPStapling_GetStatus 基准测试 OCSP 状态查询性能。
//
// 测量 GetStatus 方法获取证书状态信息的开销。
// BenchmarkSessionResumption 基准测试使用 Session Ticket 的会话恢复性能。
//
// 使用 ClientSessionCache 实现会话恢复,
// 测量使用缓存 session 时的握手开销。
// BenchmarkSessionResumption_FullHandshake 基准测试完整握手(无会话恢复)。
//
// 作为对照组,测量没有 session ticket 的完整 TLS 握手开销,
// 与 BenchmarkSessionResumption 对比评估会话恢复的性能提升。
// BenchmarkSessionTicketManager_ApplyToTLSConfig 基准测试应用 Session Ticket 到 TLS 配置的开销。
func BenchmarkSessionTicketManager_ApplyToTLSConfig(b *testing.B) {
sessionMgr, err := NewSessionTicketManager(config.SessionTicketsConfig{
Enabled: true,
RotateInterval: time.Hour,
RetainKeys: 3,
})
if err != nil {
b.Fatalf("NewSessionTicketManager failed: %v", err)
}
defer sessionMgr.Stop()
for range 2 {
_ = sessionMgr.RotateKey()
}
baseTLS := &tls.Config{
Certificates: []tls.Certificate{},
MinVersion: tls.VersionTLS12,
}
b.ResetTimer()
for b.Loop() {
tlsCfg := baseTLS.Clone()
sessionMgr.ApplyToTLSConfig(tlsCfg)
}
}
// BenchmarkSNI_GetCertificate 基准测试 SNI 证书查找性能。
//
// 测量 GetCertificate 回调在多证书场景下的查找开销。
// BenchmarkCipherSuiteParsing 基准测试加密套件解析性能。
func BenchmarkCipherSuiteParsing(b *testing.B) {
ciphers := []string{
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
}
b.ResetTimer()
for b.Loop() {
_, err := sslutil.ParseCipherSuites(ciphers)
if err != nil {
b.Fatal(err)
}
}
}
// BenchmarkTLSVersionsParsing 基准测试 TLS 版本解析性能。
func BenchmarkTLSVersionsParsing(b *testing.B) {
protocols := []string{"TLSv1.2", "TLSv1.3"}
b.ResetTimer()
for b.Loop() {
_, _, err := sslutil.ParseTLSVersions(protocols)
if err != nil {
b.Fatal(err)
}
}
}