httparty 0.14.0 → 0.20.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of httparty might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.editorconfig +18 -0
- data/.github/workflows/ci.yml +23 -0
- data/.gitignore +2 -0
- data/.rubocop_todo.yml +1 -1
- data/{History → Changelog.md} +216 -63
- data/Gemfile +6 -1
- data/README.md +8 -8
- data/bin/httparty +3 -1
- data/docs/README.md +108 -37
- data/examples/README.md +34 -12
- data/examples/aaws.rb +7 -3
- data/examples/body_stream.rb +14 -0
- data/examples/crack.rb +1 -1
- data/examples/custom_parsers.rb +5 -1
- data/examples/delicious.rb +4 -4
- data/examples/headers_and_user_agents.rb +7 -3
- data/examples/idn.rb +10 -0
- data/examples/logging.rb +4 -4
- data/examples/microsoft_graph.rb +52 -0
- data/examples/multipart.rb +22 -0
- data/examples/peer_cert.rb +9 -0
- data/examples/stackexchange.rb +1 -1
- data/examples/stream_download.rb +26 -0
- data/examples/tripit_sign_in.rb +1 -1
- data/examples/twitter.rb +2 -2
- data/examples/whoismyrep.rb +1 -1
- data/httparty.gemspec +7 -4
- data/lib/httparty/connection_adapter.rb +73 -16
- data/lib/httparty/cookie_hash.rb +10 -8
- data/lib/httparty/decompressor.rb +92 -0
- data/lib/httparty/exceptions.rb +4 -1
- data/lib/httparty/hash_conversions.rb +30 -12
- data/lib/httparty/headers_processor.rb +32 -0
- data/lib/httparty/logger/apache_formatter.rb +31 -6
- data/lib/httparty/logger/curl_formatter.rb +9 -7
- data/lib/httparty/logger/logger.rb +5 -1
- data/lib/httparty/logger/logstash_formatter.rb +61 -0
- data/lib/httparty/module_inheritable_attributes.rb +6 -4
- data/lib/httparty/net_digest_auth.rb +19 -19
- data/lib/httparty/parser.rb +25 -14
- data/lib/httparty/request/body.rb +98 -0
- data/lib/httparty/request/multipart_boundary.rb +13 -0
- data/lib/httparty/request.rb +137 -110
- data/lib/httparty/response/headers.rb +23 -19
- data/lib/httparty/response.rb +81 -22
- data/lib/httparty/response_fragment.rb +21 -0
- data/lib/httparty/text_encoder.rb +72 -0
- data/lib/httparty/utils.rb +13 -0
- data/lib/httparty/version.rb +3 -1
- data/lib/httparty.rb +79 -30
- data/website/css/common.css +1 -1
- metadata +37 -103
- data/.travis.yml +0 -9
- data/features/basic_authentication.feature +0 -20
- data/features/command_line.feature +0 -95
- data/features/deals_with_http_error_codes.feature +0 -26
- data/features/digest_authentication.feature +0 -30
- data/features/handles_compressed_responses.feature +0 -27
- data/features/handles_multiple_formats.feature +0 -57
- data/features/steps/env.rb +0 -27
- data/features/steps/httparty_response_steps.rb +0 -56
- data/features/steps/httparty_steps.rb +0 -43
- data/features/steps/mongrel_helper.rb +0 -127
- data/features/steps/remote_service_steps.rb +0 -92
- data/features/supports_read_timeout_option.feature +0 -13
- data/features/supports_redirection.feature +0 -22
- data/features/supports_timeout_option.feature +0 -13
- data/spec/fixtures/delicious.xml +0 -23
- data/spec/fixtures/empty.xml +0 -0
- data/spec/fixtures/google.html +0 -3
- data/spec/fixtures/ssl/generate.sh +0 -29
- data/spec/fixtures/ssl/generated/1fe462c2.0 +0 -16
- data/spec/fixtures/ssl/generated/bogushost.crt +0 -13
- data/spec/fixtures/ssl/generated/ca.crt +0 -16
- data/spec/fixtures/ssl/generated/ca.key +0 -15
- data/spec/fixtures/ssl/generated/selfsigned.crt +0 -14
- data/spec/fixtures/ssl/generated/server.crt +0 -13
- data/spec/fixtures/ssl/generated/server.key +0 -15
- data/spec/fixtures/ssl/openssl-exts.cnf +0 -9
- data/spec/fixtures/twitter.csv +0 -2
- data/spec/fixtures/twitter.json +0 -1
- data/spec/fixtures/twitter.xml +0 -403
- data/spec/fixtures/undefined_method_add_node_for_nil.xml +0 -2
- data/spec/httparty/connection_adapter_spec.rb +0 -495
- data/spec/httparty/cookie_hash_spec.rb +0 -100
- data/spec/httparty/exception_spec.rb +0 -45
- data/spec/httparty/hash_conversions_spec.rb +0 -49
- data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
- data/spec/httparty/logger/curl_formatter_spec.rb +0 -119
- data/spec/httparty/logger/logger_spec.rb +0 -38
- data/spec/httparty/net_digest_auth_spec.rb +0 -240
- data/spec/httparty/parser_spec.rb +0 -173
- data/spec/httparty/request_spec.rb +0 -1183
- data/spec/httparty/response_spec.rb +0 -291
- data/spec/httparty/ssl_spec.rb +0 -74
- data/spec/httparty_spec.rb +0 -872
- data/spec/spec_helper.rb +0 -59
- data/spec/support/ssl_test_helper.rb +0 -47
- data/spec/support/ssl_test_server.rb +0 -80
- data/spec/support/stub_response.rb +0 -49
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'digest/md5'
|
2
4
|
require 'net/http'
|
3
5
|
|
@@ -12,13 +14,13 @@ module Net
|
|
12
14
|
response
|
13
15
|
)
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
-
|
17
|
+
authenticator.authorization_header.each do |v|
|
18
|
+
add_field('Authorization', v)
|
19
|
+
end
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
authenticator.cookie_header.each do |v|
|
22
|
+
add_field('Cookie', v)
|
23
|
+
end
|
22
24
|
end
|
23
25
|
|
24
26
|
class DigestAuthenticator
|
@@ -44,12 +46,9 @@ module Net
|
|
44
46
|
header << %(algorithm="#{@response['algorithm']}") if algorithm_present?
|
45
47
|
|
46
48
|
if qop_present?
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
"nc=00000001"
|
51
|
-
]
|
52
|
-
fields.each { |field| header << field }
|
49
|
+
header << %(cnonce="#{@cnonce}")
|
50
|
+
header << %(qop="#{@response['qop']}")
|
51
|
+
header << 'nc=00000001'
|
53
52
|
end
|
54
53
|
|
55
54
|
header << %(opaque="#{@response['opaque']}") if opaque_present?
|
@@ -64,7 +63,8 @@ module Net
|
|
64
63
|
|
65
64
|
def parse(response_header)
|
66
65
|
header = response_header['www-authenticate']
|
67
|
-
|
66
|
+
|
67
|
+
header = header.gsub(/qop=(auth(?:-int)?)/, 'qop="\\1"')
|
68
68
|
|
69
69
|
header =~ /Digest (.*)/
|
70
70
|
params = {}
|
@@ -97,13 +97,13 @@ module Net
|
|
97
97
|
end
|
98
98
|
|
99
99
|
def random
|
100
|
-
format
|
100
|
+
format '%x', (Time.now.to_i + rand(65535))
|
101
101
|
end
|
102
102
|
|
103
103
|
def request_digest
|
104
104
|
a = [md5(a1), @response['nonce'], md5(a2)]
|
105
|
-
a.insert(2,
|
106
|
-
md5(a.join(
|
105
|
+
a.insert(2, '00000001', @cnonce, @response['qop']) if qop_present?
|
106
|
+
md5(a.join(':'))
|
107
107
|
end
|
108
108
|
|
109
109
|
def md5(str)
|
@@ -113,11 +113,11 @@ module Net
|
|
113
113
|
def algorithm_present?
|
114
114
|
@response.key?('algorithm') && !@response['algorithm'].empty?
|
115
115
|
end
|
116
|
-
|
116
|
+
|
117
117
|
def use_md5_sess?
|
118
118
|
algorithm_present? && @response['algorithm'] == 'MD5-sess'
|
119
119
|
end
|
120
|
-
|
120
|
+
|
121
121
|
def a1
|
122
122
|
a1_user_realm_pwd = [@username, @response['realm'], @password].join(':')
|
123
123
|
if use_md5_sess?
|
@@ -128,7 +128,7 @@ module Net
|
|
128
128
|
end
|
129
129
|
|
130
130
|
def a2
|
131
|
-
[@method, @path].join(
|
131
|
+
[@method, @path].join(':')
|
132
132
|
end
|
133
133
|
end
|
134
134
|
end
|
data/lib/httparty/parser.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTParty
|
2
4
|
# The default parser used by HTTParty, supports xml, json, html, csv and
|
3
5
|
# plain text.
|
@@ -38,16 +40,18 @@ module HTTParty
|
|
38
40
|
# @abstract Read the Custom Parsers section for more information.
|
39
41
|
class Parser
|
40
42
|
SupportedFormats = {
|
41
|
-
'text/xml'
|
42
|
-
'application/xml'
|
43
|
-
'application/json'
|
44
|
-
'
|
45
|
-
'application/
|
46
|
-
'text/
|
47
|
-
'
|
48
|
-
'text/
|
49
|
-
'text/
|
50
|
-
'
|
43
|
+
'text/xml' => :xml,
|
44
|
+
'application/xml' => :xml,
|
45
|
+
'application/json' => :json,
|
46
|
+
'application/vnd.api+json' => :json,
|
47
|
+
'application/hal+json' => :json,
|
48
|
+
'text/json' => :json,
|
49
|
+
'application/javascript' => :plain,
|
50
|
+
'text/javascript' => :plain,
|
51
|
+
'text/html' => :html,
|
52
|
+
'text/plain' => :plain,
|
53
|
+
'text/csv' => :csv,
|
54
|
+
'application/csv' => :csv,
|
51
55
|
'text/comma-separated-values' => :csv
|
52
56
|
}
|
53
57
|
|
@@ -99,8 +103,11 @@ module HTTParty
|
|
99
103
|
# @return [nil] when the response body is nil, an empty string, spaces only or "null"
|
100
104
|
def parse
|
101
105
|
return nil if body.nil?
|
102
|
-
return nil if body ==
|
106
|
+
return nil if body == 'null'
|
103
107
|
return nil if body.valid_encoding? && body.strip.empty?
|
108
|
+
if body.valid_encoding? && body.encoding == Encoding::UTF_8
|
109
|
+
@body = body.gsub(/\A#{UTF8_BOM}/, '')
|
110
|
+
end
|
104
111
|
if supports_format?
|
105
112
|
parse_supported_format
|
106
113
|
else
|
@@ -114,6 +121,8 @@ module HTTParty
|
|
114
121
|
MultiXml.parse(body)
|
115
122
|
end
|
116
123
|
|
124
|
+
UTF8_BOM = "\xEF\xBB\xBF"
|
125
|
+
|
117
126
|
def json
|
118
127
|
JSON.parse(body, :quirks_mode => true, :allow_nan => true)
|
119
128
|
end
|
@@ -135,9 +144,11 @@ module HTTParty
|
|
135
144
|
end
|
136
145
|
|
137
146
|
def parse_supported_format
|
138
|
-
|
139
|
-
|
140
|
-
|
147
|
+
if respond_to?(format, true)
|
148
|
+
send(format)
|
149
|
+
else
|
150
|
+
raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format."
|
151
|
+
end
|
141
152
|
end
|
142
153
|
end
|
143
154
|
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'multipart_boundary'
|
4
|
+
|
5
|
+
module HTTParty
|
6
|
+
class Request
|
7
|
+
class Body
|
8
|
+
NEWLINE = "\r\n"
|
9
|
+
private_constant :NEWLINE
|
10
|
+
|
11
|
+
def initialize(params, query_string_normalizer: nil, force_multipart: false)
|
12
|
+
@params = params
|
13
|
+
@query_string_normalizer = query_string_normalizer
|
14
|
+
@force_multipart = force_multipart
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
if params.respond_to?(:to_hash)
|
19
|
+
multipart? ? generate_multipart : normalize_query(params)
|
20
|
+
else
|
21
|
+
params
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def boundary
|
26
|
+
@boundary ||= MultipartBoundary.generate
|
27
|
+
end
|
28
|
+
|
29
|
+
def multipart?
|
30
|
+
params.respond_to?(:to_hash) && (force_multipart || has_file?(params))
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def generate_multipart
|
36
|
+
normalized_params = params.flat_map { |key, value| HashConversions.normalize_keys(key, value) }
|
37
|
+
|
38
|
+
multipart = normalized_params.inject(''.dup) do |memo, (key, value)|
|
39
|
+
memo << "--#{boundary}#{NEWLINE}"
|
40
|
+
memo << %(Content-Disposition: form-data; name="#{key}")
|
41
|
+
# value.path is used to support ActionDispatch::Http::UploadedFile
|
42
|
+
# https://github.com/jnunemaker/httparty/pull/585
|
43
|
+
memo << %(; filename="#{file_name(value)}") if file?(value)
|
44
|
+
memo << NEWLINE
|
45
|
+
memo << "Content-Type: #{content_type(value)}#{NEWLINE}" if file?(value)
|
46
|
+
memo << NEWLINE
|
47
|
+
memo << content_body(value)
|
48
|
+
memo << NEWLINE
|
49
|
+
end
|
50
|
+
|
51
|
+
multipart << "--#{boundary}--#{NEWLINE}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_file?(value)
|
55
|
+
if value.respond_to?(:to_hash)
|
56
|
+
value.to_hash.any? { |_, v| has_file?(v) }
|
57
|
+
elsif value.respond_to?(:to_ary)
|
58
|
+
value.to_ary.any? { |v| has_file?(v) }
|
59
|
+
else
|
60
|
+
file?(value)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def file?(object)
|
65
|
+
object.respond_to?(:path) && object.respond_to?(:read)
|
66
|
+
end
|
67
|
+
|
68
|
+
def normalize_query(query)
|
69
|
+
if query_string_normalizer
|
70
|
+
query_string_normalizer.call(query)
|
71
|
+
else
|
72
|
+
HashConversions.to_params(query)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def content_body(object)
|
77
|
+
if file?(object)
|
78
|
+
object = (file = object).read
|
79
|
+
file.rewind if file.respond_to?(:rewind)
|
80
|
+
end
|
81
|
+
|
82
|
+
object.to_s
|
83
|
+
end
|
84
|
+
|
85
|
+
def content_type(object)
|
86
|
+
return object.content_type if object.respond_to?(:content_type)
|
87
|
+
mime = MIME::Types.type_for(object.path)
|
88
|
+
mime.empty? ? 'application/octet-stream' : mime[0].content_type
|
89
|
+
end
|
90
|
+
|
91
|
+
def file_name(object)
|
92
|
+
object.respond_to?(:original_filename) ? object.original_filename : File.basename(object.path)
|
93
|
+
end
|
94
|
+
|
95
|
+
attr_reader :params, :query_string_normalizer, :force_multipart
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/lib/httparty/request.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
|
1
5
|
module HTTParty
|
2
6
|
class Request #:nodoc:
|
3
7
|
SupportedHTTPMethods = [
|
@@ -11,6 +15,8 @@ module HTTParty
|
|
11
15
|
Net::HTTP::Move,
|
12
16
|
Net::HTTP::Copy,
|
13
17
|
Net::HTTP::Mkcol,
|
18
|
+
Net::HTTP::Lock,
|
19
|
+
Net::HTTP::Unlock,
|
14
20
|
]
|
15
21
|
|
16
22
|
SupportedURISchemes = ['http', 'https', 'webcal', nil]
|
@@ -27,10 +33,31 @@ module HTTParty
|
|
27
33
|
end.flatten.join('&')
|
28
34
|
end
|
29
35
|
|
36
|
+
JSON_API_QUERY_STRING_NORMALIZER = proc do |query|
|
37
|
+
Array(query).sort_by { |a| a[0].to_s }.map do |key, value|
|
38
|
+
if value.nil?
|
39
|
+
key.to_s
|
40
|
+
elsif value.respond_to?(:to_ary)
|
41
|
+
values = value.to_ary.map{|v| ERB::Util.url_encode(v.to_s)}
|
42
|
+
"#{key}=#{values.join(',')}"
|
43
|
+
else
|
44
|
+
HashConversions.to_params(key => value)
|
45
|
+
end
|
46
|
+
end.flatten.join('&')
|
47
|
+
end
|
48
|
+
|
49
|
+
def self._load(data)
|
50
|
+
http_method, path, options = Marshal.load(data)
|
51
|
+
new(http_method, path, options)
|
52
|
+
end
|
53
|
+
|
30
54
|
attr_accessor :http_method, :options, :last_response, :redirect, :last_uri
|
31
55
|
attr_reader :path
|
32
56
|
|
33
57
|
def initialize(http_method, path, o = {})
|
58
|
+
@changed_hosts = false
|
59
|
+
@credentials_sent = false
|
60
|
+
|
34
61
|
self.http_method = http_method
|
35
62
|
self.options = {
|
36
63
|
limit: o.delete(:no_follow) ? 1 : 5,
|
@@ -51,7 +78,7 @@ module HTTParty
|
|
51
78
|
@path = if uri.is_a?(uri_adapter)
|
52
79
|
uri
|
53
80
|
elsif String.try_convert(uri)
|
54
|
-
uri_adapter.parse
|
81
|
+
uri_adapter.parse(uri).normalize
|
55
82
|
else
|
56
83
|
raise ArgumentError,
|
57
84
|
"bad argument (expected #{uri_adapter} object or URI string)"
|
@@ -67,14 +94,20 @@ module HTTParty
|
|
67
94
|
end
|
68
95
|
|
69
96
|
def uri
|
70
|
-
if redirect && path.relative? && path.path[0] !=
|
71
|
-
last_uri_host = @last_uri.path.gsub(/[^\/]+$/,
|
97
|
+
if redirect && path.relative? && path.path[0] != '/'
|
98
|
+
last_uri_host = @last_uri.path.gsub(/[^\/]+$/, '')
|
72
99
|
|
73
|
-
path.path = "/#{path.path}" if last_uri_host[-1] !=
|
74
|
-
path.path = last_uri_host
|
100
|
+
path.path = "/#{path.path}" if last_uri_host[-1] != '/'
|
101
|
+
path.path = "#{last_uri_host}#{path.path}"
|
75
102
|
end
|
76
103
|
|
77
|
-
|
104
|
+
if path.relative? && path.host
|
105
|
+
new_uri = options[:uri_adapter].parse("#{@last_uri.scheme}:#{path}").normalize
|
106
|
+
elsif path.relative?
|
107
|
+
new_uri = options[:uri_adapter].parse("#{base_uri}#{path}").normalize
|
108
|
+
else
|
109
|
+
new_uri = path.clone
|
110
|
+
end
|
78
111
|
|
79
112
|
# avoid double query string on redirects [#12]
|
80
113
|
unless redirect
|
@@ -91,7 +124,7 @@ module HTTParty
|
|
91
124
|
def base_uri
|
92
125
|
if redirect
|
93
126
|
base_uri = "#{@last_uri.scheme}://#{@last_uri.host}"
|
94
|
-
base_uri
|
127
|
+
base_uri = "#{base_uri}:#{@last_uri.port}" if @last_uri.port != 80
|
95
128
|
base_uri
|
96
129
|
else
|
97
130
|
options[:base_uri] && HTTParty.normalize_base_uri(options[:base_uri])
|
@@ -114,39 +147,52 @@ module HTTParty
|
|
114
147
|
validate
|
115
148
|
setup_raw_request
|
116
149
|
chunked_body = nil
|
150
|
+
current_http = http
|
117
151
|
|
118
|
-
self.last_response =
|
152
|
+
self.last_response = current_http.request(@raw_request) do |http_response|
|
119
153
|
if block
|
120
154
|
chunks = []
|
121
155
|
|
122
156
|
http_response.read_body do |fragment|
|
123
|
-
|
124
|
-
|
157
|
+
encoded_fragment = encode_text(fragment, http_response['content-type'])
|
158
|
+
chunks << encoded_fragment if !options[:stream_body]
|
159
|
+
block.call ResponseFragment.new(encoded_fragment, http_response, current_http)
|
125
160
|
end
|
126
161
|
|
127
162
|
chunked_body = chunks.join
|
128
163
|
end
|
129
164
|
end
|
130
165
|
|
131
|
-
handle_deflation unless http_method == Net::HTTP::Head
|
132
166
|
handle_host_redirection if response_redirects?
|
133
|
-
|
167
|
+
result = handle_unauthorized
|
168
|
+
result ||= handle_response(chunked_body, &block)
|
169
|
+
result
|
170
|
+
end
|
171
|
+
|
172
|
+
def handle_unauthorized(&block)
|
173
|
+
return unless digest_auth? && response_unauthorized? && response_has_digest_auth_challenge?
|
174
|
+
return if @credentials_sent
|
175
|
+
@credentials_sent = true
|
176
|
+
perform(&block)
|
134
177
|
end
|
135
178
|
|
136
179
|
def raw_body
|
137
180
|
@raw_request.body
|
138
181
|
end
|
139
182
|
|
183
|
+
def _dump(_level)
|
184
|
+
opts = options.dup
|
185
|
+
opts.delete(:logger)
|
186
|
+
opts.delete(:parser) if opts[:parser] && opts[:parser].is_a?(Proc)
|
187
|
+
Marshal.dump([http_method, path, opts])
|
188
|
+
end
|
189
|
+
|
140
190
|
private
|
141
191
|
|
142
192
|
def http
|
143
193
|
connection_adapter.call(uri, options)
|
144
194
|
end
|
145
195
|
|
146
|
-
def body
|
147
|
-
options[:body].respond_to?(:to_hash) ? normalize_query(options[:body]) : options[:body]
|
148
|
-
end
|
149
|
-
|
150
196
|
def credentials
|
151
197
|
(options[:basic_auth] || options[:digest_auth]).to_hash
|
152
198
|
end
|
@@ -172,106 +218,78 @@ module HTTParty
|
|
172
218
|
end
|
173
219
|
|
174
220
|
def setup_raw_request
|
175
|
-
|
176
|
-
|
177
|
-
@raw_request.body_stream = options[:body_stream] if options[:body_stream]
|
178
|
-
@raw_request.initialize_http_header(options[:headers].to_hash) if options[:headers].respond_to?(:to_hash)
|
179
|
-
@raw_request.basic_auth(username, password) if options[:basic_auth] && send_authorization_header?
|
180
|
-
setup_digest_auth if options[:digest_auth]
|
181
|
-
end
|
182
|
-
|
183
|
-
def setup_digest_auth
|
184
|
-
auth_request = http_method.new(uri.request_uri)
|
185
|
-
auth_request.initialize_http_header(options[:headers].to_hash) if options[:headers].respond_to?(:to_hash)
|
186
|
-
res = http.request(auth_request)
|
187
|
-
|
188
|
-
if !res['www-authenticate'].nil? && res['www-authenticate'].length > 0
|
189
|
-
@raw_request.digest_auth(username, password, res)
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
def query_string(uri)
|
194
|
-
query_string_parts = []
|
195
|
-
query_string_parts << uri.query unless uri.query.nil?
|
196
|
-
|
197
|
-
if options[:query].respond_to?(:to_hash)
|
198
|
-
query_string_parts << normalize_query(options[:default_params].merge(options[:query].to_hash))
|
221
|
+
if options[:headers].respond_to?(:to_hash)
|
222
|
+
headers_hash = options[:headers].to_hash
|
199
223
|
else
|
200
|
-
|
201
|
-
query_string_parts << options[:query] unless options[:query].nil?
|
224
|
+
headers_hash = nil
|
202
225
|
end
|
203
226
|
|
204
|
-
|
205
|
-
|
206
|
-
end
|
227
|
+
@raw_request = http_method.new(request_uri(uri), headers_hash)
|
228
|
+
@raw_request.body_stream = options[:body_stream] if options[:body_stream]
|
207
229
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
230
|
+
if options[:body]
|
231
|
+
body = Body.new(
|
232
|
+
options[:body],
|
233
|
+
query_string_normalizer: query_string_normalizer,
|
234
|
+
force_multipart: options[:multipart]
|
235
|
+
)
|
213
236
|
|
214
|
-
|
215
|
-
|
237
|
+
if body.multipart?
|
238
|
+
content_type = "multipart/form-data; boundary=#{body.boundary}"
|
239
|
+
@raw_request['Content-Type'] = content_type
|
240
|
+
end
|
241
|
+
@raw_request.body = body.call
|
216
242
|
end
|
217
243
|
|
218
|
-
|
219
|
-
return $1.gsub(/\\(.)/, '\1')
|
220
|
-
end
|
244
|
+
@raw_request.instance_variable_set(:@decode_content, decompress_content?)
|
221
245
|
|
222
|
-
|
246
|
+
if options[:basic_auth] && send_authorization_header?
|
247
|
+
@raw_request.basic_auth(username, password)
|
248
|
+
@credentials_sent = true
|
249
|
+
end
|
250
|
+
setup_digest_auth if digest_auth? && response_unauthorized? && response_has_digest_auth_challenge?
|
223
251
|
end
|
224
252
|
|
225
|
-
def
|
226
|
-
|
227
|
-
body.force_encoding(encoding)
|
228
|
-
rescue
|
229
|
-
body
|
253
|
+
def digest_auth?
|
254
|
+
!!options[:digest_auth]
|
230
255
|
end
|
231
256
|
|
232
|
-
def
|
233
|
-
options[:
|
257
|
+
def decompress_content?
|
258
|
+
!options[:skip_decompression]
|
234
259
|
end
|
235
260
|
|
236
|
-
def
|
237
|
-
|
238
|
-
|
239
|
-
return body.force_encoding("UTF-16LE")
|
240
|
-
elsif body.getbyte(0) == 0xFE && body.getbyte(1) == 0xFF
|
241
|
-
return body.force_encoding("UTF-16BE")
|
242
|
-
end
|
243
|
-
end
|
261
|
+
def response_unauthorized?
|
262
|
+
!!last_response && last_response.code == '401'
|
263
|
+
end
|
244
264
|
|
245
|
-
|
246
|
-
|
247
|
-
else
|
248
|
-
body.force_encoding("UTF-16LE")
|
249
|
-
end
|
265
|
+
def response_has_digest_auth_challenge?
|
266
|
+
!last_response['www-authenticate'].nil? && last_response['www-authenticate'].length > 0
|
250
267
|
end
|
251
268
|
|
252
|
-
def
|
253
|
-
|
269
|
+
def setup_digest_auth
|
270
|
+
@raw_request.digest_auth(username, password, last_response)
|
271
|
+
end
|
254
272
|
|
255
|
-
|
256
|
-
|
257
|
-
|
273
|
+
def query_string(uri)
|
274
|
+
query_string_parts = []
|
275
|
+
query_string_parts << uri.query unless uri.query.nil?
|
258
276
|
|
259
|
-
if
|
260
|
-
|
277
|
+
if options[:query].respond_to?(:to_hash)
|
278
|
+
query_string_parts << normalize_query(options[:default_params].merge(options[:query].to_hash))
|
261
279
|
else
|
262
|
-
|
280
|
+
query_string_parts << normalize_query(options[:default_params]) unless options[:default_params].empty?
|
281
|
+
query_string_parts << options[:query] unless options[:query].nil?
|
263
282
|
end
|
283
|
+
|
284
|
+
query_string_parts.reject!(&:empty?) unless query_string_parts == ['']
|
285
|
+
query_string_parts.size > 0 ? query_string_parts.join('&') : nil
|
264
286
|
end
|
265
287
|
|
266
|
-
def
|
267
|
-
|
268
|
-
_encode_body(body)
|
269
|
-
else
|
270
|
-
body
|
271
|
-
end
|
288
|
+
def assume_utf16_is_big_endian
|
289
|
+
options[:assume_utf16_is_big_endian]
|
272
290
|
end
|
273
291
|
|
274
|
-
def handle_response(
|
292
|
+
def handle_response(raw_body, &block)
|
275
293
|
if response_redirects?
|
276
294
|
options[:limit] -= 1
|
277
295
|
if options[:logger]
|
@@ -292,31 +310,26 @@ module HTTParty
|
|
292
310
|
capture_cookies(last_response)
|
293
311
|
perform(&block)
|
294
312
|
else
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
313
|
+
raw_body ||= last_response.body
|
314
|
+
|
315
|
+
body = decompress(raw_body, last_response['content-encoding']) unless raw_body.nil?
|
316
|
+
|
317
|
+
unless body.nil?
|
318
|
+
body = encode_text(body, last_response['content-type'])
|
300
319
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
320
|
+
if decompress_content?
|
321
|
+
last_response.delete('content-encoding')
|
322
|
+
raw_body = body
|
323
|
+
end
|
324
|
+
end
|
305
325
|
|
306
|
-
|
307
|
-
when "gzip", "x-gzip"
|
308
|
-
body_io = StringIO.new(last_response.body)
|
309
|
-
last_response.body.replace Zlib::GzipReader.new(body_io).read
|
310
|
-
last_response.delete('content-encoding')
|
311
|
-
when "deflate"
|
312
|
-
last_response.body.replace Zlib::Inflate.inflate(last_response.body)
|
313
|
-
last_response.delete('content-encoding')
|
326
|
+
Response.new(self, last_response, lambda { parse_response(body) }, body: raw_body)
|
314
327
|
end
|
315
328
|
end
|
316
329
|
|
317
330
|
def handle_host_redirection
|
318
331
|
check_duplicate_location_header
|
319
|
-
redirect_path = options[:uri_adapter].parse
|
332
|
+
redirect_path = options[:uri_adapter].parse(last_response['location']).normalize
|
320
333
|
return if redirect_path.relative? || path.host == redirect_path.host
|
321
334
|
@changed_hosts = true
|
322
335
|
end
|
@@ -329,7 +342,7 @@ module HTTParty
|
|
329
342
|
end
|
330
343
|
|
331
344
|
def send_authorization_header?
|
332
|
-
|
345
|
+
!@changed_hosts
|
333
346
|
end
|
334
347
|
|
335
348
|
def response_redirects?
|
@@ -350,6 +363,7 @@ module HTTParty
|
|
350
363
|
cookies_hash = HTTParty::CookieHash.new
|
351
364
|
cookies_hash.add_cookies(options[:headers].to_hash['Cookie']) if options[:headers] && options[:headers].to_hash['Cookie']
|
352
365
|
response.get_fields('Set-Cookie').each { |cookie| cookies_hash.add_cookies(cookie) }
|
366
|
+
|
353
367
|
options[:headers] ||= {}
|
354
368
|
options[:headers]['Cookie'] = cookies_hash.to_cookie_string
|
355
369
|
end
|
@@ -381,7 +395,20 @@ module HTTParty
|
|
381
395
|
if path.userinfo
|
382
396
|
username, password = path.userinfo.split(':')
|
383
397
|
options[:basic_auth] = {username: username, password: password}
|
398
|
+
@credentials_sent = true
|
384
399
|
end
|
385
400
|
end
|
401
|
+
|
402
|
+
def decompress(body, encoding)
|
403
|
+
Decompressor.new(body, encoding).decompress
|
404
|
+
end
|
405
|
+
|
406
|
+
def encode_text(text, content_type)
|
407
|
+
TextEncoder.new(
|
408
|
+
text,
|
409
|
+
content_type: content_type,
|
410
|
+
assume_utf16_is_big_endian: assume_utf16_is_big_endian
|
411
|
+
).call
|
412
|
+
end
|
386
413
|
end
|
387
414
|
end
|