feat: add 7 built-in middleware components inspired by Hono.js

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
This commit is contained in:
meiseayoung
2026-01-04 11:11:15 +08:00
parent 44ba82c194
commit 4e2c6a90c3
23 changed files with 7156 additions and 89 deletions
+310
View File
@@ -0,0 +1,310 @@
import meiseayoung.hono
import rand
import time
// JWT 中间件属性测试
// Property-Based Testing for JWT 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=== JWT 中间件属性测试总结 ===')
println('总测试数: ${stats.total_tests}')
println('通过: ${stats.passed_tests}')
println('失败: ${stats.failed_tests}')
if stats.failed_tests == 0 {
println('🎉 所有属性测试通过!')
} else {
println('⚠️ 有 ${stats.failed_tests} 个属性测试失败')
}
}
// 生成随机的 subject
fn generate_random_subject() string {
chars := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
len := rand.int_in_range(5, 30) or { 10 }
mut subject := ''
for _ in 0 .. len {
idx := rand.int_in_range(0, chars.len) or { 0 }
subject += chars[idx].ascii_str()
}
return subject
}
// 生成随机的 issuer
fn generate_random_issuer() string {
prefixes := ['https://auth.', 'https://api.', 'https://']
domains := ['example.com', 'test.org', 'myapp.io', 'service.net']
prefix_idx := rand.int_in_range(0, prefixes.len) or { 0 }
domain_idx := rand.int_in_range(0, domains.len) or { 0 }
return prefixes[prefix_idx] + domains[domain_idx]
}
// 生成随机的密钥
fn generate_random_secret() string {
chars := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*'
len := rand.int_in_range(32, 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
}
// 生成随机的自定义声明
fn generate_random_claims() map[string]string {
mut claims := map[string]string{}
num_claims := rand.int_in_range(0, 5) or { 2 }
for i in 0 .. num_claims {
key := 'claim_${i}'
value := generate_random_subject()
claims[key] = value
}
return claims
}
// 生成随机的 JwtPayload
fn generate_random_payload() hono.JwtPayload {
now := time.now().unix()
return hono.JwtPayload{
sub: generate_random_subject()
iss: generate_random_issuer()
aud: 'test-audience'
exp: now + 3600 // 1 hour from now
nbf: now - 60 // 1 minute ago
iat: now
jti: generate_random_subject()
claims: generate_random_claims()
}
}
// 随机选择算法
fn random_algorithm() hono.JwtAlgorithm {
idx := rand.int_in_range(0, 3) or { 0 }
return match idx {
0 { hono.JwtAlgorithm.hs256 }
1 { hono.JwtAlgorithm.hs384 }
else { hono.JwtAlgorithm.hs512 }
}
}
// ============================================================================
// Property 6: JWT Sign-Verify Round-Trip
// Feature: builtin-middleware, Property 6: JWT Sign-Verify Round-Trip
// Validates: Requirements 3.1, 3.4, 3.11
//
// *For any* valid JwtPayload and secret, calling sign_jwt followed by verify_jwt
// with the same secret SHALL return an equivalent payload.
// ============================================================================
fn test_property_6_jwt_sign_verify_roundtrip() bool {
rand.seed([u32(time.now().unix()), u32(12345)])
for i in 0 .. test_iterations {
payload := generate_random_payload()
secret := generate_random_secret()
alg := random_algorithm()
// 签名 JWT
token := hono.sign_jwt(payload, secret, alg) or {
println(' Iteration ${i}: Failed to sign JWT: ${err}')
return false
}
// 验证 JWT
verified_payload := hono.verify_jwt(token, secret, alg) or {
println(' Iteration ${i}: Failed to verify JWT: ${err}')
return false
}
// 验证 payload 字段
if verified_payload.sub != payload.sub {
println(' Iteration ${i}: Subject mismatch - expected "${payload.sub}", got "${verified_payload.sub}"')
return false
}
if verified_payload.iss != payload.iss {
println(' Iteration ${i}: Issuer mismatch - expected "${payload.iss}", got "${verified_payload.iss}"')
return false
}
if verified_payload.aud != payload.aud {
println(' Iteration ${i}: Audience mismatch - expected "${payload.aud}", got "${verified_payload.aud}"')
return false
}
if verified_payload.exp != payload.exp {
println(' Iteration ${i}: Exp mismatch - expected ${payload.exp}, got ${verified_payload.exp}')
return false
}
if verified_payload.nbf != payload.nbf {
println(' Iteration ${i}: Nbf mismatch - expected ${payload.nbf}, got ${verified_payload.nbf}')
return false
}
if verified_payload.iat != payload.iat {
println(' Iteration ${i}: Iat mismatch - expected ${payload.iat}, got ${verified_payload.iat}')
return false
}
if verified_payload.jti != payload.jti {
println(' Iteration ${i}: JTI mismatch - expected "${payload.jti}", got "${verified_payload.jti}"')
return false
}
// 验证自定义声明
for key, value in payload.claims {
if key !in verified_payload.claims {
println(' Iteration ${i}: Missing claim "${key}"')
return false
}
if verified_payload.claims[key] != value {
println(' Iteration ${i}: Claim "${key}" mismatch - expected "${value}", got "${verified_payload.claims[key]}"')
return false
}
}
}
return true
}
// ============================================================================
// Property 7: JWT Expiration Enforcement
// Feature: builtin-middleware, Property 7: JWT Expiration Enforcement
// Validates: Requirements 3.2, 3.9
//
// *For any* JWT with an exp claim in the past, verify_jwt SHALL return an error
// when verifyOptions.exp is true.
// ============================================================================
fn test_property_7_jwt_expiration_enforcement() bool {
rand.seed([u32(time.now().unix()), u32(54321)])
for i in 0 .. test_iterations {
now := time.now().unix()
// 创建已过期的 payload
mut payload := generate_random_payload()
// 设置过期时间为过去(1秒到1小时前)
seconds_ago := rand.int_in_range(1, 3600) or { 60 }
payload.exp = now - seconds_ago
secret := generate_random_secret()
alg := random_algorithm()
// 签名 JWT
token := hono.sign_jwt(payload, secret, alg) or {
println(' Iteration ${i}: Failed to sign JWT: ${err}')
return false
}
// 使用带过期验证的选项验证 JWT
verify_options := hono.JwtVerifyOptions{
exp: true
nbf: false
iat: false
}
// 验证应该失败(因为 token 已过期)
if _ := hono.verify_jwt_with_options(token, secret, alg, verify_options) {
println(' Iteration ${i}: Expired JWT was accepted (should have been rejected)')
return false
}
}
return true
}
// ============================================================================
// Property 8: JWT Algorithm Consistency
// Feature: builtin-middleware, Property 8: JWT Algorithm Consistency
// Validates: Requirements 3.7
//
// *For any* JWT signed with algorithm A, verification with algorithm B
// (where A ≠ B) SHALL fail.
// ============================================================================
fn test_property_8_jwt_algorithm_consistency() bool {
rand.seed([u32(time.now().unix()), u32(98765)])
algorithms := [hono.JwtAlgorithm.hs256, hono.JwtAlgorithm.hs384, hono.JwtAlgorithm.hs512]
for i in 0 .. test_iterations {
payload := generate_random_payload()
secret := generate_random_secret()
// 随机选择签名算法
sign_alg_idx := rand.int_in_range(0, algorithms.len) or { 0 }
sign_alg := algorithms[sign_alg_idx]
// 选择不同的验证算法
mut verify_alg_idx := rand.int_in_range(0, algorithms.len) or { 0 }
// 确保验证算法与签名算法不同
for verify_alg_idx == sign_alg_idx {
verify_alg_idx = rand.int_in_range(0, algorithms.len) or { 0 }
}
verify_alg := algorithms[verify_alg_idx]
// 签名 JWT
token := hono.sign_jwt(payload, secret, sign_alg) or {
println(' Iteration ${i}: Failed to sign JWT: ${err}')
return false
}
// 使用不同算法验证应该失败
if _ := hono.verify_jwt(token, secret, verify_alg) {
println(' Iteration ${i}: JWT verified with wrong algorithm (signed with ${sign_alg}, verified with ${verify_alg})')
return false
}
}
return true
}
fn main() {
println('🚀 开始 JWT 中间件属性测试...')
println('每个属性测试运行 ${test_iterations} 次迭代\n')
mut stats := PropertyTestStats{}
// 运行属性测试
// Feature: builtin-middleware, Property 6: JWT Sign-Verify Round-Trip
// Validates: Requirements 3.1, 3.4, 3.11
stats.run_property_test('Property 6: JWT Sign-Verify Round-Trip', test_property_6_jwt_sign_verify_roundtrip)
// Feature: builtin-middleware, Property 7: JWT Expiration Enforcement
// Validates: Requirements 3.2, 3.9
stats.run_property_test('Property 7: JWT Expiration Enforcement', test_property_7_jwt_expiration_enforcement)
// Feature: builtin-middleware, Property 8: JWT Algorithm Consistency
// Validates: Requirements 3.7
stats.run_property_test('Property 8: JWT Algorithm Consistency', test_property_8_jwt_algorithm_consistency)
// 打印测试总结
stats.print_summary()
}