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