New Middleware: - CORS: Cross-Origin Resource Sharing with full configuration support - Cookie Helper: Cookie management with signed cookie support (HMAC-SHA256) - JWT: JSON Web Token authentication (HS256/384/512) - Bearer Auth: Simple Bearer token authentication - Compression: Gzip and deflate response compression - Rate Limiting: Request rate limiting with customizable stores - Validator: Schema-based request validation Features: - Context store for middleware data sharing (get/set/get_client_ip) - Middleware export module with utility functions - Comprehensive property-based tests for all middleware - Example file demonstrating all middleware usage Files Added: - cors.v, cookie.v, jwt.v, bearer_auth.v, compress.v, rate_limit.v, validator.v - middleware.v (unified exports) - examples/middleware_demo.v - tests/test_*.v (9 new test files) Documentation: - Updated README.md with detailed middleware usage guide - Added Schema Builder API documentation
321 lines
9.4 KiB
V
321 lines
9.4 KiB
V
import meiseayoung.hono
|
|
import net.http
|
|
import rand
|
|
import time
|
|
|
|
// Cookie Helper 属性测试
|
|
// Property-Based Testing for Cookie functionality
|
|
|
|
const test_iterations = 100
|
|
|
|
struct PropertyTestStats {
|
|
mut:
|
|
total_tests int
|
|
passed_tests int
|
|
failed_tests int
|
|
}
|
|
|
|
fn (mut stats PropertyTestStats) run_property_test(test_name string, test_func fn () bool) {
|
|
stats.total_tests++
|
|
print('🔬 ${test_name}... ')
|
|
|
|
if test_func() {
|
|
stats.passed_tests++
|
|
println('✅')
|
|
} else {
|
|
stats.failed_tests++
|
|
println('❌')
|
|
}
|
|
}
|
|
|
|
fn (stats PropertyTestStats) print_summary() {
|
|
println('\n=== Cookie Helper 属性测试总结 ===')
|
|
println('总测试数: ${stats.total_tests}')
|
|
println('通过: ${stats.passed_tests}')
|
|
println('失败: ${stats.failed_tests}')
|
|
|
|
if stats.failed_tests == 0 {
|
|
println('🎉 所有属性测试通过!')
|
|
} else {
|
|
println('⚠️ 有 ${stats.failed_tests} 个属性测试失败')
|
|
}
|
|
}
|
|
|
|
// 创建带 Cookie 头的测试 Context
|
|
fn create_context_with_cookies(cookie_header string) hono.Context {
|
|
mut headers := http.new_header()
|
|
if cookie_header.len > 0 {
|
|
headers.add_custom('Cookie', cookie_header) or {}
|
|
}
|
|
|
|
req := http.Request{
|
|
method: .get
|
|
url: '/test'
|
|
header: headers
|
|
}
|
|
return hono.Context.new(req, map[string]string{}, map[string]string{}, '')
|
|
}
|
|
|
|
// 创建空的测试 Context
|
|
fn create_empty_context() hono.Context {
|
|
req := http.Request{
|
|
method: .get
|
|
url: '/test'
|
|
}
|
|
return hono.Context.new(req, map[string]string{}, map[string]string{}, '')
|
|
}
|
|
|
|
|
|
// 生成随机的 Cookie 名称(只包含有效字符)
|
|
fn generate_random_cookie_name() string {
|
|
valid_chars := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'
|
|
len := rand.int_in_range(1, 20) or { 5 }
|
|
mut name := ''
|
|
for _ in 0 .. len {
|
|
idx := rand.int_in_range(0, valid_chars.len) or { 0 }
|
|
name += valid_chars[idx].ascii_str()
|
|
}
|
|
return name
|
|
}
|
|
|
|
// 生成随机的 Cookie 值(不包含分号和等号)
|
|
fn generate_random_cookie_value() string {
|
|
// 使用安全的字符集,避免特殊字符
|
|
valid_chars := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.'
|
|
len := rand.int_in_range(1, 50) or { 10 }
|
|
mut value := ''
|
|
for _ in 0 .. len {
|
|
idx := rand.int_in_range(0, valid_chars.len) or { 0 }
|
|
value += valid_chars[idx].ascii_str()
|
|
}
|
|
return value
|
|
}
|
|
|
|
// 生成随机的密钥
|
|
fn generate_random_secret() string {
|
|
chars := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*'
|
|
len := rand.int_in_range(16, 64) or { 32 }
|
|
mut secret := ''
|
|
for _ in 0 .. len {
|
|
idx := rand.int_in_range(0, chars.len) or { 0 }
|
|
secret += chars[idx].ascii_str()
|
|
}
|
|
return secret
|
|
}
|
|
|
|
// ============================================================================
|
|
// Property 3: Cookie Round-Trip Consistency
|
|
// Feature: builtin-middleware, Property 3: Cookie Round-Trip Consistency
|
|
// Validates: Requirements 2.1, 2.3
|
|
//
|
|
// *For any* cookie name and value, calling set_cookie followed by get_cookie
|
|
// on the same name SHALL return the original value.
|
|
// ============================================================================
|
|
fn test_property_3_cookie_roundtrip() bool {
|
|
rand.seed([u32(time.now().unix()), u32(12345)])
|
|
|
|
for i in 0 .. test_iterations {
|
|
name := generate_random_cookie_name()
|
|
value := generate_random_cookie_value()
|
|
|
|
// 设置 Cookie
|
|
mut ctx := create_empty_context()
|
|
hono.set_cookie(mut ctx, name, value)
|
|
|
|
// 获取 Set-Cookie 头中的值
|
|
set_cookie_header := ctx.headers['Set-Cookie'] or {
|
|
println(' Iteration ${i}: Failed to get Set-Cookie header')
|
|
return false
|
|
}
|
|
|
|
// 提取 Cookie 值
|
|
mut cookie_value := ''
|
|
parts := set_cookie_header.split(';')
|
|
if parts.len > 0 {
|
|
name_value := parts[0].trim_space()
|
|
eq_pos := name_value.index('=') or {
|
|
println(' Iteration ${i}: Invalid cookie format')
|
|
return false
|
|
}
|
|
cookie_value = name_value[eq_pos + 1..]
|
|
}
|
|
|
|
// 创建带 Cookie 的 Context 来验证
|
|
verify_ctx := create_context_with_cookies('${name}=${cookie_value}')
|
|
|
|
// 获取 Cookie 并验证
|
|
retrieved := hono.get_cookie(verify_ctx, name) or {
|
|
println(' Iteration ${i}: Failed to get cookie')
|
|
return false
|
|
}
|
|
|
|
if retrieved != value {
|
|
println(' Iteration ${i}: Value mismatch - expected "${value}", got "${retrieved}"')
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
|
|
// ============================================================================
|
|
// Property 4: Signed Cookie Round-Trip
|
|
// Feature: builtin-middleware, Property 4: Signed Cookie Round-Trip
|
|
// Validates: Requirements 2.7, 2.8
|
|
//
|
|
// *For any* cookie name, value, and secret, calling set_signed_cookie followed
|
|
// by get_signed_cookie with the same secret SHALL return the original value.
|
|
// ============================================================================
|
|
fn test_property_4_signed_cookie_roundtrip() bool {
|
|
rand.seed([u32(time.now().unix()), u32(54321)])
|
|
|
|
for i in 0 .. test_iterations {
|
|
name := generate_random_cookie_name()
|
|
value := generate_random_cookie_value()
|
|
secret := generate_random_secret()
|
|
|
|
// 设置签名 Cookie
|
|
mut ctx := create_empty_context()
|
|
hono.set_signed_cookie(mut ctx, name, value, secret) or {
|
|
println(' Iteration ${i}: Failed to set signed cookie: ${err}')
|
|
return false
|
|
}
|
|
|
|
// 获取 Set-Cookie 头中的值
|
|
set_cookie_header := ctx.headers['Set-Cookie'] or {
|
|
println(' Iteration ${i}: Failed to get Set-Cookie header')
|
|
return false
|
|
}
|
|
|
|
// 提取 Cookie 值(包含签名)
|
|
mut cookie_value := ''
|
|
parts := set_cookie_header.split(';')
|
|
if parts.len > 0 {
|
|
name_value := parts[0].trim_space()
|
|
eq_pos := name_value.index('=') or {
|
|
println(' Iteration ${i}: Invalid cookie format')
|
|
return false
|
|
}
|
|
cookie_value = name_value[eq_pos + 1..]
|
|
}
|
|
|
|
// 创建带签名 Cookie 的 Context 来验证
|
|
verify_ctx := create_context_with_cookies('${name}=${cookie_value}')
|
|
|
|
// 获取并验证签名 Cookie
|
|
retrieved := hono.get_signed_cookie(verify_ctx, name, secret) or {
|
|
println(' Iteration ${i}: Failed to get signed cookie: ${err}')
|
|
return false
|
|
}
|
|
|
|
if retrieved != value {
|
|
println(' Iteration ${i}: Value mismatch - expected "${value}", got "${retrieved}"')
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// ============================================================================
|
|
// Property 5: Signed Cookie Tamper Detection
|
|
// Feature: builtin-middleware, Property 5: Signed Cookie Tamper Detection
|
|
// Validates: Requirements 2.9
|
|
//
|
|
// *For any* signed cookie, modifying the cookie value or signature SHALL cause
|
|
// get_signed_cookie to return an error.
|
|
// ============================================================================
|
|
fn test_property_5_signed_cookie_tamper_detection() bool {
|
|
rand.seed([u32(time.now().unix()), u32(98765)])
|
|
|
|
for i in 0 .. test_iterations {
|
|
name := generate_random_cookie_name()
|
|
value := generate_random_cookie_value()
|
|
secret := generate_random_secret()
|
|
|
|
// 设置签名 Cookie
|
|
mut ctx := create_empty_context()
|
|
hono.set_signed_cookie(mut ctx, name, value, secret) or {
|
|
println(' Iteration ${i}: Failed to set signed cookie: ${err}')
|
|
return false
|
|
}
|
|
|
|
// 获取 Set-Cookie 头中的值
|
|
set_cookie_header := ctx.headers['Set-Cookie'] or {
|
|
println(' Iteration ${i}: Failed to get Set-Cookie header')
|
|
return false
|
|
}
|
|
|
|
// 提取 Cookie 值(包含签名)
|
|
mut cookie_value := ''
|
|
parts := set_cookie_header.split(';')
|
|
if parts.len > 0 {
|
|
name_value := parts[0].trim_space()
|
|
eq_pos := name_value.index('=') or {
|
|
println(' Iteration ${i}: Invalid cookie format')
|
|
return false
|
|
}
|
|
cookie_value = name_value[eq_pos + 1..]
|
|
}
|
|
|
|
// 篡改 Cookie 值(修改第一个字符)
|
|
if cookie_value.len > 1 {
|
|
// 找到签名分隔符
|
|
dot_pos := cookie_value.last_index('.') or {
|
|
println(' Iteration ${i}: No signature separator found')
|
|
return false
|
|
}
|
|
|
|
// 篡改值部分
|
|
original_value_part := cookie_value[..dot_pos]
|
|
signature_part := cookie_value[dot_pos..]
|
|
|
|
// 修改值的第一个字符
|
|
mut tampered_value := ''
|
|
if original_value_part.len > 0 {
|
|
first_char := original_value_part[0]
|
|
new_char := if first_char == `a` { 'b' } else { 'a' }
|
|
tampered_value = new_char + original_value_part[1..] + signature_part
|
|
} else {
|
|
tampered_value = 'x' + signature_part
|
|
}
|
|
|
|
// 创建带篡改 Cookie 的 Context
|
|
verify_ctx := create_context_with_cookies('${name}=${tampered_value}')
|
|
|
|
// 验证应该失败
|
|
if _ := hono.get_signed_cookie(verify_ctx, name, secret) {
|
|
println(' Iteration ${i}: Tampered cookie was accepted (should have been rejected)')
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
|
|
fn main() {
|
|
println('🚀 开始 Cookie Helper 属性测试...')
|
|
println('每个属性测试运行 ${test_iterations} 次迭代\n')
|
|
|
|
mut stats := PropertyTestStats{}
|
|
|
|
// 运行属性测试
|
|
// Feature: builtin-middleware, Property 3: Cookie Round-Trip Consistency
|
|
// Validates: Requirements 2.1, 2.3
|
|
stats.run_property_test('Property 3: Cookie Round-Trip Consistency', test_property_3_cookie_roundtrip)
|
|
|
|
// Feature: builtin-middleware, Property 4: Signed Cookie Round-Trip
|
|
// Validates: Requirements 2.7, 2.8
|
|
stats.run_property_test('Property 4: Signed Cookie Round-Trip', test_property_4_signed_cookie_roundtrip)
|
|
|
|
// Feature: builtin-middleware, Property 5: Signed Cookie Tamper Detection
|
|
// Validates: Requirements 2.9
|
|
stats.run_property_test('Property 5: Signed Cookie Tamper Detection', test_property_5_signed_cookie_tamper_detection)
|
|
|
|
// 打印测试总结
|
|
stats.print_summary()
|
|
}
|