httparty 0.21.0 → 0.24.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.
@@ -113,6 +113,8 @@ module HTTParty
113
113
  new_uri = path.clone
114
114
  end
115
115
 
116
+ validate_uri_safety!(new_uri) unless redirect
117
+
116
118
  # avoid double query string on redirects [#12]
117
119
  unless redirect
118
120
  new_uri.query = query_string(new_uri)
@@ -153,24 +155,28 @@ module HTTParty
153
155
  chunked_body = nil
154
156
  current_http = http
155
157
 
156
- self.last_response = current_http.request(@raw_request) do |http_response|
157
- if block
158
- chunks = []
158
+ begin
159
+ self.last_response = current_http.request(@raw_request) do |http_response|
160
+ if block
161
+ chunks = []
159
162
 
160
- http_response.read_body do |fragment|
161
- encoded_fragment = encode_text(fragment, http_response['content-type'])
162
- chunks << encoded_fragment if !options[:stream_body]
163
- block.call ResponseFragment.new(encoded_fragment, http_response, current_http)
164
- end
163
+ http_response.read_body do |fragment|
164
+ encoded_fragment = encode_text(fragment, http_response['content-type'])
165
+ chunks << encoded_fragment if !options[:stream_body]
166
+ block.call ResponseFragment.new(encoded_fragment, http_response, current_http)
167
+ end
165
168
 
166
- chunked_body = chunks.join
169
+ chunked_body = chunks.join
170
+ end
167
171
  end
168
- end
169
172
 
170
- handle_host_redirection if response_redirects?
171
- result = handle_unauthorized
172
- result ||= handle_response(chunked_body, &block)
173
- result
173
+ handle_host_redirection if response_redirects?
174
+ result = handle_unauthorized
175
+ result ||= handle_response(chunked_body, &block)
176
+ result
177
+ rescue *COMMON_NETWORK_ERRORS => e
178
+ raise options[:foul] ? HTTParty::NetworkError.new("#{e.class}: #{e.message}") : e
179
+ end
174
180
  end
175
181
 
176
182
  def handle_unauthorized(&block)
@@ -241,8 +247,17 @@ module HTTParty
241
247
  if body.multipart?
242
248
  content_type = "multipart/form-data; boundary=#{body.boundary}"
243
249
  @raw_request['Content-Type'] = content_type
250
+ elsif options[:body].respond_to?(:to_hash) && !@raw_request['Content-Type']
251
+ @raw_request['Content-Type'] = 'application/x-www-form-urlencoded'
252
+ end
253
+
254
+ if body.streaming? && options[:stream_body] != false
255
+ stream = body.to_stream
256
+ @raw_request.body_stream = stream
257
+ @raw_request['Content-Length'] = stream.size.to_s
258
+ else
259
+ @raw_request.body = body.call
244
260
  end
245
- @raw_request.body = body.call
246
261
  end
247
262
 
248
263
  @raw_request.instance_variable_set(:@decode_content, decompress_content?)
@@ -295,24 +310,7 @@ module HTTParty
295
310
 
296
311
  def handle_response(raw_body, &block)
297
312
  if response_redirects?
298
- options[:limit] -= 1
299
- if options[:logger]
300
- logger = HTTParty::Logger.build(options[:logger], options[:log_level], options[:log_format])
301
- logger.format(self, last_response)
302
- end
303
- self.path = last_response['location']
304
- self.redirect = true
305
- if last_response.class == Net::HTTPSeeOther
306
- unless options[:maintain_method_across_redirects] && options[:resend_on_redirect]
307
- self.http_method = Net::HTTP::Get
308
- end
309
- elsif last_response.code != '307' && last_response.code != '308'
310
- unless options[:maintain_method_across_redirects]
311
- self.http_method = Net::HTTP::Get
312
- end
313
- end
314
- capture_cookies(last_response)
315
- perform(&block)
313
+ handle_redirection(&block)
316
314
  else
317
315
  raw_body ||= last_response.body
318
316
 
@@ -331,10 +329,34 @@ module HTTParty
331
329
  end
332
330
  end
333
331
 
332
+ def handle_redirection(&block)
333
+ options[:limit] -= 1
334
+ if options[:logger]
335
+ logger = HTTParty::Logger.build(options[:logger], options[:log_level], options[:log_format])
336
+ logger.format(self, last_response)
337
+ end
338
+ self.path = last_response['location']
339
+ self.redirect = true
340
+ if last_response.class == Net::HTTPSeeOther
341
+ unless options[:maintain_method_across_redirects] && options[:resend_on_redirect]
342
+ self.http_method = Net::HTTP::Get
343
+ end
344
+ elsif last_response.code != '307' && last_response.code != '308'
345
+ unless options[:maintain_method_across_redirects]
346
+ self.http_method = Net::HTTP::Get
347
+ end
348
+ end
349
+ if http_method == Net::HTTP::Get
350
+ clear_body
351
+ end
352
+ capture_cookies(last_response)
353
+ perform(&block)
354
+ end
355
+
334
356
  def handle_host_redirection
335
357
  check_duplicate_location_header
336
358
  redirect_path = options[:uri_adapter].parse(last_response['location']).normalize
337
- return if redirect_path.relative? || path.host == redirect_path.host
359
+ return if redirect_path.relative? || path.host == redirect_path.host || uri.host == redirect_path.host
338
360
  @changed_hosts = true
339
361
  end
340
362
 
@@ -362,6 +384,14 @@ module HTTParty
362
384
  parser.call(body, format)
363
385
  end
364
386
 
387
+ # Some Web Application Firewalls reject incoming GET requests that have a body
388
+ # if we redirect, and the resulting verb is GET then we will clear the body that
389
+ # may be left behind from the initiating request
390
+ def clear_body
391
+ options[:body] = nil
392
+ @raw_request.body = nil
393
+ end
394
+
365
395
  def capture_cookies(response)
366
396
  return unless response['Set-Cookie']
367
397
  cookies_hash = HTTParty::CookieHash.new
@@ -414,5 +444,23 @@ module HTTParty
414
444
  assume_utf16_is_big_endian: assume_utf16_is_big_endian
415
445
  ).call
416
446
  end
447
+
448
+ def validate_uri_safety!(new_uri)
449
+ return if options[:skip_uri_validation]
450
+
451
+ configured_base_uri = options[:base_uri]
452
+ return unless configured_base_uri
453
+
454
+ normalized_base = options[:uri_adapter].parse(
455
+ HTTParty.normalize_base_uri(configured_base_uri)
456
+ )
457
+
458
+ return if new_uri.host == normalized_base.host
459
+
460
+ raise UnsafeURIError,
461
+ "Requested URI '#{new_uri}' has host '#{new_uri.host}' but the " \
462
+ "configured base_uri '#{normalized_base}' has host '#{normalized_base.host}'. " \
463
+ "This request could send credentials to an unintended server."
464
+ end
417
465
  end
418
466
  end
@@ -67,12 +67,12 @@ module HTTParty
67
67
  end
68
68
 
69
69
  # Support old multiple_choice? method from pre 2.0.0 era.
70
- if ::RUBY_VERSION >= '2.0.0' && ::RUBY_PLATFORM != 'java'
70
+ if ::RUBY_PLATFORM != 'java'
71
71
  alias_method :multiple_choice?, :multiple_choices?
72
72
  end
73
73
 
74
74
  # Support old status codes method from pre 2.6.0 era.
75
- if ::RUBY_VERSION >= '2.6.0' && ::RUBY_PLATFORM != 'java'
75
+ if ::RUBY_PLATFORM != 'java'
76
76
  alias_method :gateway_time_out?, :gateway_timeout?
77
77
  alias_method :request_entity_too_large?, :payload_too_large?
78
78
  alias_method :request_time_out?, :request_timeout?
@@ -133,7 +133,7 @@ module HTTParty
133
133
  end
134
134
 
135
135
  def throw_exception
136
- if @request.options[:raise_on] && @request.options[:raise_on].include?(code)
136
+ if @request.options[:raise_on].to_a.detect { |c| code.to_s.match(/#{c.to_s}/) }
137
137
  ::Kernel.raise ::HTTParty::ResponseError.new(@response), "Code #{code} - #{body}"
138
138
  end
139
139
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTParty
4
- VERSION = '0.21.0'
4
+ VERSION = '0.24.0'
5
5
  end
data/lib/httparty.rb CHANGED
@@ -3,11 +3,6 @@
3
3
  require 'pathname'
4
4
  require 'net/http'
5
5
  require 'uri'
6
- require 'zlib'
7
- require 'multi_xml'
8
- require 'mini_mime'
9
- require 'json'
10
- require 'csv'
11
6
 
12
7
  require 'httparty/module_inheritable_attributes'
13
8
  require 'httparty/cookie_hash'
@@ -67,6 +62,16 @@ module HTTParty
67
62
  # * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.
68
63
 
69
64
  module ClassMethods
65
+ # Turns on or off the foul option.
66
+ #
67
+ # class Foo
68
+ # include HTTParty
69
+ # foul true
70
+ # end
71
+ def foul(bool)
72
+ default_options[:foul] = bool
73
+ end
74
+
70
75
  # Turns on logging
71
76
  #
72
77
  # class Foo
@@ -83,7 +88,7 @@ module HTTParty
83
88
  #
84
89
  # class Foo
85
90
  # include HTTParty
86
- # raise_on [404, 500]
91
+ # raise_on [404, 500, '5[0-9]*']
87
92
  # end
88
93
  def raise_on(codes = [])
89
94
  default_options[:raise_on] = *codes
@@ -591,6 +596,13 @@ module HTTParty
591
596
  perform_request Net::HTTP::Unlock, path, options, &block
592
597
  end
593
598
 
599
+ def build_request(http_method, path, options = {})
600
+ options = ModuleInheritableAttributes.hash_deep_dup(default_options).merge(options)
601
+ HeadersProcessor.new(headers, options).call
602
+ process_cookies(options)
603
+ Request.new(http_method, path, options)
604
+ end
605
+
594
606
  attr_reader :default_options
595
607
 
596
608
  private
@@ -606,10 +618,7 @@ module HTTParty
606
618
  end
607
619
 
608
620
  def perform_request(http_method, path, options, &block) #:nodoc:
609
- options = ModuleInheritableAttributes.hash_deep_dup(default_options).merge(options)
610
- HeadersProcessor.new(headers, options).call
611
- process_cookies(options)
612
- Request.new(http_method, path, options).perform(&block)
621
+ build_request(http_method, path, options).perform(&block)
613
622
  end
614
623
 
615
624
  def process_cookies(options) #:nodoc:
@@ -676,6 +685,10 @@ module HTTParty
676
685
  def self.options(*args, &block)
677
686
  Basement.options(*args, &block)
678
687
  end
688
+
689
+ def self.build_request(*args, &block)
690
+ Basement.build_request(*args, &block)
691
+ end
679
692
  end
680
693
 
681
694
  require 'httparty/hash_conversions'
data/script/release CHANGED
@@ -18,9 +18,9 @@ gem_name=httparty
18
18
  rm -rf $gem_name-*.gem
19
19
  gem build -q $gem_name.gemspec
20
20
 
21
- # Make sure we're on the master branch.
22
- (git branch | grep -q '* master') || {
23
- echo "Only release from the master branch."
21
+ # Make sure we're on the main branch.
22
+ (git branch | grep -q '* main') || {
23
+ echo "Only release from the main branch."
24
24
  exit 1
25
25
  }
26
26
 
@@ -39,4 +39,4 @@ git fetch -t origin
39
39
 
40
40
  # Tag it and bag it.
41
41
  gem push $gem_name-*.gem && git tag "$tag" &&
42
- git push origin master && git push origin "$tag"
42
+ git push origin main && git push origin "$tag"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httparty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.24.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
@@ -9,8 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-12-30 00:00:00.000000000 Z
12
+ date: 2025-12-28 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: csv
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
14
28
  - !ruby/object:Gem::Dependency
15
29
  name: multi_xml
16
30
  requirement: !ruby/object:Gem::Requirement
@@ -48,6 +62,7 @@ extensions: []
48
62
  extra_rdoc_files: []
49
63
  files:
50
64
  - ".editorconfig"
65
+ - ".github/dependabot.yml"
51
66
  - ".github/workflows/ci.yml"
52
67
  - ".gitignore"
53
68
  - ".rubocop.yml"
@@ -76,6 +91,7 @@ files:
76
91
  - examples/microsoft_graph.rb
77
92
  - examples/multipart.rb
78
93
  - examples/nokogiri_html_parser.rb
94
+ - examples/party_foul_mode.rb
79
95
  - examples/peer_cert.rb
80
96
  - examples/rescue_json.rb
81
97
  - examples/rubyurl.rb
@@ -102,6 +118,7 @@ files:
102
118
  - lib/httparty/request.rb
103
119
  - lib/httparty/request/body.rb
104
120
  - lib/httparty/request/multipart_boundary.rb
121
+ - lib/httparty/request/streaming_multipart_body.rb
105
122
  - lib/httparty/response.rb
106
123
  - lib/httparty/response/headers.rb
107
124
  - lib/httparty/response_fragment.rb
@@ -114,7 +131,8 @@ files:
114
131
  homepage: https://github.com/jnunemaker/httparty
115
132
  licenses:
116
133
  - MIT
117
- metadata: {}
134
+ metadata:
135
+ changelog_uri: https://github.com/jnunemaker/httparty/releases
118
136
  post_install_message: When you HTTParty, you must party hard!
119
137
  rdoc_options: []
120
138
  require_paths:
@@ -123,7 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
123
141
  requirements:
124
142
  - - ">="
125
143
  - !ruby/object:Gem::Version
126
- version: 2.3.0
144
+ version: 2.7.0
127
145
  required_rubygems_version: !ruby/object:Gem::Requirement
128
146
  requirements:
129
147
  - - ">="