tangled
alpha
login
or
join now
anil.recoil.org
/
ocaml-requests
1
fork
atom
A batteries included HTTP/1.1 client in OCaml
1
fork
atom
overview
issues
pulls
pipelines
features
anil.recoil.org
2 months ago
42037660
60979f57
+224
-128
1 changed file
expand all
collapse all
unified
split
RECOMMENDATIONS.md
+224
-128
RECOMMENDATIONS.md
···
2
2
3
3
> Auto-generated summary of recommendations from analyzing HTTP client libraries across multiple languages.
4
4
5
5
+
## Implementation Status Summary
6
6
+
7
7
+
| Status | Count | Description |
8
8
+
|--------|-------|-------------|
9
9
+
| ✅ Implemented | 15 | Fully implemented and tested |
10
10
+
| 🔧 Partial | 1 | Partially implemented |
11
11
+
| ❌ Not Started | 14 | Not yet implemented |
12
12
+
13
13
+
### Implemented Features
14
14
+
15
15
+
| # | Feature | Location |
16
16
+
|---|---------|----------|
17
17
+
| 1 | Strip sensitive headers on cross-origin redirects | `lib/requests.ml`, `lib/one.ml` |
18
18
+
| 3 | Enforce HTTPS for sensitive authentication | `lib/auth.ml` (`apply_secure`, `Insecure_auth` error) |
19
19
+
| 4 | Content-Length vs Transfer-Encoding precedence | `lib/http_read.ml` (RFC 9112 warning) |
20
20
+
| 5 | Cap maximum Retry-After delay | `lib/retry.ml` (`backoff_max` parameter) |
21
21
+
| 11 | Base URL support with relative path resolution | `lib/requests.ml` (`base_url`, `resolve_url`) |
22
22
+
| 12 | Detailed timing metrics | `lib/timing.ml` (new module) |
23
23
+
| 13 | Stale connection detection | `ocaml-conpool/lib/conpool.ml` (`is_healthy`) |
24
24
+
| 14 | Custom retry condition predicates | `lib/retry.ml` (`response_predicate`, `exception_predicate`) |
25
25
+
| 19 | Link header parsing for pagination | `lib/link.ml` (new module) |
26
26
+
| 21 | Result-based error_for_status variant | `lib/response.ml` (`check_status`) |
27
27
+
| 24 | XSRF/CSRF token handling | `lib/requests.ml` (`apply_xsrf_token`) |
28
28
+
| 25 | Decompression bomb protection | `lib/http_client.ml` (`check_decompression_limits`) |
29
29
+
| 26 | HTTP version in response metadata | `lib/http_read.ml` (`http_version` type) |
30
30
+
| 28 | Restrict URL protocols to HTTP/HTTPS | `lib/requests.ml`, `lib/one.ml` (`validate_redirect_url`) |
31
31
+
| 29 | Path parameter templating | `lib/requests.ml` (`substitute_path_params`) |
32
32
+
| 30 | Default User-Agent header | `lib/version.ml`, applied in `lib/requests.ml` |
33
33
+
34
34
+
### Not Yet Implemented
35
35
+
36
36
+
| # | Feature | Priority |
37
37
+
|---|---------|----------|
38
38
+
| 2 | Certificate/public key pinning | High |
39
39
+
| 6 | HTTP proxy support | High |
40
40
+
| 7 | Request/response middleware system | Medium |
41
41
+
| 8 | Progress callbacks | Medium |
42
42
+
| 9 | Request cancellation | Medium |
43
43
+
| 10 | Concurrent request batching | Low |
44
44
+
| 15 | Charset/encoding detection | Low |
45
45
+
| 16 | Redirect policy customization | Low |
46
46
+
| 17 | TCP keep-alive configuration | Low |
47
47
+
| 18 | URL sanitization for error messages | Low |
48
48
+
| 20 | Mock handler for testing | Medium |
49
49
+
| 22 | Request body rewind for retries | Medium |
50
50
+
| 23 | Event listener/hooks | Low |
51
51
+
| 27 | Unix domain socket support | Low |
52
52
+
53
53
+
---
54
54
+
5
55
## Executive Summary
6
56
7
57
This comprehensive analysis synthesizes recommendations from 45+ HTTP client libraries across 10 programming languages (Python, JavaScript, Go, Rust, Haskell, PHP, Java, Swift, C++, Bash). The most consistent themes are: (1) Security features like certificate pinning, cross-origin header stripping, and HTTPS enforcement for sensitive auth; (2) Middleware/interceptor systems for extensibility; (3) HTTP proxy support; (4) Progress callbacks for uploads/downloads; (5) Request cancellation capabilities; (6) Detailed timing metrics; (7) Retry logic improvements including Retry-After capping and custom predicates. The prioritization emphasizes security fixes that could lead to credential leakage, followed by missing features that block enterprise adoption (proxy support), then enhancements that improve developer experience.
···
11
61
12
62
## Security & Spec Compliance
13
63
14
14
-
### 1. Strip sensitive headers on cross-origin redirects
64
64
+
### 1. ✅ Strip sensitive headers on cross-origin redirects
65
65
+
66
66
+
**Status: IMPLEMENTED** in `lib/requests.ml` and `lib/one.ml`
15
67
16
68
When redirecting to a different origin (host/port/scheme), automatically strip Authorization, Cookie, Proxy-Authorization, and WWW-Authenticate headers to prevent credential leakage to unintended domains. Also strip headers on HTTPS→HTTP protocol downgrade.
17
69
···
21
73
**Cross-Language Consensus:** 8 libraries
22
74
**Source Libraries:** reqwest, got, node-fetch, okhttp, axios, superagent, http-client, needle
23
75
24
24
-
**Affected Files:**
25
25
-
- `lib/requests.ml`
26
26
-
- `lib/one.ml`
27
27
-
- `lib/http_client.ml`
76
76
+
**Implementation:**
77
77
+
- `same_origin` function compares scheme and host
78
78
+
- `strip_sensitive_headers` removes Authorization, Cookie, Proxy-Authorization, WWW-Authenticate
79
79
+
- Applied during redirect handling in `make_with_redirects`
28
80
29
29
-
**Implementation Notes:**
30
30
-
Implement same_origin check comparing scheme, host, and port. Create a list of sensitive headers to strip. Call strip_sensitive_headers before following any redirect where origin changes.
81
81
+
### 2. ❌ Add certificate/public key pinning support
31
82
32
32
-
### 2. Add certificate/public key pinning support
83
83
+
**Status: NOT IMPLEMENTED** - High priority for enterprise security
33
84
34
85
Allow pinning certificates or public keys (SPKI hashes) to protect against compromised certificate authorities. Support per-host pinning configuration with wildcard patterns.
35
86
···
46
97
**Implementation Notes:**
47
98
Add certificate_pins parameter to create() accepting (hostname * sha256_hash list) list. Verify pins during TLS handshake by extracting SPKI from certificate chain. Provide helpful error messages showing expected vs actual hashes.
48
99
49
49
-
### 3. Enforce HTTPS for sensitive authentication methods
100
100
+
### 3. ✅ Enforce HTTPS for sensitive authentication methods
101
101
+
102
102
+
**Status: IMPLEMENTED** in `lib/auth.ml`
50
103
51
104
Reject Basic, Bearer, and Digest authentication over unencrypted HTTP connections by default. Provide explicit opt-out flag (allow_insecure_auth) for testing environments.
52
105
···
57
110
**Cross-Language Consensus:** 2 libraries
58
111
**Source Libraries:** req (Haskell), urllib3
59
112
60
60
-
**Affected Files:**
61
61
-
- `lib/auth.ml`
62
62
-
- `lib/auth.mli`
63
63
-
- `lib/requests.ml`
64
64
-
- `lib/one.ml`
113
113
+
**Implementation:**
114
114
+
- `Auth.apply_secure` validates transport security before applying auth
115
115
+
- `Auth.validate_secure_transport` checks if HTTPS is required
116
116
+
- `Error.Insecure_auth` error type for clear error messages
117
117
+
- `allow_insecure_auth` parameter in request functions for testing
65
118
66
66
-
**Implementation Notes:**
67
67
-
Add runtime check in auth application: if scheme is http (not https) and auth is Basic/Bearer/Digest, raise Security_error unless allow_insecure_auth=true. Log warning when flag is used.
119
119
+
### 4. ✅ Validate Content-Length vs Transfer-Encoding precedence
68
120
69
69
-
### 4. Validate Content-Length vs Transfer-Encoding precedence
121
121
+
**Status: IMPLEMENTED** in `lib/http_read.ml`
70
122
71
123
Per RFC 9112, when both Content-Length and Transfer-Encoding headers are present, ignore Content-Length. This prevents HTTP request smuggling attacks.
72
124
···
76
128
**Cross-Language Consensus:** 2 libraries
77
129
**Source Libraries:** hyper, http-streams
78
130
79
79
-
**Affected Files:**
80
80
-
- `lib/http_read.ml`
131
131
+
**Implementation:**
132
132
+
- Transfer-Encoding: chunked takes precedence over Content-Length
133
133
+
- Warning logged when both headers present (potential attack indicator)
134
134
+
- Per RFC 9112 Section 6.3 compliance
81
135
82
82
-
**Implementation Notes:**
83
83
-
Audit http_read.ml to ensure Transfer-Encoding: chunked takes precedence. Log warning when both headers present (potential attack indicator). Add test case for this scenario.
136
136
+
### 5. ✅ Cap maximum Retry-After delay to prevent DoS
84
137
85
85
-
### 5. Cap maximum Retry-After delay to prevent DoS
138
138
+
**Status: IMPLEMENTED** in `lib/retry.ml`
86
139
87
140
Implement a configurable maximum backoff time (default 120s) that caps server-specified Retry-After values, preventing malicious servers from causing indefinite client blocking.
88
141
···
92
145
**Cross-Language Consensus:** 2 libraries
93
146
**Source Libraries:** req (Go), got
94
147
95
95
-
**Affected Files:**
96
96
-
- `lib/retry.ml`
97
97
-
- `lib/retry.mli`
98
98
-
99
99
-
**Implementation Notes:**
100
100
-
Add backoff_max field to Retry.config. In calculate_backoff, apply: min retry_after backoff_max. Document that very long delays (>backoff_max) result in immediate failure rather than unbounded waiting.
148
148
+
**Implementation:**
149
149
+
- `backoff_max` field in `Retry.config` (default: 120.0 seconds)
150
150
+
- `parse_retry_after` function accepts `?backoff_max` parameter
151
151
+
- Delays exceeding cap are logged as warnings and capped
101
152
102
153
103
154
---
104
155
105
156
## Feature Enhancements
106
157
107
107
-
### 6. Add HTTP proxy support with environment variable detection
158
158
+
### 6. ❌ Add HTTP proxy support with environment variable detection
159
159
+
160
160
+
**Status: NOT IMPLEMENTED** - High priority, see `spec/PROXY_IMPLEMENTATION_PLAN.md`
108
161
109
162
Implement HTTP/HTTPS proxy support including: proxy URL configuration, authentication (Basic), CONNECT method for HTTPS tunneling, and automatic detection from HTTP_PROXY, HTTPS_PROXY, NO_PROXY environment variables.
110
163
···
122
175
- `lib/one.ml`
123
176
124
177
**Implementation Notes:**
125
125
-
Add proxy configuration type with host/port/auth. Implement HTTP CONNECT for HTTPS proxies. Parse environment variables on session creation. Add NO_PROXY pattern matching for bypass. Strip Proxy-Authorization on HTTPS connections.
178
178
+
A comprehensive implementation plan exists in `spec/PROXY_IMPLEMENTATION_PLAN.md` with 8 phases covering proxy configuration, HTTP CONNECT tunneling, environment variable detection, NO_PROXY pattern matching, and proxy authentication.
179
179
+
180
180
+
### 7. ❌ Implement request/response middleware system
126
181
127
127
-
### 7. Implement request/response middleware system
182
182
+
**Status: NOT IMPLEMENTED** - Medium priority
128
183
129
184
Add a composable middleware chain that allows intercepting and transforming requests before sending and responses after receiving. Enable cross-cutting concerns like logging, metrics, request signing, and custom caching.
130
185
···
138
193
**Implementation Notes:**
139
194
Define type middleware = Request.t -> (Request.t -> Response.t) -> Response.t. Store middleware list in session. Execute chain: apply each middleware wrapping the next. Provide default middleware for retry and redirect as replaceable components.
140
195
141
141
-
### 8. Add progress callbacks for uploads and downloads
196
196
+
### 8. ❌ Add progress callbacks for uploads and downloads
197
197
+
198
198
+
**Status: NOT IMPLEMENTED** - Medium priority
142
199
143
200
Implement optional progress callbacks that report bytes transferred, total size (if known), and percent complete. Rate-limit callbacks to prevent performance degradation.
144
201
···
154
211
**Implementation Notes:**
155
212
Add optional progress:(transferred:int64 -> total:int64 option -> unit) parameter. Track bytes during streaming read/write. Throttle callbacks (e.g., max 3/second or every 64KB). Support both upload and download progress.
156
213
157
157
-
### 9. Add request cancellation support
214
214
+
### 9. ❌ Add request cancellation support
215
215
+
216
216
+
**Status: NOT IMPLEMENTED** - Medium priority (Eio provides natural cancellation via switches)
158
217
159
218
Allow programmatic cancellation of in-flight requests. Integrate with Eio's cancellation system while providing explicit cancel() function or cancel token pattern.
160
219
···
169
228
**Implementation Notes:**
170
229
Return (Response.t * cancel_token) from request functions where cancel_token allows aborting the request. Internally use Eio.Cancel or Switch.fail to terminate the fiber. Raise Cancelled error on cancellation.
171
230
172
172
-
### 10. Add concurrent request batching API (map/imap)
231
231
+
### 10. ❌ Add concurrent request batching API (map/imap)
232
232
+
233
233
+
**Status: NOT IMPLEMENTED** - Low priority (Eio.Fiber.all/both provides this)
173
234
174
235
Provide map() for executing multiple requests concurrently and returning results in order, and imap() for yielding responses as they complete. Include configurable concurrency limits and batch timeout.
175
236
···
183
244
**Implementation Notes:**
184
245
Implement using Eio.Fiber.all for map, Eio.Stream for imap. Add max_concurrent parameter using Eio.Semaphore. Provide exception_handler callback for partial failure handling. Add batch_timeout for overall operation limit.
185
246
186
186
-
### 11. Add base URL support with relative path resolution
247
247
+
### 11. ✅ Add base URL support with relative path resolution
248
248
+
249
249
+
**Status: IMPLEMENTED** in `lib/requests.ml`
187
250
188
251
Allow configuring a base URL on the session that is prepended to relative request URLs. Use proper RFC 3986 URI resolution for correct path joining.
189
252
···
193
256
**Cross-Language Consensus:** 5 libraries
194
257
**Source Libraries:** surf, axios, got, sling, req (Go)
195
258
196
196
-
**Affected Files:**
197
197
-
- `lib/requests.ml`
198
198
-
- `lib/requests.mli`
199
199
-
200
200
-
**Implementation Notes:**
201
201
-
Add base_url field to session type. In request methods, check if URL is relative (no scheme); if so, use Uri.resolve to join with base_url. Document trailing slash importance per RFC 3986.
259
259
+
**Implementation:**
260
260
+
- `base_url` parameter in `create` function
261
261
+
- `is_relative_url` checks if URL has scheme
262
262
+
- `resolve_url` uses `Uri.resolve` for RFC 3986 compliant resolution
263
263
+
- Trailing slashes automatically normalized
202
264
203
265
204
266
---
205
267
206
268
## Architectural Improvements
207
269
208
208
-
### 12. Add detailed timing metrics to responses
270
270
+
### 12. ✅ Add detailed timing metrics to responses
271
271
+
272
272
+
**Status: IMPLEMENTED** in new `lib/timing.ml` module
209
273
210
274
Expose granular timing breakdown: DNS lookup time, TCP connection time, TLS handshake time, time to first byte (TTFB), download time, and total time. Include connection reuse tracking.
211
275
212
276
**Cross-Language Consensus:** 7 libraries
213
277
**Source Libraries:** curl-rust, got, resty, req (Go), guzzle, okhttp, isahc
214
278
215
215
-
**Affected Files:**
216
216
-
- `lib/response.ml`
217
217
-
- `lib/response.mli`
218
218
-
- `lib/http_client.ml`
279
279
+
**Implementation:**
280
280
+
- New `Timing` module with detailed metrics type
281
281
+
- Phases: `dns_lookup`, `tcp_connect`, `tls_handshake`, `request_sent`, `time_to_first_byte`, `content_transfer`, `total`
282
282
+
- `timer` type for incremental collection during requests
283
283
+
- `connection_time` and `server_time` computed metrics
284
284
+
- Pretty-printing with `pp` and `to_string`
219
285
220
220
-
**Implementation Notes:**
221
221
-
Define type timings = { dns: float option; connect: float; tls: float option; send: float; first_byte: float; download: float; total: float; connection_reused: bool }. Instrument http_client.ml with timestamps at each phase.
286
286
+
### 13. ✅ Add automatic retry on stale connection detection
222
287
223
223
-
### 13. Add automatic retry on stale connection detection
288
288
+
**Status: IMPLEMENTED** in `ocaml-conpool` library
224
289
225
290
Detect when a pooled connection was closed by the server between requests (NoResponseDataReceived) and automatically retry with a fresh connection. Mark this error as always retryable.
226
291
227
292
**Cross-Language Consensus:** 2 libraries
228
293
**Source Libraries:** http-client (Haskell), okhttp
229
294
230
230
-
**Affected Files:**
231
231
-
- `lib/http_client.ml`
232
232
-
- `lib/error.ml`
233
233
-
- `lib/retry.ml`
234
234
-
- `lib/conpool.ml`
295
295
+
**Implementation:**
296
296
+
The `ocaml-conpool` library provides comprehensive stale connection detection via:
297
297
+
- `is_healthy` function checking age, idle time, and use count
298
298
+
- `max_idle_time` configuration (default: 60s)
299
299
+
- `max_connection_lifetime` configuration (default: 300s)
300
300
+
- `max_connection_uses` configuration (optional)
301
301
+
- Custom `health_check` callback support
302
302
+
- Pool `validate` function runs health checks before connection reuse
235
303
236
236
-
**Implementation Notes:**
237
237
-
Add No_response_data error variant. In http_client, catch empty response on reused connection. Track connection reuse status. In retry logic, always retry No_response_data even for non-idempotent methods since request wasn't sent.
304
304
+
### 14. ✅ Add custom retry condition predicates
238
305
239
239
-
### 14. Add custom retry condition predicates
306
306
+
**Status: IMPLEMENTED** in `lib/retry.ml`
240
307
241
308
Allow users to provide custom retry decision functions beyond status codes and error types. Separate predicates for response-based retry (status/headers) and exception-based retry.
242
309
243
310
**Cross-Language Consensus:** 4 libraries
244
311
**Source Libraries:** resty, got, req (Go), req (Haskell)
245
312
246
246
-
**Affected Files:**
247
247
-
- `lib/retry.ml`
248
248
-
- `lib/retry.mli`
249
249
-
250
250
-
**Implementation Notes:**
251
251
-
Add retry_condition:(Response.t option -> exn option -> retry_status -> bool) to Retry.config. Provide default that checks status_forcelist and Error.is_retryable. Allow custom logic for specific error messages, custom headers, etc.
313
313
+
**Implementation:**
314
314
+
- `response_predicate` type: `Method.t -> int -> Headers.t -> bool`
315
315
+
- `exception_predicate` type: `exn -> bool`
316
316
+
- `retry_response` and `retry_exception` fields in `Retry.config`
317
317
+
- `should_retry_response` checks both built-in rules and custom predicates
318
318
+
- `should_retry_exn` for exception-based retry decisions
319
319
+
- Examples in module documentation
252
320
253
321
254
322
---
255
323
256
324
## Feature Enhancements
257
325
258
258
-
### 15. Add response charset/encoding detection and conversion
326
326
+
### 15. ❌ Add response charset/encoding detection and conversion
327
327
+
328
328
+
**Status: NOT IMPLEMENTED** - Low priority
259
329
260
330
Automatically detect character encoding from Content-Type charset parameter and convert response text to UTF-8. Provide BOM sniffing fallback for encoding detection.
261
331
···
273
343
**Implementation Notes:**
274
344
Parse charset from Content-Type in Mime module. Add Response.text_utf8 that detects and converts encoding. Use uutf library for encoding conversion. Return raw bytes on conversion failure with access to detected charset.
275
345
276
276
-
### 16. Add redirect policy customization
346
346
+
### 16. ❌ Add redirect policy customization
347
347
+
348
348
+
**Status: NOT IMPLEMENTED** - Low priority
277
349
278
350
Allow custom redirect policies with access to attempt status, URL, and previous URLs. Enable controlling method preservation (307/308 vs 301/302), header stripping, and cross-protocol redirect behavior.
279
351
···
295
367
296
368
## Architectural Improvements
297
369
298
298
-
### 17. Add TCP keep-alive configuration
370
370
+
### 17. ❌ Add TCP keep-alive configuration
371
371
+
372
372
+
**Status: NOT IMPLEMENTED** - Low priority
299
373
300
374
Expose TCP keep-alive settings including enable/disable, interval, and retry count. This helps detect broken connections and maintain long-lived connections through firewalls/NAT.
301
375
···
314
388
315
389
## Feature Enhancements
316
390
317
317
-
### 18. Add URL sanitization for error messages
391
391
+
### 18. ❌ Add URL sanitization for error messages
392
392
+
393
393
+
**Status: NOT IMPLEMENTED** - Low priority
318
394
319
395
Automatically sanitize URLs in error messages to prevent leakage of credentials embedded in query parameters (API keys, tokens). Provide without_url() method to strip URLs entirely.
320
396
···
328
404
**Implementation Notes:**
329
405
Extend existing sanitize_url function to handle query parameter credentials. Add Error.without_url to remove URL entirely from error. Apply sanitization automatically in Error.to_string and pp functions.
330
406
331
331
-
### 19. Add Link header parsing for pagination
407
407
+
### 19. ✅ Add Link header parsing for pagination
408
408
+
409
409
+
**Status: IMPLEMENTED** in new `lib/link.ml` module
332
410
333
411
Parse RFC 8288 Link headers to extract relation types (next, prev, first, last) for API pagination. Provide helpers like Response.next_url for common patterns.
334
412
···
338
416
**Cross-Language Consensus:** 3 libraries
339
417
**Source Libraries:** got, superagent, just
340
418
341
341
-
**Affected Files:**
342
342
-
- `lib/headers.ml`
343
343
-
- `lib/headers.mli`
344
344
-
- `lib/response.ml`
345
345
-
346
346
-
**Implementation Notes:**
347
347
-
Add type link_rel = { url: string; rel: string; params: (string * string) list }. Implement parse_link_header following RFC 8288. Add Response.links and Response.next_url/prev_url convenience functions.
419
419
+
**Implementation:**
420
420
+
- New `Link` module with RFC 8288 compliant parsing
421
421
+
- `Link.t` type with `uri`, `rel`, `title`, `media_type`, `hreflang`, `params`
422
422
+
- `parse` function handles comma-separated links with quoted values
423
423
+
- `from_headers` extracts Link header from response headers
424
424
+
- `pagination` returns `(first, prev, next, last)` tuple
425
425
+
- `next_url`, `prev_url`, `has_next` convenience functions
426
426
+
- Pretty-printing with `pp` and `to_string`
348
427
349
428
350
429
---
351
430
352
431
## Architectural Improvements
353
432
354
354
-
### 20. Add mock handler for testing
433
433
+
### 20. ❌ Add mock handler for testing
434
434
+
435
435
+
**Status: NOT IMPLEMENTED** - Medium priority
355
436
356
437
Provide a mock handler that queues pre-defined responses and exceptions for testing HTTP client code without network calls. Support request inspection and assertion.
357
438
···
370
451
371
452
## Feature Enhancements
372
453
373
373
-
### 21. Add Result-based error_for_status variant
454
454
+
### 21. ✅ Add Result-based error_for_status variant
455
455
+
456
456
+
**Status: IMPLEMENTED** in `lib/response.ml`
374
457
375
458
Provide check_status() that returns (Response.t, Error.error) result instead of raising, enabling functional error handling alongside the existing raise_for_status.
376
459
377
460
**Cross-Language Consensus:** 2 libraries
378
461
**Source Libraries:** reqwest, grequests
379
462
380
380
-
**Affected Files:**
381
381
-
- `lib/response.ml`
382
382
-
- `lib/response.mli`
463
463
+
**Implementation:**
464
464
+
- `Response.check_status : t -> (t, Error.t) result`
465
465
+
- Returns `Ok response` for 2xx status codes
466
466
+
- Returns `Error (Http_error {...})` for 4xx/5xx status codes
467
467
+
- Complements existing `raise_for_status` for functional style
383
468
384
384
-
**Implementation Notes:**
385
385
-
Add Response.check_status : t -> (t, Error.error) result that returns Ok response for 2xx, Error with Http_error for 4xx/5xx. Complement existing raise_for_status for different coding styles.
469
469
+
### 22. ❌ Add request body rewind support for retries
386
470
387
387
-
### 22. Add request body rewind support for retries
471
471
+
**Status: NOT IMPLEMENTED** - Medium priority
388
472
389
473
Track whether request bodies are rewindable (seekable) and automatically replay them on retry. Raise clear error when retry requires body replay but body is a consumed stream.
390
474
···
404
488
405
489
## Architectural Improvements
406
490
407
407
-
### 23. Add event listener/hooks for observability
491
491
+
### 23. ❌ Add event listener/hooks for observability
492
492
+
493
493
+
**Status: NOT IMPLEMENTED** - Low priority
408
494
409
495
Provide comprehensive event hooks at each request lifecycle stage: DNS start/end, connection start/end, TLS handshake, request headers/body sent, response headers/body received. Enable metrics and tracing integration.
410
496
···
424
510
425
511
## Feature Enhancements
426
512
427
427
-
### 24. Add XSRF/CSRF token handling
513
513
+
### 24. ✅ Add XSRF/CSRF token handling
514
514
+
515
515
+
**Status: IMPLEMENTED** in `lib/requests.ml`
428
516
429
517
Automatically read CSRF token from a configurable cookie name (default: XSRF-TOKEN) and add it to request headers (default: X-XSRF-TOKEN) for same-origin requests.
430
518
431
519
**Cross-Language Consensus:** 1 libraries
432
520
**Source Libraries:** axios
433
521
434
434
-
**Affected Files:**
435
435
-
- `lib/requests.ml`
436
436
-
- `lib/headers.ml`
437
437
-
438
438
-
**Implementation Notes:**
439
439
-
Add xsrf_cookie_name and xsrf_header_name config options. Before request, if cookie exists, add corresponding header. Only for same-origin requests. Disable by setting cookie name to empty.
522
522
+
**Implementation:**
523
523
+
- `xsrf_cookie_name` parameter (default: `Some "XSRF-TOKEN"`)
524
524
+
- `xsrf_header_name` parameter (default: `"X-XSRF-TOKEN"`)
525
525
+
- `apply_xsrf_token` function reads cookie and injects header
526
526
+
- Set `xsrf_cookie_name` to `None` to disable
527
527
+
- Applied automatically in `make_request_internal`
440
528
441
529
442
530
---
443
531
444
532
## Architectural Improvements
445
533
446
446
-
### 25. Add decompression bomb protection
534
534
+
### 25. ✅ Add decompression bomb protection
535
535
+
536
536
+
**Status: IMPLEMENTED** in `lib/http_client.ml`
447
537
448
538
Enforce configurable compression ratio limits (e.g., 100:1) to detect and abort decompression bomb attacks. Track compressed vs decompressed bytes during streaming.
449
539
450
540
**Cross-Language Consensus:** 1 libraries
451
541
**Source Libraries:** axios
452
542
453
453
-
**Affected Files:**
454
454
-
- `lib/http_read.ml`
455
455
-
- `lib/response_limits.ml`
456
456
-
457
457
-
**Implementation Notes:**
458
458
-
Add max_compression_ratio to Response_limits. During decompression, track input/output bytes. If ratio exceeds limit, raise Decompression_bomb error. Use existing error type from error.ml.
543
543
+
**Implementation:**
544
544
+
- `check_decompression_limits` function validates size and ratio
545
545
+
- `max_decompressed_size` in `Response_limits` (absolute size limit)
546
546
+
- `max_compression_ratio` in `Response_limits` (ratio limit)
547
547
+
- `Error.Decompression_bomb` error type with limit and ratio info
548
548
+
- Applied during gzip/deflate/zlib decompression
459
549
460
550
461
551
---
462
552
463
553
## Feature Enhancements
464
554
465
465
-
### 26. Add HTTP version to response metadata
555
555
+
### 26. ✅ Add HTTP version to response metadata
556
556
+
557
557
+
**Status: IMPLEMENTED** in `lib/http_read.ml`
466
558
467
559
Expose the HTTP version used for the response (HTTP/1.0, HTTP/1.1, HTTP/2) in Response type. Useful for debugging protocol negotiation and monitoring HTTP/2 adoption.
468
560
469
561
**Cross-Language Consensus:** 3 libraries
470
562
**Source Libraries:** reqwest, surf, curl-rust
471
563
472
472
-
**Affected Files:**
473
473
-
- `lib/response.ml`
474
474
-
- `lib/response.mli`
475
475
-
- `lib/http_read.ml`
564
564
+
**Implementation:**
565
565
+
- `http_version` type: `HTTP_1_0 | HTTP_1_1`
566
566
+
- `http_version_to_string` for display
567
567
+
- `status_line` returns `(http_version, status_code)` tuple
568
568
+
- `response` function returns `(http_version, status, headers, body)`
569
569
+
- `stream_response` includes `http_version` field
476
570
477
477
-
**Implementation Notes:**
478
478
-
Add http_version field to Response.t. Parse version from status line. Add Response.version accessor. Prepare for future HTTP/2 support.
571
571
+
### 27. ❌ Add Unix domain socket support
479
572
480
480
-
### 27. Add Unix domain socket support
573
573
+
**Status: NOT IMPLEMENTED** - Low priority
481
574
482
575
Enable connecting to local services via Unix domain sockets (e.g., Docker daemon at /var/run/docker.sock) without TCP overhead.
483
576
···
497
590
498
591
## Security & Spec Compliance
499
592
500
500
-
### 28. Restrict URL protocols to HTTP/HTTPS only
593
593
+
### 28. ✅ Restrict URL protocols to HTTP/HTTPS only
594
594
+
595
595
+
**Status: IMPLEMENTED** in `lib/requests.ml` and `lib/one.ml`
501
596
502
597
Explicitly whitelist only http:// and https:// protocols to prevent protocol smuggling attacks via file://, ftp://, gopher://, etc. Reject invalid protocols early in URL parsing.
503
598
504
599
**Cross-Language Consensus:** 1 libraries
505
600
**Source Libraries:** buzz
506
601
507
507
-
**Affected Files:**
508
508
-
- `lib/http_client.ml`
509
509
-
- `lib/one.ml`
510
510
-
- `lib/requests.ml`
511
511
-
512
512
-
**Implementation Notes:**
513
513
-
Add URL scheme validation early in request processing. Reject any scheme other than http/https with Invalid_url error. Apply validation also to redirect URLs.
602
602
+
**Implementation:**
603
603
+
- `allowed_redirect_schemes = ["http"; "https"]`
604
604
+
- `validate_redirect_url` checks scheme on redirects
605
605
+
- `Error.Invalid_redirect` raised for disallowed schemes
606
606
+
- Applied during redirect following to prevent SSRF
514
607
515
608
516
609
---
517
610
518
611
## Feature Enhancements
519
612
520
520
-
### 29. Add path parameter templating
613
613
+
### 29. ✅ Add path parameter templating
614
614
+
615
615
+
**Status: IMPLEMENTED** in `lib/requests.ml`
521
616
522
617
Support URL templates like /users/{id}/posts/{post_id} with placeholder substitution from a parameter map. Automatically URL-encode substituted values.
523
618
···
527
622
**Cross-Language Consensus:** 3 libraries
528
623
**Source Libraries:** sling, resty, req (Go)
529
624
530
530
-
**Affected Files:**
531
531
-
- `lib/requests.ml`
532
532
-
- `lib/one.ml`
533
533
-
534
534
-
**Implementation Notes:**
535
535
-
Add path_params:(string * string) list parameter. Before URL resolution, scan for {key} patterns and substitute with URL-encoded values from map. Raise error if template has unmatched placeholders.
625
625
+
**Implementation:**
626
626
+
- `substitute_path_params` function for RFC 6570 templating
627
627
+
- `path_params:(string * string) list` parameter on all request methods
628
628
+
- Automatic URL encoding via `Uri.pct_encode`
629
629
+
- Applied before base URL resolution in request pipeline
630
630
+
- Example: `get ~path_params:[("id", "123")] "/users/{id}"`
536
631
537
632
538
633
---
539
634
540
635
## Architectural Improvements
541
636
542
542
-
### 30. Add default User-Agent header
637
637
+
### 30. ✅ Add default User-Agent header
638
638
+
639
639
+
**Status: IMPLEMENTED** in `lib/version.ml`
543
640
544
641
Set a default User-Agent identifying the library (ocaml-requests/VERSION) unless user provides one. Helps server-side debugging and follows HTTP best practices.
545
642
···
549
646
**Cross-Language Consensus:** 5 libraries
550
647
**Source Libraries:** needle, resty, requests (Go), okhttp, guzzle
551
648
552
552
-
**Affected Files:**
553
553
-
- `lib/requests.ml`
554
554
-
- `lib/one.ml`
555
555
-
556
556
-
**Implementation Notes:**
557
557
-
Add default User-Agent in default_headers if not set. Include library version and optionally OCaml version/platform. Allow override via explicit header or user_agent parameter.
649
649
+
**Implementation:**
650
650
+
- New `Version` module with library version info
651
651
+
- `Version.user_agent` returns `"ocaml-requests/0.1.0 (OCaml X.XX.X)"`
652
652
+
- Applied automatically if not already set in `make_request_internal`
653
653
+
- Can be overridden via explicit User-Agent header
558
654