patient_http 1.3.0 → 1.5.0

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.
@@ -20,6 +20,7 @@ module PatientHttp
20
20
  @on_error = on_error
21
21
  @proxy_client = nil
22
22
  @request_preparer = RequestPreparer.new(config)
23
+ @response_reader = ResponseReader.new(nil, config: config)
23
24
  end
24
25
 
25
26
  # Execute the request synchronously.
@@ -62,11 +63,12 @@ module PatientHttp
62
63
  # flattened to a single joined string value.
63
64
  headers_hash = async_response.headers.to_h.transform_values(&:to_s)
64
65
 
65
- body_content = read_response_body(async_response, headers_hash)
66
+ chunks = read_response_body(async_response, headers_hash)
67
+ body_content = @response_reader.decode_body(chunks, headers_hash)
66
68
 
67
69
  {
68
70
  status: async_response.status,
69
- headers: headers_hash,
71
+ headers: ResponseReader.rewrite_content_encoding(headers_hash),
70
72
  body: body_content
71
73
  }
72
74
  end
@@ -133,19 +135,22 @@ module PatientHttp
133
135
 
134
136
  # Create HTTP client with config settings (retries, proxy, connection timeout).
135
137
  #
138
+ # The client is not wrapped in a Protocol::HTTP::AcceptEncoding middleware.
139
+ # That wrapper overwrites the request's accept-encoding header, which would
140
+ # ignore a caller opting out of compression, so response bodies are decoded
141
+ # by ResponseReader here exactly as they are on the async path.
142
+ #
136
143
  # @param url [String] the resolved request URL
137
- # @return [Protocol::HTTP::AcceptEncoding] wrapped HTTP client
144
+ # @return [Async::HTTP::Client] the HTTP client
138
145
  def create_http_client(url)
139
146
  endpoint = Async::HTTP::Endpoint.parse(url)
140
147
  endpoint = configure_endpoint(endpoint) if @config.connection_timeout
141
148
 
142
- client = if @config.proxy_url
149
+ if @config.proxy_url
143
150
  create_proxied_client(endpoint)
144
151
  else
145
152
  Async::HTTP::Client.new(endpoint, retries: @config.retries)
146
153
  end
147
-
148
- Protocol::HTTP::AcceptEncoding.new(client)
149
154
  end
150
155
 
151
156
  # Create a proxied HTTP client.
@@ -174,11 +179,13 @@ module PatientHttp
174
179
  )
175
180
  end
176
181
 
177
- # Read the response body with size validation.
182
+ # Read the raw response body chunks with size validation. The chunks are
183
+ # the wire bytes; ResponseReader#decode_body inflates and applies the
184
+ # charset, enforcing the same limit on the inflated bytes.
178
185
  #
179
186
  # @param async_response [Async::HTTP::Protocol::Response] the async HTTP response
180
187
  # @param headers_hash [Hash] the response headers
181
- # @return [String, nil] the response body
188
+ # @return [Array<String>, nil] the raw body chunks or nil if no body present
182
189
  def read_response_body(async_response, headers_hash)
183
190
  return nil unless async_response.body
184
191
 
@@ -210,31 +217,7 @@ module PatientHttp
210
217
  async_response.body.close unless finished
211
218
  end
212
219
 
213
- body = chunks.join.force_encoding(Encoding::ASCII_8BIT)
214
-
215
- charset = extract_charset(headers_hash)
216
- if charset
217
- begin
218
- encoding = Encoding.find(charset)
219
- body.force_encoding(encoding)
220
- rescue ArgumentError
221
- # Invalid charset, keep binary
222
- end
223
- end
224
-
225
- body
226
- end
227
-
228
- # Extract charset from Content-Type header.
229
- def extract_charset(headers_hash)
230
- content_type = headers_hash["content-type"]
231
- return nil unless content_type
232
-
233
- match = content_type.match(/;\s*charset\s*=\s*([^;\s]+)/i)
234
- return nil unless match
235
-
236
- charset = match[1].strip
237
- charset.gsub(/\A["']|["']\z/, "")
220
+ chunks
238
221
  end
239
222
 
240
223
  # Invoke callback synchronously.
data/lib/patient_http.rb CHANGED
@@ -30,6 +30,12 @@ module PatientHttp
30
30
 
31
31
  class ResponseTooLargeError < StandardError; end
32
32
 
33
+ # Raised when a request names a processor that is not configured. Handlers
34
+ # that support named processors raise this at enqueue time; the executing
35
+ # side raises it for a job that names an unconfigured processor so the job
36
+ # lands in the job system's retry mechanism instead of being dropped.
37
+ class UnknownProcessorError < StandardError; end
38
+
33
39
  # HTTP redirect status codes that should be followed
34
40
  FOLLOWABLE_REDIRECT_STATUSES = [301, 302, 303, 307, 308].freeze
35
41
 
@@ -45,6 +51,7 @@ module PatientHttp
45
51
  autoload :Client, File.join(__dir__, "patient_http/client")
46
52
  autoload :ClientError, File.join(__dir__, "patient_http/http_error")
47
53
  autoload :ClientPool, File.join(__dir__, "patient_http/client_pool")
54
+ autoload :CompletionExecutor, File.join(__dir__, "patient_http/completion_executor")
48
55
  autoload :Configuration, File.join(__dir__, "patient_http/configuration")
49
56
  autoload :Encryptor, File.join(__dir__, "patient_http/encryptor")
50
57
  autoload :Error, File.join(__dir__, "patient_http/error")
@@ -323,6 +330,8 @@ module PatientHttp
323
330
  # @param callback_args [Hash, nil] JSON-compatible callback arguments
324
331
  # @param preprocessors [String, Symbol, Array<String, Symbol>, nil] names of preprocessors
325
332
  # registered on the configuration to apply to the request when it is sent
333
+ # @param processor [String, Symbol, nil] name of the processor that should execute
334
+ # the request; handlers that support named processors route on this value
326
335
  # @return [Object] return value from the registered request handler
327
336
  def request(
328
337
  method,
@@ -335,7 +344,8 @@ module PatientHttp
335
344
  timeout: nil,
336
345
  raise_error_responses: nil,
337
346
  callback_args: nil,
338
- preprocessors: nil
347
+ preprocessors: nil,
348
+ processor: nil
339
349
  )
340
350
  request = Request.new(
341
351
  method,
@@ -345,7 +355,8 @@ module PatientHttp
345
355
  headers: headers,
346
356
  params: params,
347
357
  timeout: timeout,
348
- preprocessors: preprocessors
358
+ preprocessors: preprocessors,
359
+ processor: processor
349
360
  )
350
361
  execute(
351
362
  request: request,
data/patient_http.gemspec CHANGED
@@ -43,6 +43,4 @@ Gem::Specification.new do |spec|
43
43
  spec.add_dependency "async-http", "~> 0.60"
44
44
  spec.add_dependency "concurrent-ruby", "~> 1.2"
45
45
  spec.add_dependency "logger"
46
-
47
- spec.add_development_dependency "bundler"
48
46
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: patient_http
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Durand
@@ -65,20 +65,6 @@ dependencies:
65
65
  - - ">="
66
66
  - !ruby/object:Gem::Version
67
67
  version: '0'
68
- - !ruby/object:Gem::Dependency
69
- name: bundler
70
- requirement: !ruby/object:Gem::Requirement
71
- requirements:
72
- - - ">="
73
- - !ruby/object:Gem::Version
74
- version: '0'
75
- type: :development
76
- prerelease: false
77
- version_requirements: !ruby/object:Gem::Requirement
78
- requirements:
79
- - - ">="
80
- - !ruby/object:Gem::Version
81
- version: '0'
82
68
  description: This gem provides a dedicated async HTTP processor that uses Ruby's Fiber
83
69
  scheduler for non-blocking I/O. Application threads hand off HTTP requests to the
84
70
  processor and return immediately. The processor handles hundreds of concurrent HTTP
@@ -103,6 +89,7 @@ files:
103
89
  - lib/patient_http/class_helper.rb
104
90
  - lib/patient_http/client.rb
105
91
  - lib/patient_http/client_pool.rb
92
+ - lib/patient_http/completion_executor.rb
106
93
  - lib/patient_http/configuration.rb
107
94
  - lib/patient_http/encryptor.rb
108
95
  - lib/patient_http/error.rb
@@ -159,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
146
  - !ruby/object:Gem::Version
160
147
  version: '0'
161
148
  requirements: []
162
- rubygems_version: 4.0.3
149
+ rubygems_version: 3.6.9
163
150
  specification_version: 4
164
151
  summary: Generic async HTTP connection pool for Ruby applications using Fiber-based
165
152
  concurrency