FastCGI implementation in OCaml
1(** Filter role implementation for FastCGI. 2 3 The Filter role processes data streams with filtering capabilities. 4 A Filter FastCGI application receives all the information associated with 5 an HTTP request, plus an extra stream of data from a file stored on the 6 web server, and generates a "filtered" version of the data stream as an 7 HTTP response. 8 9 This role is useful for: 10 - Dynamic content transformation (e.g., server-side includes, templating) 11 - Format conversion (e.g., Markdown to HTML, image resizing) 12 - Content compression and optimization 13 - Real-time content modification based on request context 14 15 A Filter is similar in functionality to a Responder that takes a data file 16 as a parameter. The key difference is that with a Filter, both the data file 17 and the Filter itself can be access controlled using the web server's access 18 control mechanisms, while a Responder that takes the name of a data file as 19 a parameter must perform its own access control checks on the data file. 20 21 The web server presents the Filter with environment variables first, then 22 standard input (normally form POST data), and finally the data file input 23 that needs to be processed. *) 24 25(** {1 Filter Types} *) 26 27(** Filter request with data stream. 28 29 Represents a complete Filter request including both the HTTP request 30 context and the data stream that needs to be processed. *) 31type 'a filter_request = { 32 request : 'a Fastcgi_types.request; (** Base FastCGI request containing HTTP context, 33 parameters, and form data. *) 34 data_stream : 'a Eio.Flow.source; (** File data stream to be filtered. 35 This is the content that needs processing. *) 36 data_last_modified : float option; (** File modification time as seconds since epoch. 37 Useful for caching and conditional requests. *) 38 data_length : int option; (** Expected length of the data stream in bytes. 39 May be None if length is unknown. *) 40} 41 42(** Filter metadata about the data being processed. *) 43type filter_metadata = { 44 filename : string option; (** Original filename *) 45 mime_type : string option; (** MIME type of data *) 46 last_modified : float option; (** Last modification time *) 47 size : int option; (** Data size in bytes *) 48 etag : string option; (** Entity tag for caching *) 49} 50 51(** Filter context with additional processing information. *) 52type 'a filter_context = { 53 filter_request : 'a filter_request; (** Request and data *) 54 metadata : filter_metadata; (** File metadata *) 55 cache_control : cache_policy; (** Caching policy *) 56} 57 58(** Cache policy for filtered content. *) 59and cache_policy = 60 | No_cache (** No caching *) 61 | Cache_for of int (** Cache for N seconds *) 62 | Cache_until of float (** Cache until timestamp *) 63 | Conditional_cache of (filter_metadata -> bool) (** Conditional caching *) 64 65(** {1 Conversion Functions} *) 66 67(** Convert FastCGI request to filter request. 68 69 Extracts filter-specific information from FastCGI streams 70 and creates a filter request object. 71 72 @param request FastCGI request 73 @return Filter request with data stream *) 74val request_of_fastcgi : 'a Fastcgi_types.request -> 'a filter_request 75 76(** Create filter context with metadata. 77 78 @param filter_request Filter request 79 @param metadata File metadata 80 @param cache_control Caching policy 81 @return Filter context *) 82val context_of_request : 83 'a filter_request -> 84 metadata:filter_metadata -> 85 cache_control:cache_policy -> 86 'a filter_context 87 88(** {1 Handler Utilities} *) 89 90(** Filter handler type. *) 91type 'a filter_handler = 'a Fastcgi_types.request -> 'a Fastcgi_types.response -> Fastcgi_types.response_result 92 93(** Convenience handler wrapper for filter handlers. 94 95 Converts a filter handler function into a FastCGI handler. 96 This allows writing handlers that work with filter request/response 97 types instead of raw FastCGI types. 98 99 @param handler Filter handler function 100 @return FastCGI filter handler *) 101 102val filter_handler : 103 ('a filter_request -> 'a Fastcgi_types.response -> unit) -> 104 'a filter_handler 105 106(** Context-aware filter handler. 107 108 Similar to filter_handler but provides additional context 109 including metadata and caching information. 110 111 @param handler Context-aware filter handler 112 @return FastCGI filter handler *) 113val context_filter_handler : 114 ('a filter_context -> 'a Fastcgi_types.response -> unit) -> 115 'a filter_handler 116 117(** {1 Common Filter Operations} *) 118 119(** Text processing filters. *) 120module Text : sig 121 (** Convert text encoding. 122 123 @param from_encoding Source encoding 124 @param to_encoding Target encoding 125 @param input Text stream 126 @param output Filtered stream *) 127 val convert_encoding : 128 from_encoding:string -> 129 to_encoding:string -> 130 input:'a Eio.Flow.source -> 131 output:'a Eio.Flow.sink -> 132 unit 133 134 (** Find and replace text patterns. 135 136 @param pattern Regular expression pattern 137 @param replacement Replacement text 138 @param input Text stream 139 @param output Filtered stream *) 140 val find_replace : 141 pattern:string -> 142 replacement:string -> 143 input:'a Eio.Flow.source -> 144 output:'a Eio.Flow.sink -> 145 unit 146 147 (** Template substitution. 148 149 @param variables Variable substitutions 150 @param input Template stream 151 @param output Processed stream *) 152 val template_substitute : 153 variables:(string * string) list -> 154 input:'a Eio.Flow.source -> 155 output:'a Eio.Flow.sink -> 156 unit 157 158 (** Markdown to HTML conversion. 159 160 @param options Markdown processing options 161 @param input Markdown stream 162 @param output HTML stream *) 163 val markdown_to_html : 164 options:string list -> 165 input:'a Eio.Flow.source -> 166 output:'a Eio.Flow.sink -> 167 unit 168end 169 170(** Image processing filters. *) 171module Image : sig 172 (** Image resize operation. 173 174 @param width Target width 175 @param height Target height 176 @param input Image stream 177 @param output Resized stream *) 178 val resize : 179 width:int -> 180 height:int -> 181 input:'a Eio.Flow.source -> 182 output:'a Eio.Flow.sink -> 183 unit 184 185 (** Image format conversion. 186 187 @param format Target format (jpeg, png, etc.) 188 @param quality Quality setting (for lossy formats) 189 @param input Image stream 190 @param output Converted stream *) 191 val convert_format : 192 format:string -> 193 quality:int option -> 194 input:'a Eio.Flow.source -> 195 output:'a Eio.Flow.sink -> 196 unit 197 198 (** Image compression. 199 200 @param level Compression level 201 @param input Image stream 202 @param output Compressed stream *) 203 val compress : 204 level:int -> 205 input:'a Eio.Flow.source -> 206 output:'a Eio.Flow.sink -> 207 unit 208end 209 210(** Data compression filters. *) 211module Compression : sig 212 (** Gzip compression. 213 214 @param level Compression level (1-9) 215 @param input Data stream 216 @param output Compressed stream *) 217 val gzip : 218 level:int -> 219 input:'a Eio.Flow.source -> 220 output:'a Eio.Flow.sink -> 221 unit 222 223 (** Brotli compression. 224 225 @param level Compression level 226 @param input Data stream 227 @param output Compressed stream *) 228 val brotli : 229 level:int -> 230 input:'a Eio.Flow.source -> 231 output:'a Eio.Flow.sink -> 232 unit 233 234 (** Auto-detect and compress. 235 236 @param preferred_formats Preferred compression formats 237 @param accept_encoding Client Accept-Encoding header 238 @param input Data stream 239 @param output Compressed stream *) 240 val auto_compress : 241 preferred_formats:string list -> 242 accept_encoding:string option -> 243 input:'a Eio.Flow.source -> 244 output:'a Eio.Flow.sink -> 245 string option (** Returns content-encoding used *) 246end 247 248(** {1 Caching and Optimization} *) 249 250(** Check if content should be served from cache. 251 252 @param metadata File metadata 253 @param if_modified_since Client If-Modified-Since header 254 @param if_none_match Client If-None-Match header 255 @return True if content is not modified *) 256val check_not_modified : 257 metadata:filter_metadata -> 258 if_modified_since:string option -> 259 if_none_match:string option -> 260 bool 261 262(** Generate appropriate cache headers. 263 264 @param metadata File metadata 265 @param cache_policy Caching policy 266 @return List of cache-related headers *) 267val generate_cache_headers : 268 metadata:filter_metadata -> 269 cache_policy:cache_policy -> 270 (string * string) list 271 272(** {1 Streaming Operations} *) 273 274(** Stream processor for chunk-by-chunk filtering. *) 275type 'a stream_processor = { 276 process_chunk : bytes -> bytes option; (** Process a data chunk *) 277 finalize : unit -> bytes option; (** Finalize and get remaining data *) 278 reset : unit -> unit; (** Reset processor state *) 279} 280 281(** Create a streaming filter. 282 283 @param processor Stream processor 284 @param input Source stream 285 @param output Target stream *) 286val streaming_filter : 287 processor:'a stream_processor -> 288 input:'a Eio.Flow.source -> 289 output:'a Eio.Flow.sink -> 290 unit 291 292(** Chain multiple filters together. 293 294 @param filters List of filter functions 295 @param input Source stream 296 @param output Target stream *) 297val chain_filters : 298 filters:(('a Eio.Flow.source -> 'a Eio.Flow.sink -> unit) list) -> 299 input:'a Eio.Flow.source -> 300 output:'a Eio.Flow.sink -> 301 unit 302 303(** {1 Error Handling} *) 304 305(** Filter-specific errors. *) 306type filter_error = 307 | Data_corruption of string (** Data corruption detected *) 308 | Unsupported_format of string (** Unsupported file format *) 309 | Processing_failed of string (** Filter processing failed *) 310 | Resource_exhausted of string (** Out of memory/disk space *) 311 312(** Filter exception. *) 313exception Filter_error of filter_error 314 315(** Convert filter error to string. *) 316val filter_error_to_string : filter_error -> string 317 318(** Safe filter wrapper that handles errors gracefully. 319 320 @param fallback Fallback function if filter fails 321 @param filter Primary filter function 322 @param input Source stream 323 @param output Target stream *) 324val safe_filter : 325 fallback:('a Eio.Flow.source -> 'a Eio.Flow.sink -> unit) -> 326 filter:('a Eio.Flow.source -> 'a Eio.Flow.sink -> unit) -> 327 input:'a Eio.Flow.source -> 328 output:'a Eio.Flow.sink -> 329 unit