|
| 1 | +#!/usr/bin/env ruby |
| 2 | +# frozen_string_literal: true |
| 3 | + |
| 4 | +# Released under the MIT License. |
| 5 | +# Copyright, 2025, by Samuel Williams. |
| 6 | + |
| 7 | +require_relative "../lib/protocol/http/headers" |
| 8 | +require "digest" |
| 9 | + |
| 10 | +# Example: Using various headers suitable for trailers |
| 11 | +puts "HTTP Trailers - Suitable Headers Example" |
| 12 | +puts "=" * 50 |
| 13 | + |
| 14 | +# Create a new headers collection |
| 15 | +headers = Protocol::HTTP::Headers.new |
| 16 | + |
| 17 | +# Add regular response headers |
| 18 | +headers.add("content-type", "application/json") |
| 19 | +headers.add("content-length", "2048") |
| 20 | + |
| 21 | +# Enable trailers for headers that are calculated during response generation |
| 22 | +headers.trailer! |
| 23 | + |
| 24 | +puts "Regular Headers:" |
| 25 | +headers.each do |key, value| |
| 26 | + next if headers.trailer? && headers.trailer.any? {|tk, _| tk == key} |
| 27 | + puts " #{key}: #{value}" |
| 28 | +end |
| 29 | + |
| 30 | +puts "\nSimulating response generation and trailer calculation..." |
| 31 | + |
| 32 | +# 1. Server-Timing - Performance metrics calculated during processing |
| 33 | +puts "\n1. Server-Timing Header:" |
| 34 | +server_timing = Protocol::HTTP::Header::ServerTiming.new |
| 35 | +server_timing << "db;dur=45.2;desc=\"Database query\"" |
| 36 | +server_timing << "cache;dur=12.8;desc=\"Redis lookup\"" |
| 37 | +server_timing << "render;dur=23.5;desc=\"JSON serialization\"" |
| 38 | + |
| 39 | +headers.add("server-timing", server_timing.to_s) |
| 40 | +puts " Added: #{server_timing}" |
| 41 | + |
| 42 | +# 2. Digest - Content integrity calculated after body generation |
| 43 | +puts "\n2. Digest Header:" |
| 44 | +response_body = '{"message": "Hello, World!", "timestamp": "2025-09-19T06:18:21Z"}' |
| 45 | +sha256_digest = Digest::SHA256.base64digest(response_body) |
| 46 | +md5_digest = Digest::MD5.hexdigest(response_body) |
| 47 | + |
| 48 | +digest = Protocol::HTTP::Header::Digest.new |
| 49 | +digest << "sha-256=#{sha256_digest}" |
| 50 | +digest << "md5=#{md5_digest}" |
| 51 | + |
| 52 | +headers.add("digest", digest.to_s) |
| 53 | +puts " Added: #{digest}" |
| 54 | +puts " Response body: #{response_body}" |
| 55 | + |
| 56 | +# 3. Custom Application Header - Application-specific metadata |
| 57 | +puts "\n3. Custom Application Header:" |
| 58 | +headers.add("x-processing-stats", "requests=1250, cache_hits=892, errors=0") |
| 59 | +puts " Added: x-processing-stats=requests=1250, cache_hits=892, errors=0" |
| 60 | + |
| 61 | +# 4. Date - Response completion timestamp |
| 62 | +puts "\n4. Date Header:" |
| 63 | +completion_time = Time.now |
| 64 | +headers.add("date", completion_time.httpdate) |
| 65 | +puts " Added: #{completion_time.httpdate}" |
| 66 | + |
| 67 | +# 5. ETag - Content-based entity tag (when calculated from response) |
| 68 | +puts "\n5. ETag Header:" |
| 69 | +etag_value = "\"#{Digest::SHA1.hexdigest(response_body)[0..15]}\"" |
| 70 | +headers.add("etag", etag_value) |
| 71 | +puts " Added: #{etag_value}" |
| 72 | + |
| 73 | +puts "\nFinal Trailer Headers (sent after response body):" |
| 74 | +puts "-" * 50 |
| 75 | +headers.trailer do |key, value| |
| 76 | + puts " #{key}: #{value}" |
| 77 | +end |
| 78 | + |
| 79 | +puts "\nWhy These Headers Are Perfect for Trailers:" |
| 80 | +puts "-" * 45 |
| 81 | +puts "• Server-Timing: Performance metrics collected during processing" |
| 82 | +puts "• Digest: Content hashes calculated after body generation" |
| 83 | +puts "• Custom Headers: Application-specific metadata generated during response" |
| 84 | +puts "• Date: Completion timestamp when response finishes" |
| 85 | +puts "• ETag: Content-based tags when derived from response body" |
| 86 | + |
| 87 | +puts "\nBenefits of Using Trailers:" |
| 88 | +puts "• No need to buffer entire response to calculate metadata" |
| 89 | +puts "• Streaming-friendly - can start sending body immediately" |
| 90 | +puts "• Perfect for large responses where metadata depends on content" |
| 91 | +puts "• Maintains HTTP semantics while enabling efficient processing" |
| 92 | + |
| 93 | +# Demonstrate header integration and parsing |
| 94 | +puts "\nHeader Integration Examples:" |
| 95 | +puts "-" * 30 |
| 96 | + |
| 97 | +# Show that these headers work normally in the main header section too |
| 98 | +normal_headers = Protocol::HTTP::Headers.new |
| 99 | +normal_headers.add("server-timing", "total;dur=150.5") |
| 100 | +normal_headers.add("digest", "sha-256=abc123") |
| 101 | +normal_headers.add("x-cache-status", "hit") |
| 102 | + |
| 103 | +puts "Normal headers (not trailers):" |
| 104 | +normal_headers.each do |key, value| |
| 105 | + puts " #{key}: #{value}" |
| 106 | +end |
| 107 | + |
| 108 | +puts "\nParsing capabilities:" |
| 109 | +parsed_digest = Protocol::HTTP::Header::Digest.new("sha-256=#{sha256_digest}, md5=#{md5_digest}") |
| 110 | +entries = parsed_digest.entries |
| 111 | +puts "• Parsed digest entries: #{entries.size}" |
| 112 | +puts "• First algorithm: #{entries.first.algorithm}" |
| 113 | +puts "• Algorithms: #{entries.map(&:algorithm).join(', ')}" |
| 114 | + |
| 115 | +parsed_timing = Protocol::HTTP::Header::ServerTiming.new("db;dur=25.4, cache;dur=8.2;desc=\"Redis hit\"") |
| 116 | +timing_metrics = parsed_timing.metrics |
| 117 | +puts "• Parsed timing metrics: #{timing_metrics.size}" |
| 118 | +puts "• First metric: #{timing_metrics.first.name} (#{timing_metrics.first.duration}ms)" |
0 commit comments