FastCGI implementation in OCaml
1#!/usr/bin/env python3
2"""
3Validate that the generated FastCGI test cases are properly formatted.
4"""
5import struct
6import os
7import glob
8
9def parse_record_header(data):
10 """Parse a FastCGI record header."""
11 if len(data) < 8:
12 return None
13
14 version, record_type, request_id, content_length, padding_length, reserved = struct.unpack('>BBHHBB', data[:8])
15 return {
16 'version': version,
17 'type': record_type,
18 'request_id': request_id,
19 'content_length': content_length,
20 'padding_length': padding_length,
21 'reserved': reserved,
22 'total_length': 8 + content_length + padding_length
23 }
24
25def validate_file(filename):
26 """Validate a single test case file."""
27 print(f"\nValidating {filename}:")
28
29 with open(filename, 'rb') as f:
30 data = f.read()
31
32 if len(data) < 8:
33 print(f" ❌ File too short: {len(data)} bytes")
34 return False
35
36 # Parse all records in the file
37 offset = 0
38 record_count = 0
39
40 while offset < len(data):
41 if offset + 8 > len(data):
42 print(f" ❌ Incomplete header at offset {offset}")
43 return False
44
45 header = parse_record_header(data[offset:])
46 if not header:
47 print(f" ❌ Failed to parse header at offset {offset}")
48 return False
49
50 record_count += 1
51 print(f" Record {record_count}:")
52 print(f" Version: {header['version']}")
53 print(f" Type: {header['type']}")
54 print(f" Request ID: {header['request_id']}")
55 print(f" Content Length: {header['content_length']}")
56 print(f" Padding Length: {header['padding_length']}")
57 print(f" Reserved: {header['reserved']}")
58
59 # Validate header fields
60 if header['version'] != 1:
61 print(f" ❌ Invalid version: {header['version']}")
62 return False
63
64 if header['type'] < 1 or header['type'] > 11:
65 print(f" ❌ Invalid record type: {header['type']}")
66 return False
67
68 if header['reserved'] != 0:
69 print(f" ❌ Reserved field not zero: {header['reserved']}")
70 return False
71
72 # Check if we have enough data for content and padding
73 expected_end = offset + header['total_length']
74 if expected_end > len(data):
75 print(f" ❌ Not enough data: need {header['total_length']}, have {len(data) - offset}")
76 return False
77
78 # Extract content
79 content_start = offset + 8
80 content_end = content_start + header['content_length']
81 content = data[content_start:content_end]
82
83 # Extract padding
84 padding_start = content_end
85 padding_end = padding_start + header['padding_length']
86 padding = data[padding_start:padding_end]
87
88 print(f" Content: {len(content)} bytes")
89 if header['padding_length'] > 0:
90 print(f" Padding: {len(padding)} bytes")
91
92 # Show content preview for small records
93 if len(content) <= 32:
94 print(f" Content hex: {content.hex()}")
95 else:
96 print(f" Content preview: {content[:16].hex()}...")
97
98 print(f" ✅ Record valid")
99
100 offset = expected_end
101
102 print(f" ✅ File valid: {record_count} record(s), {len(data)} total bytes")
103 return True
104
105def main():
106 """Validate all test case files."""
107 test_files = glob.glob("*.bin")
108 test_files.sort()
109
110 print(f"Found {len(test_files)} test case files")
111
112 valid_count = 0
113 for filename in test_files:
114 if validate_file(filename):
115 valid_count += 1
116
117 print(f"\n{'='*50}")
118 print(f"Validation complete: {valid_count}/{len(test_files)} files valid")
119
120 if valid_count == len(test_files):
121 print("✅ All test cases are valid!")
122 else:
123 print("❌ Some test cases failed validation")
124 return 1
125
126 return 0
127
128if __name__ == "__main__":
129 import sys
130 sys.exit(main())