FastCGI implementation in OCaml
at main 4.1 kB view raw
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())