http 6.0.0-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +267 -0
  3. data/CONTRIBUTING.md +26 -0
  4. data/LICENSE.txt +20 -0
  5. data/README.md +263 -0
  6. data/SECURITY.md +17 -0
  7. data/UPGRADING.md +491 -0
  8. data/http.gemspec +48 -0
  9. data/lib/http/base64.rb +22 -0
  10. data/lib/http/chainable/helpers.rb +62 -0
  11. data/lib/http/chainable/verbs.rb +136 -0
  12. data/lib/http/chainable.rb +377 -0
  13. data/lib/http/client.rb +230 -0
  14. data/lib/http/connection/internals.rb +141 -0
  15. data/lib/http/connection.rb +265 -0
  16. data/lib/http/content_type.rb +89 -0
  17. data/lib/http/errors.rb +67 -0
  18. data/lib/http/feature.rb +86 -0
  19. data/lib/http/features/auto_deflate.rb +230 -0
  20. data/lib/http/features/auto_inflate.rb +64 -0
  21. data/lib/http/features/caching/entry.rb +178 -0
  22. data/lib/http/features/caching/in_memory_store.rb +63 -0
  23. data/lib/http/features/caching.rb +216 -0
  24. data/lib/http/features/digest_auth.rb +234 -0
  25. data/lib/http/features/instrumentation.rb +149 -0
  26. data/lib/http/features/logging.rb +231 -0
  27. data/lib/http/features/normalize_uri.rb +34 -0
  28. data/lib/http/features/raise_error.rb +37 -0
  29. data/lib/http/form_data/composite_io.rb +106 -0
  30. data/lib/http/form_data/file.rb +95 -0
  31. data/lib/http/form_data/multipart/param.rb +62 -0
  32. data/lib/http/form_data/multipart.rb +106 -0
  33. data/lib/http/form_data/part.rb +52 -0
  34. data/lib/http/form_data/readable.rb +58 -0
  35. data/lib/http/form_data/urlencoded.rb +175 -0
  36. data/lib/http/form_data/version.rb +8 -0
  37. data/lib/http/form_data.rb +102 -0
  38. data/lib/http/headers/known.rb +90 -0
  39. data/lib/http/headers/normalizer.rb +50 -0
  40. data/lib/http/headers.rb +343 -0
  41. data/lib/http/mime_type/adapter.rb +43 -0
  42. data/lib/http/mime_type/json.rb +41 -0
  43. data/lib/http/mime_type.rb +96 -0
  44. data/lib/http/options/definitions.rb +189 -0
  45. data/lib/http/options.rb +241 -0
  46. data/lib/http/redirector.rb +157 -0
  47. data/lib/http/request/body.rb +181 -0
  48. data/lib/http/request/builder.rb +184 -0
  49. data/lib/http/request/proxy.rb +83 -0
  50. data/lib/http/request/writer.rb +186 -0
  51. data/lib/http/request.rb +375 -0
  52. data/lib/http/response/body.rb +172 -0
  53. data/lib/http/response/inflater.rb +60 -0
  54. data/lib/http/response/parser.rb +223 -0
  55. data/lib/http/response/status/reasons.rb +79 -0
  56. data/lib/http/response/status.rb +263 -0
  57. data/lib/http/response.rb +350 -0
  58. data/lib/http/retriable/delay_calculator.rb +91 -0
  59. data/lib/http/retriable/errors.rb +35 -0
  60. data/lib/http/retriable/performer.rb +197 -0
  61. data/lib/http/session.rb +280 -0
  62. data/lib/http/timeout/global.rb +229 -0
  63. data/lib/http/timeout/null.rb +225 -0
  64. data/lib/http/timeout/per_operation.rb +197 -0
  65. data/lib/http/uri/normalizer.rb +82 -0
  66. data/lib/http/uri/parsing.rb +182 -0
  67. data/lib/http/uri.rb +376 -0
  68. data/lib/http/version.rb +6 -0
  69. data/lib/http.rb +36 -0
  70. data/sig/deps.rbs +122 -0
  71. data/sig/http.rbs +1619 -0
  72. data/test/http/base64_test.rb +28 -0
  73. data/test/http/client_test.rb +739 -0
  74. data/test/http/connection_test.rb +1533 -0
  75. data/test/http/content_type_test.rb +190 -0
  76. data/test/http/errors_test.rb +28 -0
  77. data/test/http/feature_test.rb +49 -0
  78. data/test/http/features/auto_deflate_test.rb +317 -0
  79. data/test/http/features/auto_inflate_test.rb +213 -0
  80. data/test/http/features/caching_test.rb +942 -0
  81. data/test/http/features/digest_auth_test.rb +996 -0
  82. data/test/http/features/instrumentation_test.rb +246 -0
  83. data/test/http/features/logging_test.rb +654 -0
  84. data/test/http/features/normalize_uri_test.rb +41 -0
  85. data/test/http/features/raise_error_test.rb +77 -0
  86. data/test/http/form_data/composite_io_test.rb +215 -0
  87. data/test/http/form_data/file_test.rb +255 -0
  88. data/test/http/form_data/fixtures/the-http-gem.info +1 -0
  89. data/test/http/form_data/multipart_test.rb +303 -0
  90. data/test/http/form_data/part_test.rb +90 -0
  91. data/test/http/form_data/urlencoded_test.rb +164 -0
  92. data/test/http/form_data_test.rb +232 -0
  93. data/test/http/headers/normalizer_test.rb +93 -0
  94. data/test/http/headers_test.rb +888 -0
  95. data/test/http/mime_type/json_test.rb +39 -0
  96. data/test/http/mime_type_test.rb +150 -0
  97. data/test/http/options/base_uri_test.rb +148 -0
  98. data/test/http/options/body_test.rb +21 -0
  99. data/test/http/options/features_test.rb +38 -0
  100. data/test/http/options/form_test.rb +21 -0
  101. data/test/http/options/headers_test.rb +32 -0
  102. data/test/http/options/json_test.rb +21 -0
  103. data/test/http/options/merge_test.rb +78 -0
  104. data/test/http/options/new_test.rb +37 -0
  105. data/test/http/options/proxy_test.rb +32 -0
  106. data/test/http/options_test.rb +575 -0
  107. data/test/http/redirector_test.rb +639 -0
  108. data/test/http/request/body_test.rb +318 -0
  109. data/test/http/request/builder_test.rb +623 -0
  110. data/test/http/request/writer_test.rb +391 -0
  111. data/test/http/request_test.rb +1733 -0
  112. data/test/http/response/body_test.rb +292 -0
  113. data/test/http/response/parser_test.rb +105 -0
  114. data/test/http/response/status_test.rb +322 -0
  115. data/test/http/response_test.rb +502 -0
  116. data/test/http/retriable/delay_calculator_test.rb +194 -0
  117. data/test/http/retriable/errors_test.rb +71 -0
  118. data/test/http/retriable/performer_test.rb +551 -0
  119. data/test/http/session_test.rb +424 -0
  120. data/test/http/timeout/global_test.rb +239 -0
  121. data/test/http/timeout/null_test.rb +218 -0
  122. data/test/http/timeout/per_operation_test.rb +220 -0
  123. data/test/http/uri/normalizer_test.rb +89 -0
  124. data/test/http/uri_test.rb +1140 -0
  125. data/test/http/version_test.rb +15 -0
  126. data/test/http_test.rb +818 -0
  127. data/test/regression_tests.rb +27 -0
  128. data/test/support/capture_warning.rb +10 -0
  129. data/test/support/dummy_server/encoding_routes.rb +47 -0
  130. data/test/support/dummy_server/routes.rb +201 -0
  131. data/test/support/dummy_server/servlet.rb +81 -0
  132. data/test/support/dummy_server.rb +200 -0
  133. data/test/support/fakeio.rb +21 -0
  134. data/test/support/http_handling_shared/connection_reuse_tests.rb +97 -0
  135. data/test/support/http_handling_shared/timeout_tests.rb +134 -0
  136. data/test/support/http_handling_shared.rb +11 -0
  137. data/test/support/proxy_server.rb +207 -0
  138. data/test/support/servers/runner.rb +67 -0
  139. data/test/support/simplecov.rb +28 -0
  140. data/test/support/ssl_helper.rb +108 -0
  141. data/test/test_helper.rb +38 -0
  142. metadata +218 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d825505ad2427825690e2c8ca8b6e2eb448f4d143e1429118b40c99102ed9ac3
4
+ data.tar.gz: d2265d0dfeef4b92002da361703283185db942390d9fdeeb5cfff9b33ba6161a
5
+ SHA512:
6
+ metadata.gz: 8474712ee9528afb559ac0d7e0b4e3d0ba5768eb4c12bab5bc6a6238e7ed4c3e78fd49921e4d1243f44b12737646150496a07fde71f9b0643344aada4d5b7538
7
+ data.tar.gz: 669acc5dd73ded2112a61c049424aa375feff6a80d4d3b42091304c5de10bd30d2e0fab5c8cf5397c10280774a6678b4788a8f4cbbdbadacf37dbc950990483f
data/CHANGELOG.md ADDED
@@ -0,0 +1,267 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [6.0.0] - 2026-03-16
9
+
10
+ ### Changed
11
+
12
+ - Merged `http-form_data` gem into the main `http` gem. The `HTTP::FormData`
13
+ module (including `Part`, `File`, `Multipart`, `Urlencoded`, and `CompositeIO`)
14
+ is now shipped directly with `http` instead of being a separate dependency.
15
+ The public API is unchanged.
16
+
17
+ ### Fixed
18
+
19
+ - `Inflater` no longer raises `Zlib::BufError` when a response declares
20
+ `Content-Encoding: gzip` (or deflate) but the body is not valid compressed
21
+ data. This commonly occurred when following redirects with `auto_inflate`
22
+ enabled, because the redirect response had a `Content-Encoding` header but a
23
+ non-compressed body. ([#621])
24
+ - Persistent connections now auto-flush unread response bodies before sending
25
+ the next request, instead of raising `StateError`. Bodies up to 1 MiB are
26
+ drained transparently; larger bodies cause the connection to close and reopen.
27
+ This prevents the silent body clobbering described in [#371], where an unread
28
+ response body would return `""` after a subsequent request. ([#371])
29
+ - `Response#content_length` now handles duplicate `Content-Length` headers per
30
+ RFC 7230 Section 3.3.2. When all values are identical, they are collapsed into
31
+ a single valid value. When values conflict, `nil` is returned instead of
32
+ raising `TypeError`. ([#566])
33
+ - HTTP 1xx informational responses (e.g. `100 Continue`) are now transparently
34
+ skipped, returning the final response. This was a regression introduced when
35
+ the parser was migrated from http-parser to llhttp. ([#667])
36
+ - Redirect loop detection now considers cookies, so a redirect back to the
37
+ same URL with different cookies is no longer falsely detected as an endless
38
+ loop. Fixes cookie-dependent redirect flows where a server sets a cookie on
39
+ one hop and expects it on the next. ([#544])
40
+ - Per-operation timeouts (`HTTP.timeout(read: n, write: n, connect: n)`) no
41
+ longer default unspecified values to 0.25 seconds. Omitted timeouts now mean
42
+ no timeout for that operation, matching the behavior when no timeout is
43
+ configured at all. ([#579])
44
+ - Per-operation timeout handler now correctly handles `:wait_writable` from
45
+ `read_nonblock` and `:wait_readable` from `write_nonblock` on SSL sockets
46
+ during TLS renegotiation. Previously these symbols were returned as data
47
+ instead of being waited on. ([#358])
48
+ - Persistent sessions now follow cross-origin redirects instead of raising
49
+ `StateError`. `HTTP.persistent` returns an `HTTP::Session` that pools one
50
+ `HTTP::Client` per origin, so redirects to a different domain transparently
51
+ open (and reuse) a separate persistent connection. Cookie management is
52
+ preserved across all hops. ([#557])
53
+ - Chaining configuration methods (`.headers`, `.auth`, `.cookies`, etc.) on a
54
+ persistent session no longer breaks connection reuse. Child sessions created
55
+ by chaining now share the parent's connection pool, so
56
+ `HTTP.persistent(host).headers(...).get(path)` reuses the same underlying
57
+ TCP connection across calls. ([#372])
58
+
59
+ ### Changed
60
+
61
+ - **BREAKING** `HTTP.persistent` now returns an `HTTP::Session` instead of an
62
+ `HTTP::Client`. The session pools persistent clients per origin and exposes
63
+ the same chainable API (`get`, `post`, `headers`, `follow`, etc.) plus a
64
+ `close` method that shuts down all pooled connections. Code that called
65
+ `HTTP::Client`-only methods on the return value will need updating. ([#557])
66
+ - **BREAKING** Convert options hash parameters to explicit keyword arguments
67
+ across the public API. Methods like `HTTP.get(url, body: "data")` continue to
68
+ work, but passing an explicit hash (e.g., `HTTP.get(url, {body: "data"})`) is
69
+ no longer supported, and unrecognized keyword arguments now raise
70
+ `ArgumentError`. Affected methods: all HTTP verb methods (`get`, `post`,
71
+ etc.), `request`, `follow`, `retriable`, `URI.new`, `Request.new`,
72
+ `Response.new`, `Redirector.new`, `Retriable::Performer.new`,
73
+ `Retriable::DelayCalculator.new`, and `Timeout::Null.new` (and subclasses).
74
+ `HTTP::URI.new` also no longer accepts `Addressable::URI` objects.
75
+ - **BREAKING** `addressable` is no longer a runtime dependency. It is now
76
+ lazy-loaded only when parsing non-ASCII (IRI) URIs or normalizing
77
+ internationalized hostnames. Install the `addressable` gem if you need
78
+ non-ASCII URI support. ASCII-only URIs use Ruby's stdlib `URI` parser
79
+ exclusively.
80
+ - **BREAKING** Extract request building into `HTTP::Request::Builder`. The
81
+ `build_request` method has been removed from `Client`, `Session`, and the
82
+ top-level `HTTP` module. Use `HTTP::Request::Builder.new(options).build(verb, uri)`
83
+ to construct requests without executing them.
84
+
85
+ ### Added
86
+
87
+ - Block form for verb methods and `request` that auto-closes the connection
88
+ after the block returns. `HTTP.get(url) { |response| response.status }` yields
89
+ the response, closes the underlying connection, and returns the block's value.
90
+ Works with all verb methods and chained options. ([#270])
91
+ - HTTP caching feature (`HTTP.use(:caching)`) that stores and reuses responses
92
+ according to RFC 7234. Supports `Cache-Control` (`max-age`, `no-cache`,
93
+ `no-store`), `Expires`, `ETag` / `If-None-Match`, and
94
+ `Last-Modified` / `If-Modified-Since` for freshness checks and conditional
95
+ revalidation. Ships with a default in-memory store; custom stores can be
96
+ passed via `store:` option. Only GET and HEAD responses are cached. ([#223])
97
+ - `HTTP.digest_auth(user:, pass:)` for HTTP Digest Authentication (RFC 2617 /
98
+ RFC 7616). Automatically handles 401 challenges with digest credentials,
99
+ supporting MD5, SHA-256, MD5-sess, and SHA-256-sess algorithms with
100
+ quality-of-protection negotiation. Works as a chainable feature:
101
+ `HTTP.digest_auth(user: "admin", pass: "secret").get(url)` ([#448])
102
+ - Happy Eyeballs (RFC 8305) support via Ruby 3.4's native `TCPSocket`
103
+ implementation. Connection attempts now try multiple addresses (IPv6 and
104
+ IPv4) concurrently, improving reliability on dual-stack networks. Connect
105
+ timeouts are passed natively to `TCPSocket` instead of using
106
+ `Timeout.timeout`, avoiding `Thread.raise` interference with the Happy
107
+ Eyeballs state machine. ([#739])
108
+ - `HTTP.base_uri` for setting a base URI that resolves relative request paths
109
+ per RFC 3986. Supports chaining (`HTTP.base_uri("https://api.example.com/v1")
110
+ .get("users")`), and integrates with `persistent` connections by deriving the
111
+ host when omitted ([#519], [#512], [#493])
112
+ - `Request::Body#loggable?` and `Response::Body#loggable?` predicates, and a
113
+ `binary_formatter` option for the logging feature. Binary bodies
114
+ (IO/Enumerable request sources, binary-encoded request strings, and
115
+ binary-encoded responses) are now formatted instead of dumped raw,
116
+ preventing unreadable log output when transferring files like images or
117
+ audio. Available formatters: `:stats` (default, logs byte count),
118
+ `:base64` (logs base64-encoded content), or a custom `Proc`. Invalid
119
+ formatter values raise `ArgumentError` ([#784])
120
+ - `Feature#on_request` and `Feature#around_request` lifecycle hooks, called
121
+ before/around each request attempt (including retries), for per-attempt side
122
+ effects like instrumentation spans and circuit breakers ([#826])
123
+ - Pattern matching support (`deconstruct_keys`) for Response, Response::Status,
124
+ Headers, ContentType, and URI ([#642])
125
+ - Combined global and per-operation timeouts: global and per-operation timeouts
126
+ are no longer mutually exclusive. Use
127
+ `HTTP.timeout(global: 60, read: 30, write: 30, connect: 5)` to set both a
128
+ global request timeout and individual operation limits ([#773])
129
+
130
+ ### Fixed
131
+
132
+ - `HTTP::URI.form_encode` now encodes newlines as `%0A` instead of
133
+ `%0D%0A` ([#449])
134
+ - Thread-safety: `Headers::Normalizer` cache is now per-thread via
135
+ `Thread.current`, eliminating a potential race condition when multiple
136
+ threads share a normalizer instance
137
+ - Instrumentation feature now correctly starts a new span for each retry
138
+ attempt, fixing `NoMethodError` with `ActiveSupport::Notifications` when
139
+ using `.retriable` with the instrumentation feature ([#826])
140
+ - Raise `HTTP::URI::InvalidError` for malformed or schemeless URIs and
141
+ `ArgumentError` for nil or empty URIs, instead of confusing
142
+ `UnsupportedSchemeError` or `Addressable::URI::InvalidURIError` ([#565])
143
+ - Strip `Authorization` and `Cookie` headers when following redirects to a
144
+ different origin (scheme, host, or port) to prevent credential leakage
145
+ ([#516], [#770])
146
+ - AutoInflate now preserves the response charset encoding instead of
147
+ defaulting to `Encoding::BINARY` ([#535])
148
+ - `LocalJumpError` when using instrumentation with instrumenters that
149
+ unconditionally yield in `#instrument` (e.g., `ActiveSupport::Notifications`)
150
+ ([#673])
151
+ - Logging feature no longer eagerly consumes the response body at debug level;
152
+ body chunks are now logged as they are streamed, preserving
153
+ `response.body.each` ([#785])
154
+
155
+ ### Removed
156
+
157
+ - `HTTP::URI` setter methods (`scheme=`, `user=`, `password=`, `authority=`,
158
+ `origin=`, `port=`, `request_uri=`, `fragment=`) and normalized accessors
159
+ (`normalized_user`, `normalized_password`, `normalized_port`,
160
+ `normalized_path`, `normalized_query`) that were delegated to
161
+ `Addressable::URI` but never used internally
162
+ - `HTTP::URI#origin` is no longer delegated to `Addressable::URI`. The new
163
+ implementation follows RFC 6454, normalizing scheme and host to lowercase
164
+ and excluding user info from the origin string
165
+ - `HTTP::URI#request_uri` is no longer delegated to `Addressable::URI`
166
+ - `HTTP::URI#omit` is no longer delegated to `Addressable::URI` and now
167
+ returns `HTTP::URI` instead of `Addressable::URI` ([#491])
168
+ - `HTTP::URI#query_values` and `HTTP::URI#query_values=` delegations to
169
+ `Addressable::URI`. Query parameter merging now uses stdlib
170
+ `URI.decode_www_form`/`URI.encode_www_form`
171
+ - `HTTP::URI` delegations for `normalized_scheme`, `normalized_authority`,
172
+ `normalized_fragment`, and `authority` to `Addressable::URI`. The URI
173
+ normalizer now inlines these operations directly
174
+ - `HTTP::URI#join` is no longer delegated to `Addressable::URI` and now
175
+ returns `HTTP::URI` instead of `Addressable::URI`. Uses stdlib `URI.join`
176
+ with automatic percent-encoding of non-ASCII characters ([#491])
177
+ - `HTTP::URI.form_encode` no longer delegates to `Addressable::URI`. Uses
178
+ stdlib `URI.encode_www_form` instead
179
+
180
+ ### Changed
181
+
182
+ - **BREAKING** `HTTP::Response::Status` no longer inherits from `Delegator`.
183
+ It now uses `Comparable` and `Forwardable` instead, providing `to_i`,
184
+ `to_int`, and `<=>` for numeric comparisons and range matching. Code that
185
+ called `__getobj__`/`__setobj__` or relied on implicit delegation of
186
+ arbitrary `Integer` methods (e.g., `status.even?`) will need to be updated
187
+ to use `status.code` instead
188
+ - **BREAKING** Chainable option methods (`.headers`, `.timeout`, `.cookies`,
189
+ `.auth`, `.follow`, `.via`, `.use`, `.encoding`, `.nodelay`, `.basic_auth`,
190
+ `.accept`) now return a thread-safe `HTTP::Session` instead of `HTTP::Client`.
191
+ `Session` creates a new `Client` for each request, making it safe to share a
192
+ configured session across threads. `HTTP.persistent` still returns
193
+ `HTTP::Client` since persistent connections require mutable state. Code that
194
+ calls HTTP verb methods (`.get`, `.post`, etc.) or accesses `.default_options`
195
+ is unaffected. Code that checks `is_a?(HTTP::Client)` on the return value of
196
+ chainable methods will need to be updated to check for `HTTP::Session`
197
+ - **BREAKING** `.retriable` now returns `HTTP::Session` instead of
198
+ `HTTP::Retriable::Client`. Retry is a session-level option: it flows through
199
+ `HTTP::Options` into `HTTP::Client#perform`, eliminating the need for
200
+ separate `Retriable::Client` and `Retriable::Session` classes
201
+ - Improved error message when request body size cannot be determined to suggest
202
+ setting `Content-Length` explicitly or using chunked `Transfer-Encoding` ([#560])
203
+ - **BREAKING** `Connection#readpartial` now raises `EOFError` instead of
204
+ returning `nil` at end-of-stream, and supports an `outbuf` parameter,
205
+ conforming to the `IO#readpartial` API. `Body#readpartial` and
206
+ `Inflater#readpartial` also raise `EOFError` ([#618])
207
+ - **BREAKING** Stricter timeout options parsing: `.timeout()` with a Hash now
208
+ rejects unknown keys, non-numeric values, string keys, and empty hashes ([#754])
209
+ - Bumped min llhttp dependency version
210
+ - **BREAKING** Handle responses in the reverse order from the requests ([#776])
211
+ - **BREAKING** `Response#cookies` now returns `Array<HTTP::Cookie>` instead of
212
+ `HTTP::CookieJar`. The `cookies` option has been removed from `Options`;
213
+ `Chainable#cookies` now sets the `Cookie` header directly with no implicit
214
+ merging — the last `.cookies()` call wins ([#536])
215
+ - Cookie jar management during redirects moved from `Redirector` to `Session`.
216
+ `Redirector` is now a pure redirect-following engine with no cookie
217
+ awareness; `Session#request` manages cookies across redirect hops
218
+ - **BREAKING** `HTTP::Options.new`, `HTTP::Client.new`, and `HTTP::Session.new`
219
+ now accept keyword arguments instead of an options hash. For example,
220
+ `HTTP::Options.new(response: :body)` continues to work, but
221
+ `HTTP::Options.new({response: :body})` must be updated to
222
+ `HTTP::Options.new(**options)`. Invalid option names now raise
223
+ `ArgumentError` automatically ([#447])
224
+
225
+ ### Removed
226
+
227
+ - **BREAKING** Drop Ruby 2.x support
228
+ - **BREAKING** Remove `Headers::Mixin` and the `[]`/`[]=` delegators on
229
+ `Request` and `Response`. Use `request.headers["..."]` and
230
+ `response.headers["..."]` instead ([#537])
231
+
232
+ [#270]: https://github.com/httprb/http/issues/270
233
+ [#223]: https://github.com/httprb/http/issues/223
234
+ [#358]: https://github.com/httprb/http/issues/358
235
+ [#371]: https://github.com/httprb/http/issues/371
236
+ [#372]: https://github.com/httprb/http/issues/372
237
+ [#447]: https://github.com/httprb/http/issues/447
238
+ [#448]: https://github.com/httprb/http/issues/448
239
+ [#449]: https://github.com/httprb/http/issues/449
240
+ [#491]: https://github.com/httprb/http/issues/491
241
+ [#493]: https://github.com/httprb/http/pull/493
242
+ [#512]: https://github.com/httprb/http/issues/512
243
+ [#516]: https://github.com/httprb/http/issues/516
244
+ [#519]: https://github.com/httprb/http/issues/519
245
+ [#535]: https://github.com/httprb/http/issues/535
246
+ [#536]: https://github.com/httprb/http/issues/536
247
+ [#537]: https://github.com/httprb/http/issues/537
248
+ [#544]: https://github.com/httprb/http/issues/544
249
+ [#557]: https://github.com/httprb/http/issues/557
250
+ [#560]: https://github.com/httprb/http/pull/560
251
+ [#565]: https://github.com/httprb/http/issues/565
252
+ [#566]: https://github.com/httprb/http/issues/566
253
+ [#579]: https://github.com/httprb/http/issues/579
254
+ [#618]: https://github.com/httprb/http/pull/618
255
+ [#621]: https://github.com/httprb/http/issues/621
256
+ [#642]: https://github.com/httprb/http/issues/642
257
+ [#667]: https://github.com/httprb/http/issues/667
258
+ [#673]: https://github.com/httprb/http/issues/673
259
+ [#739]: https://github.com/httprb/http/issues/739
260
+ [#754]: https://github.com/httprb/http/pull/754
261
+ [#770]: https://github.com/httprb/http/issues/770
262
+ [#773]: https://github.com/httprb/http/issues/773
263
+ [#776]: https://github.com/httprb/http/issues/776
264
+ [#784]: https://github.com/httprb/http/issues/784
265
+ [#785]: https://github.com/httprb/http/issues/785
266
+ [#826]: https://github.com/httprb/http/issues/826
267
+ [unreleased]: https://github.com/httprb/http/compare/v5.3.0...main
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,26 @@
1
+ # Help and Discussion
2
+
3
+ If you need help or just want to talk about the http.rb,
4
+ visit the http.rb Google Group:
5
+
6
+ https://groups.google.com/forum/#!forum/httprb
7
+
8
+ You can join by email by sending a message to:
9
+
10
+ [httprb+subscribe@googlegroups.com](mailto:httprb+subscribe@googlegroups.com)
11
+
12
+
13
+ # Reporting bugs
14
+
15
+ The best way to report a bug is by providing a reproduction script. A half
16
+ working script with comments for the parts you were unable to automate is still
17
+ appreciated.
18
+
19
+ In any case, specify following info in description of your issue:
20
+
21
+ - What you're trying to accomplish
22
+ - What you expected to happen
23
+ - What actually happened
24
+ - The exception backtrace(s), if any
25
+ - Version of gem or commit ref you are using
26
+ - Version of ruby you are using
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011-2026 Tony Arcieri, Erik Berlin, Alexey V. Zapparov, Zachary Anker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,263 @@
1
+ # ![http.rb](https://raw.github.com/httprb/http.rb/main/logo.png)
2
+
3
+ [![Gem Version][gem-image]][gem-link]
4
+ [![MIT licensed][license-image]][license-link]
5
+ [![Docs][docs-image]][docs-link]
6
+ [![Lint][lint-image]][lint-link]
7
+ [![Mutant][mutant-image]][mutant-link]
8
+ [![Test][test-image]][test-link]
9
+ [![Typecheck][typecheck-image]][typecheck-link]
10
+
11
+ [Documentation]
12
+
13
+ ## About
14
+
15
+ HTTP (The Gem! a.k.a. http.rb) is an easy-to-use client library for making requests
16
+ from Ruby. It uses a simple method chaining system for building requests, similar to
17
+ Python's [Requests].
18
+
19
+ Under the hood, http.rb uses the [llhttp] parser, a fast HTTP parsing native extension.
20
+ This library isn't just yet another wrapper around `Net::HTTP`. It implements the HTTP
21
+ protocol natively and outsources the parsing to native extensions.
22
+
23
+ ### Why http.rb?
24
+
25
+ - **Clean API**: http.rb offers an easy-to-use API that should be a
26
+ breath of fresh air after using something like Net::HTTP.
27
+
28
+ - **Maturity**: http.rb is one of the most mature Ruby HTTP clients, supporting
29
+ features like persistent connections and fine-grained timeouts.
30
+
31
+ - **Performance**: using native parsers and a clean, lightweight implementation,
32
+ http.rb achieves high performance while implementing HTTP in Ruby instead of C.
33
+
34
+
35
+ ## Installation
36
+
37
+ Add this line to your application's Gemfile:
38
+ ```ruby
39
+ gem "http"
40
+ ```
41
+
42
+ And then execute:
43
+ ```bash
44
+ $ bundle
45
+ ```
46
+
47
+ Or install it yourself as:
48
+ ```bash
49
+ $ gem install http
50
+ ```
51
+
52
+ Inside of your Ruby program do:
53
+ ```ruby
54
+ require "http"
55
+ ```
56
+
57
+ ...to pull it in as a dependency.
58
+
59
+
60
+ ## Documentation
61
+
62
+ [Please see the http.rb wiki][documentation]
63
+ for more detailed documentation and usage notes.
64
+
65
+ The following API documentation is also available:
66
+
67
+ - [YARD API documentation](https://www.rubydoc.info/github/httprb/http)
68
+ - [Chainable module (all chainable methods)](https://www.rubydoc.info/github/httprb/http/HTTP/Chainable)
69
+
70
+
71
+ ### Basic Usage
72
+
73
+ Here's some simple examples to get you started:
74
+
75
+ ```ruby
76
+ >> HTTP.get("https://github.com").to_s
77
+ => "\n\n\n<!DOCTYPE html>\n<html lang=\"en\" class=\"\">\n <head prefix=\"o..."
78
+ ```
79
+
80
+ That's all it takes! To obtain an `HTTP::Response` object instead of the response
81
+ body, all we have to do is omit the `#to_s` on the end:
82
+
83
+ ```ruby
84
+ >> HTTP.get("https://github.com")
85
+ => #<HTTP::Response/1.1 200 OK {"Server"=>"GitHub.com", "Date"=>"Tue, 10 May...>
86
+ ```
87
+
88
+ We can also obtain an `HTTP::Response::Body` object for this response:
89
+
90
+ ```ruby
91
+ >> HTTP.get("https://github.com").body
92
+ => #<HTTP::Response::Body:3ff756862b48 @streaming=false>
93
+ ```
94
+
95
+ The response body can be streamed with `HTTP::Response::Body#readpartial`.
96
+ In practice, you'll want to bind the `HTTP::Response::Body` to a local variable
97
+ and call `#readpartial` on it repeatedly until it returns `nil`:
98
+
99
+ ```ruby
100
+ >> body = HTTP.get("https://github.com").body
101
+ => #<HTTP::Response::Body:3ff756862b48 @streaming=false>
102
+ >> body.readpartial
103
+ => "\n\n\n<!DOCTYPE html>\n<html lang=\"en\" class=\"\">\n <head prefix=\"o..."
104
+ >> body.readpartial
105
+ => "\" href=\"/apple-touch-icon-72x72.png\">\n <link rel=\"apple-touch-ic..."
106
+ # ...
107
+ >> body.readpartial
108
+ => nil
109
+ ```
110
+
111
+ ### Pattern Matching
112
+
113
+ Response objects support Ruby's pattern matching:
114
+
115
+ ```ruby
116
+ case HTTP.get("https://api.example.com/users")
117
+ in { status: 200..299, body: body }
118
+ JSON.parse(body.to_s)
119
+ in { status: 404 }
120
+ nil
121
+ in { status: 400.. }
122
+ raise "request failed"
123
+ end
124
+ ```
125
+
126
+ Pattern matching is also supported on `HTTP::Response::Status`, `HTTP::Headers`,
127
+ `HTTP::ContentType`, and `HTTP::URI`.
128
+
129
+ ### Base URI
130
+
131
+ Set a base URI to avoid repeating the scheme and host in every request:
132
+
133
+ ```ruby
134
+ api = HTTP.base_uri("https://api.example.com/v1")
135
+ api.get("users") # GET https://api.example.com/v1/users
136
+ api.get("users/1") # GET https://api.example.com/v1/users/1
137
+ ```
138
+
139
+ Relative paths are resolved per [RFC 3986](https://www.rfc-editor.org/rfc/rfc3986#section-5).
140
+ Combine with `persistent` to reuse the connection:
141
+
142
+ ```ruby
143
+ HTTP.base_uri("https://api.example.com/v1").persistent do |http|
144
+ http.get("users")
145
+ http.get("posts")
146
+ end
147
+ ```
148
+
149
+ ### Thread Safety
150
+
151
+ Configured sessions are safe to share across threads:
152
+
153
+ ```ruby
154
+ # Build a session once, use it from any thread
155
+ session = HTTP.headers("Accept" => "application/json")
156
+ .timeout(10)
157
+ .auth("Bearer token")
158
+
159
+ threads = 10.times.map do
160
+ Thread.new { session.get("https://example.com/api/data") }
161
+ end
162
+ threads.each(&:join)
163
+ ```
164
+
165
+ Chainable configuration methods (`.headers`, `.timeout`, `.auth`, etc.) return
166
+ an `HTTP::Session`, which creates a fresh `HTTP::Client` for every request.
167
+
168
+ Persistent connections (`HTTP.persistent`) return an `HTTP::Session` that pools
169
+ one `HTTP::Client` per origin. The session itself is **not** thread-safe. For
170
+ thread-safe persistent connections, use the
171
+ [connection_pool](https://rubygems.org/gems/connection_pool) gem:
172
+
173
+ ```ruby
174
+ pool = ConnectionPool.new(size: 5) { HTTP.persistent("https://example.com") }
175
+ pool.with { |http| http.get("/path") }
176
+ ```
177
+
178
+ Cross-origin redirects are handled transparently — the session opens a separate
179
+ persistent connection for each origin encountered during a redirect chain:
180
+
181
+ ```ruby
182
+ HTTP.persistent("https://example.com").follow do |http|
183
+ http.get("/moved-to-other-domain") # follows redirect across origins
184
+ end
185
+ ```
186
+
187
+ ## Supported Ruby Versions
188
+
189
+ This library aims to support and is [tested against][build-link]
190
+ the following Ruby versions:
191
+
192
+ - Ruby 3.2
193
+ - Ruby 3.3
194
+ - Ruby 3.4
195
+ - Ruby 4.0
196
+
197
+ If something doesn't work on one of these versions, it's a bug.
198
+
199
+ This library may inadvertently work (or seem to work) on other Ruby versions,
200
+ however support will only be provided for the versions listed above.
201
+
202
+ If you would like this library to support another Ruby version or
203
+ implementation, you may volunteer to be a maintainer. Being a maintainer
204
+ entails making sure all tests run and pass on that implementation. When
205
+ something breaks on your implementation, you will be responsible for providing
206
+ patches in a timely fashion. If critical issues for a particular implementation
207
+ exist at the time of a major release, support for that Ruby version may be
208
+ dropped.
209
+
210
+
211
+ ## Upgrading
212
+
213
+ See [UPGRADING.md] for a detailed migration guide between major versions.
214
+
215
+
216
+ ## Security
217
+
218
+ See [SECURITY.md] for reporting vulnerabilities.
219
+
220
+
221
+ ## Contributing to http.rb
222
+
223
+ See [CONTRIBUTING.md] for guidelines, or the quick version:
224
+
225
+ - Fork http.rb on GitHub
226
+ - Make your changes
227
+ - Ensure all tests pass (`bundle exec rake`)
228
+ - Send a pull request
229
+ - If we like them we'll merge them
230
+ - If we've accepted a patch, feel free to ask for commit access!
231
+
232
+
233
+ ## Copyright
234
+
235
+ Copyright © 2011-2026 Tony Arcieri, Erik Berlin, Alexey V. Zapparov, Zachary Anker.
236
+ See LICENSE.txt for further details.
237
+
238
+
239
+ [//]: # (badges)
240
+
241
+ [gem-image]: https://img.shields.io/gem/v/http?logo=ruby
242
+ [gem-link]: https://rubygems.org/gems/http
243
+ [license-image]: https://img.shields.io/badge/license-MIT-blue.svg
244
+ [license-link]: https://github.com/httprb/http/blob/main/LICENSE.txt
245
+ [docs-image]: https://github.com/httprb/http/actions/workflows/docs.yml/badge.svg
246
+ [docs-link]: https://github.com/httprb/http/actions/workflows/docs.yml
247
+ [lint-image]: https://github.com/httprb/http/actions/workflows/lint.yml/badge.svg
248
+ [lint-link]: https://github.com/httprb/http/actions/workflows/lint.yml
249
+ [mutant-image]: https://github.com/httprb/http/actions/workflows/mutant.yml/badge.svg
250
+ [mutant-link]: https://github.com/httprb/http/actions/workflows/mutant.yml
251
+ [test-image]: https://github.com/httprb/http/actions/workflows/test.yml/badge.svg
252
+ [test-link]: https://github.com/httprb/http/actions/workflows/test.yml
253
+ [typecheck-image]: https://github.com/httprb/http/actions/workflows/typecheck.yml/badge.svg
254
+ [typecheck-link]: https://github.com/httprb/http/actions/workflows/typecheck.yml
255
+
256
+ [//]: # (links)
257
+
258
+ [contributing.md]: https://github.com/httprb/http/blob/main/CONTRIBUTING.md
259
+ [documentation]: https://github.com/httprb/http/wiki
260
+ [llhttp]: https://llhttp.org/
261
+ [requests]: https://docs.python-requests.org/en/latest/
262
+ [security.md]: https://github.com/httprb/http/blob/main/SECURITY.md
263
+ [upgrading.md]: https://github.com/httprb/http/blob/main/UPGRADING.md
data/SECURITY.md ADDED
@@ -0,0 +1,17 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ Security updates are applied only to the most recent release.
6
+
7
+ ## Reporting a Vulnerability
8
+
9
+ If you have discovered a security vulnerability in this project, please report
10
+ it privately. **Do not disclose it as a public issue.** This gives us time to
11
+ work with you to fix the issue before public exposure, reducing the chance that
12
+ the exploit will be used before a patch is released.
13
+
14
+ Please disclose it at [security advisory](https://github.com/httprb/http/security/advisories/new).
15
+
16
+ This project is maintained by a team of volunteers on a reasonable-effort basis.
17
+ As such, please give us at least 90 days to work on a fix before public exposure.