Class: Lutaml::Store::HttpHeaderProcessor
- Inherits:
-
Object
- Object
- Lutaml::Store::HttpHeaderProcessor
- Defined in:
- lib/lutaml/store/http_header_processor.rb
Class Method Summary collapse
-
.build_conditional_headers(cache_entry, original_headers) ⇒ Object
Build conditional request headers.
-
.cache_entry_matches?(cache_entry, request_headers, config) ⇒ Boolean
Check if cache entry matches request (considering Vary).
-
.calculate_expiry(response_headers, cached_at, default_ttl) ⇒ Object
Calculate expiry time based on HTTP headers.
-
.extract_vary_headers(request_headers, vary_header_names) ⇒ Object
Extract vary headers from request headers.
-
.fresh?(cached_at, max_age, expires_at) ⇒ Boolean
Check if response is fresh based on age.
-
.generate_cache_key(method, url, vary_headers = {}, ignore_params = []) ⇒ Object
Generate cache key from request details.
-
.has_caching_directives?(response_headers) ⇒ Boolean
Check if response has caching directives.
-
.normalize_header_name(name) ⇒ Object
Normalize header name for consistent storage.
-
.parse_cache_control(header_value) ⇒ Object
Parse Cache-Control header according to RFC 7234.
-
.parse_last_modified(header_value) ⇒ Object
Parse Last-Modified header.
-
.parse_vary_header(vary_header) ⇒ Object
Parse Vary header.
-
.should_cache_response?(status_code, response_headers) ⇒ Boolean
Determine if response should be cached.
Class Method Details
.build_conditional_headers(cache_entry, original_headers) ⇒ Object
Build conditional request headers
117 118 119 120 121 122 123 124 125 |
# File 'lib/lutaml/store/http_header_processor.rb', line 117 def self.build_conditional_headers(cache_entry, original_headers) conditional_headers = original_headers.dup conditional_headers["If-None-Match"] = cache_entry.etag if cache_entry.etag conditional_headers["If-Modified-Since"] = cache_entry.last_modified.httpdate if cache_entry.last_modified conditional_headers end |
.cache_entry_matches?(cache_entry, request_headers, config) ⇒ Boolean
Check if cache entry matches request (considering Vary)
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/lutaml/store/http_header_processor.rb', line 139 def self.cache_entry_matches?(cache_entry, request_headers, config) return true if cache_entry.vary_headers.empty? # Extract vary headers from current request current_vary = extract_vary_headers(request_headers, cache_entry.vary_headers) # Compare with cached vary headers cache_entry.vary_headers.each do |header_name| cached_value = cache_entry.request_headers[header_name.downcase] current_value = current_vary[header_name.downcase] # Skip comparison for ignored headers next if config.should_ignore_vary_header?(header_name) return false if cached_value != current_value end true end |
.calculate_expiry(response_headers, cached_at, default_ttl) ⇒ Object
Calculate expiry time based on HTTP headers
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/lutaml/store/http_header_processor.rb', line 31 def self.calculate_expiry(response_headers, cached_at, default_ttl) cache_control = parse_cache_control(response_headers["cache-control"]) # Check for explicit expiry if (expires_header = response_headers["expires"]) begin return Time.parse(expires_header) rescue StandardError nil end end # Check for max-age if (max_age = cache_control["max-age"]) return cached_at + max_age end # Fall back to default TTL cached_at + default_ttl end |
.extract_vary_headers(request_headers, vary_header_names) ⇒ Object
Extract vary headers from request headers
95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/lutaml/store/http_header_processor.rb', line 95 def self.extract_vary_headers(request_headers, vary_header_names) return {} if vary_header_names.empty? vary_headers = {} vary_header_names.each do |header_name| normalized_name = header_name.downcase # Find header with case-insensitive matching actual_header = request_headers.find { |k, _| k.downcase == normalized_name } vary_headers[normalized_name] = actual_header[1] if actual_header end vary_headers end |
.fresh?(cached_at, max_age, expires_at) ⇒ Boolean
Check if response is fresh based on age
109 110 111 112 113 114 |
# File 'lib/lutaml/store/http_header_processor.rb', line 109 def self.fresh?(cached_at, max_age, expires_at) return false if expires_at && Time.now > expires_at return false if max_age && (Time.now - cached_at) > max_age true end |
.generate_cache_key(method, url, vary_headers = {}, ignore_params = []) ⇒ Object
Generate cache key from request details
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/lutaml/store/http_header_processor.rb', line 64 def self.generate_cache_key(method, url, vary_headers = {}, ignore_params = []) uri = URI.parse(url) # Normalize query parameters if uri.query params = URI.decode_www_form(uri.query) # Filter out ignored parameters params = params.reject { |key, _| ignore_params.include?(key) } params = params.sort uri.query = params.empty? ? nil : URI.encode_www_form(params) end base_key = "#{method.upcase}:#{uri}" # Add vary headers to key if present if vary_headers.any? vary_suffix = vary_headers.sort.map { |k, v| "#{k}:#{v}" }.join("|") base_key += "|#{Digest::SHA256.hexdigest(vary_suffix)[0..8]}" end base_key end |
.has_caching_directives?(response_headers) ⇒ Boolean
Check if response has caching directives
165 166 167 168 169 170 171 172 |
# File 'lib/lutaml/store/http_header_processor.rb', line 165 def self.has_caching_directives?(response_headers) return true if response_headers["cache-control"] return true if response_headers["expires"] return true if response_headers["etag"] return true if response_headers["last-modified"] false end |
.normalize_header_name(name) ⇒ Object
Normalize header name for consistent storage
160 161 162 |
# File 'lib/lutaml/store/http_header_processor.rb', line 160 def self.normalize_header_name(name) name.to_s.downcase.strip end |
.parse_cache_control(header_value) ⇒ Object
Parse Cache-Control header according to RFC 7234
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/lutaml/store/http_header_processor.rb', line 11 def self.parse_cache_control(header_value) return {} unless header_value directives = {} header_value.split(",").each do |directive| key, value = directive.strip.split("=", 2) key = key.strip.downcase if value # Handle quoted values value = value.strip.gsub(/^"(.*)"$/, '\1') directives[key] = value.match?(/^\d+$/) ? value.to_i : value else directives[key] = true end end directives end |
.parse_last_modified(header_value) ⇒ Object
Parse Last-Modified header
128 129 130 131 132 133 134 135 136 |
# File 'lib/lutaml/store/http_header_processor.rb', line 128 def self.parse_last_modified(header_value) return nil unless header_value begin Time.parse(header_value) rescue StandardError nil end end |
.parse_vary_header(vary_header) ⇒ Object
Parse Vary header
88 89 90 91 92 |
# File 'lib/lutaml/store/http_header_processor.rb', line 88 def self.parse_vary_header(vary_header) return [] unless vary_header vary_header.split(",").map(&:strip).map(&:downcase) end |
.should_cache_response?(status_code, response_headers) ⇒ Boolean
Determine if response should be cached
53 54 55 56 57 58 59 60 61 |
# File 'lib/lutaml/store/http_header_processor.rb', line 53 def self.should_cache_response?(status_code, response_headers) return false if status_code < 200 || status_code >= 400 cache_control = parse_cache_control(response_headers["cache-control"]) return false if cache_control["no-store"] return false if cache_control["private"] # Unless explicitly allowing private true end |