A batteries included HTTP/1.1 client in OCaml
at claude-test 236 lines 18 kB view raw
1{ 2 "recommendations": [ 3 { 4 "source_repo": "third_party/php/http-client", 5 "source_language": "PHP", 6 "criticality": "medium", 7 "change_type": "security", 8 "title": "Implement request cancellation mechanism", 9 "description": "The PHP library provides a Cancellation interface that allows users to cancel in-flight requests gracefully. This prevents resource leaks and enables timeout-based request cancellation at the application level. OCaml library should provide a cancellation token mechanism compatible with Eio's cancellation contexts.", 10 "affected_files": [ 11 "lib/requests.mli", 12 "lib/one.mli", 13 "lib/http_client.ml" 14 ], 15 "rationale": "Cancellation is critical for preventing resource exhaustion in long-running requests. The PHP library demonstrates how cancellation can be integrated at the interceptor level. In the OCaml library, this could leverage Eio's fiber cancellation, but needs explicit API surface for user-controlled cancellation beyond just timeout." 16 }, 17 { 18 "source_repo": "third_party/php/http-client", 19 "source_language": "PHP", 20 "criticality": "medium", 21 "change_type": "feature", 22 "title": "Add event listener/hook system for observability", 23 "description": "The PHP library provides an EventListener interface that allows users to monitor HTTP requests and responses for logging, metrics, and debugging. This enables features like HTTP Archive (HAR) generation for performance analysis. OCaml library should add a similar callback/hook system for request lifecycle events (start, headers received, body chunk, complete, error).", 24 "affected_files": [ 25 "lib/requests.mli", 26 "lib/http_client.ml" 27 ], 28 "rationale": "Event listeners provide crucial observability without requiring users to wrap every request. The PHP library's LogHttpArchive listener demonstrates practical value for debugging and performance analysis. This would complement the existing Logs-based logging by providing structured programmatic access to request/response data." 29 }, 30 { 31 "source_repo": "third_party/php/http-client", 32 "source_language": "PHP", 33 "criticality": "low", 34 "change_type": "enhancement", 35 "title": "Add idle connection limiting to connection pool", 36 "description": "The PHP ConnectionLimitingPool limits idle connections to 64 (hardcoded) to prevent unbounded memory growth. The OCaml library's Conpool should add configurable idle connection limits with automatic closure of least-recently-used idle connections when the limit is exceeded.", 37 "affected_files": [ 38 "lib/requests.mli" 39 ], 40 "rationale": "While the OCaml library has connection_idle_timeout, it doesn't limit the total number of idle connections across all hosts. The PHP library shows that keeping too many idle connections can waste memory. A configurable max_idle_connections parameter (with sensible default like 100) would prevent resource exhaustion in long-running applications making requests to many different hosts." 41 }, 42 { 43 "source_repo": "third_party/php/http-client", 44 "source_language": "PHP", 45 "criticality": "high", 46 "change_type": "security", 47 "title": "Enforce ForbidUriUserInfo by default to prevent credential leakage", 48 "description": "The PHP library includes a ForbidUriUserInfo interceptor that rejects URIs containing username:password (deprecated per RFC 7230 §2.7.1). This prevents accidental credential exposure in logs and headers via URI.toString(). The OCaml library should validate URIs and reject user info by default, with an explicit opt-out flag if needed.", 49 "affected_files": [ 50 "lib/one.ml", 51 "lib/http_client.ml", 52 "lib/error.mli" 53 ], 54 "rationale": "Userinfo in URIs (http://user:pass@example.com) is deprecated and dangerous because URIs are often logged or sent in headers where credentials can leak. The PHP library treats this as a security issue requiring explicit opt-in. OCaml library should raise Error.Invalid_url when userinfo is detected, forcing users to use proper Authorization headers instead." 55 }, 56 { 57 "source_repo": "third_party/php/http-client", 58 "source_language": "PHP", 59 "criticality": "medium", 60 "change_type": "feature", 61 "title": "Add request/response interceptor framework for extensibility", 62 "description": "The PHP library uses an interceptor pattern with ApplicationInterceptor and NetworkInterceptor interfaces that allow composable request/response modification. This enables features like automatic decompression, retries, redirects, and header manipulation to be implemented as modular interceptors. OCaml library should add a similar middleware/interceptor system.", 63 "affected_files": [ 64 "lib/requests.mli", 65 "lib/http_client.ml" 66 ], 67 "rationale": "The interceptor pattern provides cleaner separation of concerns than monolithic request handling. PHP library demonstrates how ApplicationInterceptors (operate on logical requests) differ from NetworkInterceptors (operate on actual connections). This would allow users to add custom logic (e.g., request signing, response caching, metrics) without modifying core library code. Could be implemented using function composition or a dedicated Interceptor module type." 68 }, 69 { 70 "source_repo": "third_party/php/http-client", 71 "source_language": "PHP", 72 "criticality": "low", 73 "change_type": "enhancement", 74 "title": "Add support for HTTP/2 server push interception", 75 "description": "The PHP library's Request object includes an interceptPush callback that allows handling HTTP/2 server push promises. While HTTP/2 server push is being deprecated in browsers, it may still be useful for API clients. OCaml library should consider adding server push callback support if/when HTTP/2 is implemented.", 76 "affected_files": [ 77 "lib/requests.mli" 78 ], 79 "rationale": "Server push allows servers to proactively send resources the client will need. The PHP library demonstrates how this can be handled via callbacks. While not urgent (HTTP/2 not yet implemented in OCaml library, and push is deprecated), documenting this for future consideration ensures the API can evolve to support it if needed." 80 }, 81 { 82 "source_repo": "third_party/php/http-client", 83 "source_language": "PHP", 84 "criticality": "medium", 85 "change_type": "enhancement", 86 "title": "Improve retry logic to distinguish idempotent vs unprocessed requests", 87 "description": "The PHP RetryRequests interceptor checks both request.isIdempotent() and request.isUnprocessed(). The isUnprocessed flag indicates the connection determined the request wasn't processed (e.g., connection closed before sending), making it safe to retry even for non-idempotent requests. OCaml library should track whether requests were actually sent and allow retrying unsent requests regardless of method.", 88 "affected_files": [ 89 "lib/retry.ml", 90 "lib/retry.mli", 91 "lib/http_client.ml" 92 ], 93 "rationale": "Currently, OCaml retry logic only checks HTTP method and status codes. The PHP library shows that connection-level failures before the request is fully sent are always safe to retry, even for POST. This prevents false failures when connections are dropped before the request reaches the server. Add a 'request_sent' tracking flag and allow retrying when request_sent=false regardless of method." 94 }, 95 { 96 "source_repo": "third_party/php/http-client", 97 "source_language": "PHP", 98 "criticality": "medium", 99 "change_type": "enhancement", 100 "title": "Add HTTP/2 connection multiplexing with priority-based stream waiting", 101 "description": "The PHP library's ConnectionLimitingPool implements sophisticated HTTP/2 multiplexing: it waits for the first HTTPS connection to complete to check if HTTP/2 is available before spawning additional connections. This maximizes stream reuse on a single HTTP/2 connection. The OCaml library should implement similar logic when HTTP/2 support is added.", 102 "affected_files": [ 103 "lib/requests.mli" 104 ], 105 "rationale": "The PHP code shows 'waitForPriorConnection' logic that prevents creating unnecessary TCP connections when HTTP/2 multiplexing is available. Without this, connection pools may create multiple TCP connections to the same host when one HTTP/2 connection could handle all streams. This is a performance optimization to implement when adding HTTP/2 support." 106 }, 107 { 108 "source_repo": "third_party/php/http-client", 109 "source_language": "PHP", 110 "criticality": "medium", 111 "change_type": "enhancement", 112 "title": "Add configurable decompression bomb protection", 113 "description": "The PHP library's DecompressResponse interceptor checks request.getBodySizeLimit() and wraps decompressed streams in SizeLimitingReadableStream to prevent decompression bombs. The OCaml library has Body_too_large error but should enforce size limits on decompressed responses, not just raw responses.", 114 "affected_files": [ 115 "lib/response_limits.mli", 116 "lib/http_client.ml", 117 "lib/one.ml" 118 ], 119 "rationale": "A small compressed response can decompress to gigabytes, causing memory exhaustion. The OCaml library has Response_limits.max_body_size but it's unclear if this is enforced post-decompression. Should add explicit decompression bomb detection by tracking decompressed bytes separately and raising Error.Decompression_bomb when ratio exceeds threshold (e.g., 1000x expansion)." 120 }, 121 { 122 "source_repo": "third_party/php/http-client", 123 "source_language": "PHP", 124 "criticality": "low", 125 "change_type": "enhancement", 126 "title": "Improve response body API to prevent double-consumption", 127 "description": "The PHP library's Response.getBody() returns a new stream each time it's called, making it harder to accidentally double-consume. The OCaml library's Response.body returns the same flow which can only be read once. Consider adding explicit ownership tracking or response.clone() functionality.", 128 "affected_files": [ 129 "lib/response.mli", 130 "lib/response.ml" 131 ], 132 "rationale": "Double-consuming a response body is a common bug. The PHP library's approach of returning new streams (if bufferable) prevents this. OCaml library could add a 'consumed' flag and raise an error on second access, or provide Response.clone for cases where multiple reads are needed. The current documentation warns about this but doesn't enforce it programmatically." 133 }, 134 { 135 "source_repo": "third_party/php/http-client", 136 "source_language": "PHP", 137 "criticality": "low", 138 "change_type": "feature", 139 "title": "Add support for conditional Accept-Encoding header", 140 "description": "The PHP DecompressResponse interceptor only adds Accept-Encoding if the user hasn't manually set it, respecting user intent. The OCaml library's auto_decompress flag is all-or-nothing. Should allow users to set custom Accept-Encoding while still getting automatic decompression.", 141 "affected_files": [ 142 "lib/http_client.ml", 143 "lib/one.ml" 144 ], 145 "rationale": "Users may want to control which encodings are acceptable (e.g., only gzip, not deflate) while still benefiting from automatic decompression. The PHP approach of checking if the header is already set before adding it is more flexible. OCaml library should check Headers.mem \"accept-encoding\" and skip adding it if user provided their own." 146 }, 147 { 148 "source_repo": "third_party/php/http-client", 149 "source_language": "PHP", 150 "criticality": "high", 151 "change_type": "bug", 152 "title": "Fix potential retry exception handling bug", 153 "description": "The PHP RetryRequests interceptor may reference undefined $exception variable if retry loop exhausts without catching an exception on the final attempt. The OCaml retry logic should ensure exceptions are properly propagated on final retry failure.", 154 "affected_files": [ 155 "lib/retry.ml" 156 ], 157 "rationale": "The PHP code shows 'throw $exception' after the do-while loop, but $exception may be undefined if no exception was caught in the last iteration. This appears to be a bug in the PHP library. OCaml retry.ml should be reviewed to ensure it doesn't have similar issues - the with_retry function should re-raise the exception from the final attempt, not rely on a potentially unbound variable." 158 }, 159 { 160 "source_repo": "third_party/php/http-client", 161 "source_language": "PHP", 162 "criticality": "medium", 163 "change_type": "enhancement", 164 "title": "Add protocol version negotiation and filtering", 165 "description": "The PHP library's Connection interface includes getProtocolVersions() and Request includes getProtocolVersions(), allowing filtering connections by supported protocols (HTTP/1.1 vs HTTP/2). The OCaml library should add protocol version tracking and negotiation when HTTP/2 support is added.", 166 "affected_files": [ 167 "lib/requests.mli", 168 "lib/http_client.ml" 169 ], 170 "rationale": "The PHP ConnectionLimitingPool checks array_intersect($request->getProtocolVersions(), $connection->getProtocolVersions()) before using a connection. This ensures requests only use connections that support their required protocol version. Useful for gradual HTTP/2 rollout where some requests may need to force HTTP/1.1 for compatibility." 171 }, 172 { 173 "source_repo": "third_party/php/http-client", 174 "source_language": "PHP", 175 "criticality": "low", 176 "change_type": "enhancement", 177 "title": "Add connection pool statistics and metrics", 178 "description": "The PHP ConnectionLimitingPool tracks getTotalConnectionAttempts(), getTotalStreamRequests(), and getOpenConnectionCount() for monitoring and debugging. The OCaml Conpool should expose similar metrics for observability.", 179 "affected_files": [ 180 "lib/requests.mli" 181 ], 182 "rationale": "Connection pool metrics help diagnose performance issues and validate pool configuration. The PHP library exposes these metrics publicly. OCaml Conpool already has internal Stats tracking but it may not be exposed in the public API. Should add Requests.pool_stats() or similar to return {total_connections; active_connections; idle_connections; total_requests}." 183 }, 184 { 185 "source_repo": "third_party/php/http-client", 186 "source_language": "PHP", 187 "criticality": "low", 188 "change_type": "enhancement", 189 "title": "Improve builder pattern ergonomics with method chaining", 190 "description": "The PHP HttpClientBuilder uses fluent method chaining where each configuration method returns a new builder instance (clone $this). The OCaml library's Requests.create takes ~20 optional parameters. Consider adding a builder pattern or configuration record type for better ergonomics.", 191 "affected_files": [ 192 "lib/requests.mli" 193 ], 194 "rationale": "The PHP builder pattern makes configuration more discoverable and readable compared to long parameter lists. While OCaml doesn't support method chaining syntax, a similar pattern could use: let cfg = Config.default |> Config.with_timeout 30.0 |> Config.with_retry retry_cfg in Requests.create ~config:cfg env. This would reduce the function signature from 20 optional parameters to 2-3 core parameters plus a config." 195 }, 196 { 197 "source_repo": "third_party/php/http-client", 198 "source_language": "PHP", 199 "criticality": "medium", 200 "change_type": "enhancement", 201 "title": "Add automatic User-Agent header with opt-out", 202 "description": "The PHP library automatically sets User-Agent to 'amphp/http-client/5.x' unless explicitly disabled or overridden. The OCaml library should add a default User-Agent header (e.g., 'ocaml-requests/0.1.0') that can be disabled or customized.", 203 "affected_files": [ 204 "lib/requests.mli", 205 "lib/one.mli", 206 "lib/http_client.ml" 207 ], 208 "rationale": "User-Agent headers help server administrators identify clients and are considered polite HTTP behavior. The PHP library uses SetRequestHeaderIfUnset to add it by default without forcing it. OCaml library should add user_agent optional parameter to Requests.create (default: Some \"ocaml-requests/$VERSION\") and add the header in http_client if not already present." 209 }, 210 { 211 "source_repo": "third_party/php/http-client", 212 "source_language": "PHP", 213 "criticality": "low", 214 "change_type": "enhancement", 215 "title": "Add automatic Accept header with opt-out", 216 "description": "The PHP library automatically sets Accept: */* unless explicitly disabled or overridden. The OCaml library could add similar default Accept header behavior for better HTTP compliance.", 217 "affected_files": [ 218 "lib/http_client.ml", 219 "lib/one.ml" 220 ], 221 "rationale": "While not strictly required, setting Accept: */* indicates the client can handle any content type. The PHP library adds this via SetRequestHeaderIfUnset interceptor. OCaml library could add this when user hasn't set Accept header, though this is lower priority than User-Agent since Accept defaults are often implied." 222 }, 223 { 224 "source_repo": "third_party/php/http-client", 225 "source_language": "PHP", 226 "criticality": "medium", 227 "change_type": "feature", 228 "title": "Add request cloning support for retries", 229 "description": "The PHP library clones requests before each retry attempt (clonedRequest = clone $request) to ensure retries don't mutate the original request. The OCaml library should ensure retry logic doesn't modify the original request state.", 230 "affected_files": [ 231 "lib/retry.ml" 232 ], 233 "rationale": "The PHP library shows that request state may be modified during attempts (e.g., tracking if request was processed). Cloning ensures each retry starts fresh. OCaml library should verify that with_retry doesn't accidentally share mutable state across retry attempts. Since OCaml tends toward immutability, this may already be correct, but should be verified in retry.ml implementation." 234 } 235 ] 236}