httparty 0.13.6 → 0.21.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.editorconfig +18 -0
- data/.github/workflows/ci.yml +26 -0
- data/.gitignore +3 -0
- data/.rubocop_todo.yml +1 -1
- data/{History → Changelog.md} +228 -58
- data/Gemfile +10 -3
- data/Guardfile +3 -2
- data/README.md +8 -7
- data/bin/httparty +3 -1
- data/docs/README.md +191 -0
- 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 +17 -6
- data/examples/twitter.rb +2 -2
- data/examples/whoismyrep.rb +1 -1
- data/httparty.gemspec +8 -6
- data/lib/httparty/connection_adapter.rb +86 -20
- data/lib/httparty/cookie_hash.rb +10 -8
- data/lib/httparty/decompressor.rb +102 -0
- data/lib/httparty/exceptions.rb +8 -2
- data/lib/httparty/hash_conversions.rb +30 -8
- data/lib/httparty/headers_processor.rb +32 -0
- data/lib/httparty/logger/apache_formatter.rb +31 -6
- data/lib/httparty/logger/curl_formatter.rb +68 -23
- 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 +23 -21
- data/lib/httparty/parser.rb +25 -14
- data/lib/httparty/request/body.rb +105 -0
- data/lib/httparty/request/multipart_boundary.rb +13 -0
- data/lib/httparty/request.rb +160 -106
- data/lib/httparty/response/headers.rb +23 -19
- data/lib/httparty/response.rb +92 -13
- 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 +98 -35
- data/website/css/common.css +1 -1
- metadata +35 -115
- data/.simplecov +0 -1
- data/.travis.yml +0 -7
- 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 -52
- data/features/steps/httparty_steps.rb +0 -43
- data/features/steps/mongrel_helper.rb +0 -127
- data/features/steps/remote_service_steps.rb +0 -90
- 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 -468
- data/spec/httparty/cookie_hash_spec.rb +0 -83
- data/spec/httparty/exception_spec.rb +0 -38
- data/spec/httparty/hash_conversions_spec.rb +0 -41
- data/spec/httparty/logger/apache_formatter_spec.rb +0 -41
- data/spec/httparty/logger/curl_formatter_spec.rb +0 -18
- data/spec/httparty/logger/logger_spec.rb +0 -38
- data/spec/httparty/net_digest_auth_spec.rb +0 -230
- data/spec/httparty/parser_spec.rb +0 -173
- data/spec/httparty/request_spec.rb +0 -1073
- data/spec/httparty/response_spec.rb +0 -241
- data/spec/httparty/ssl_spec.rb +0 -74
- data/spec/httparty_spec.rb +0 -850
- 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,47 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTParty
|
2
4
|
module Logger
|
3
5
|
class CurlFormatter #:nodoc:
|
4
6
|
TAG_NAME = HTTParty.name
|
5
|
-
OUT
|
6
|
-
IN
|
7
|
+
OUT = '>'
|
8
|
+
IN = '<'
|
7
9
|
|
8
|
-
attr_accessor :level, :logger
|
10
|
+
attr_accessor :level, :logger
|
9
11
|
|
10
12
|
def initialize(logger, level)
|
11
|
-
@logger
|
12
|
-
@level
|
13
|
+
@logger = logger
|
14
|
+
@level = level.to_sym
|
15
|
+
@messages = []
|
13
16
|
end
|
14
17
|
|
15
18
|
def format(request, response)
|
16
|
-
|
17
|
-
|
18
|
-
http_method = request.http_method.name.split("::").last.upcase
|
19
|
-
path = request.path.to_s
|
19
|
+
@request = request
|
20
|
+
@response = response
|
20
21
|
|
21
|
-
|
22
|
+
log_request
|
23
|
+
log_response
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
logger.public_send level, messages.join("\n")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :request, :response
|
31
|
+
attr_accessor :messages
|
28
32
|
|
29
|
-
|
30
|
-
|
31
|
-
|
33
|
+
def log_request
|
34
|
+
log_url
|
35
|
+
log_headers
|
36
|
+
log_query
|
37
|
+
log OUT, request.raw_body if request.raw_body
|
38
|
+
log OUT
|
39
|
+
end
|
40
|
+
|
41
|
+
def log_response
|
42
|
+
log IN, "HTTP/#{response.http_version} #{response.code}"
|
43
|
+
log_response_headers
|
44
|
+
log IN, "\n#{response.body}"
|
45
|
+
log IN
|
46
|
+
end
|
32
47
|
|
48
|
+
def log_url
|
49
|
+
http_method = request.http_method.name.split('::').last.upcase
|
50
|
+
uri = if request.options[:base_uri]
|
51
|
+
request.options[:base_uri] + request.path.path
|
52
|
+
else
|
53
|
+
request.path.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
log OUT, "#{http_method} #{uri}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def log_headers
|
60
|
+
return unless request.options[:headers] && request.options[:headers].size > 0
|
61
|
+
|
62
|
+
log OUT, 'Headers: '
|
63
|
+
log_hash request.options[:headers]
|
64
|
+
end
|
65
|
+
|
66
|
+
def log_query
|
67
|
+
return unless request.options[:query]
|
68
|
+
|
69
|
+
log OUT, 'Query: '
|
70
|
+
log_hash request.options[:query]
|
71
|
+
end
|
72
|
+
|
73
|
+
def log_response_headers
|
33
74
|
headers = response.respond_to?(:headers) ? response.headers : response
|
34
75
|
response.each_header do |response_header|
|
35
|
-
|
76
|
+
log IN, "#{response_header.capitalize}: #{headers[response_header]}"
|
36
77
|
end
|
78
|
+
end
|
37
79
|
|
38
|
-
|
80
|
+
def log_hash(hash)
|
81
|
+
hash.each { |k, v| log(OUT, "#{k}: #{v}") }
|
82
|
+
end
|
39
83
|
|
40
|
-
|
84
|
+
def log(direction, line = '')
|
85
|
+
messages << "[#{TAG_NAME}] [#{current_time}] #{direction} #{line}"
|
41
86
|
end
|
42
87
|
|
43
|
-
def
|
44
|
-
"
|
88
|
+
def current_time
|
89
|
+
Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
|
45
90
|
end
|
46
91
|
end
|
47
92
|
end
|
@@ -1,12 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'httparty/logger/apache_formatter'
|
2
4
|
require 'httparty/logger/curl_formatter'
|
5
|
+
require 'httparty/logger/logstash_formatter'
|
3
6
|
|
4
7
|
module HTTParty
|
5
8
|
module Logger
|
6
9
|
def self.formatters
|
7
10
|
@formatters ||= {
|
8
11
|
:curl => Logger::CurlFormatter,
|
9
|
-
:apache => Logger::ApacheFormatter
|
12
|
+
:apache => Logger::ApacheFormatter,
|
13
|
+
:logstash => Logger::LogstashFormatter,
|
10
14
|
}
|
11
15
|
end
|
12
16
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTParty
|
4
|
+
module Logger
|
5
|
+
class LogstashFormatter #:nodoc:
|
6
|
+
TAG_NAME = HTTParty.name
|
7
|
+
|
8
|
+
attr_accessor :level, :logger
|
9
|
+
|
10
|
+
def initialize(logger, level)
|
11
|
+
@logger = logger
|
12
|
+
@level = level.to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
def format(request, response)
|
16
|
+
@request = request
|
17
|
+
@response = response
|
18
|
+
|
19
|
+
logger.public_send level, logstash_message
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :request, :response
|
25
|
+
|
26
|
+
def logstash_message
|
27
|
+
{
|
28
|
+
'@timestamp' => current_time,
|
29
|
+
'@version' => 1,
|
30
|
+
'content_length' => content_length || '-',
|
31
|
+
'http_method' => http_method,
|
32
|
+
'message' => message,
|
33
|
+
'path' => path,
|
34
|
+
'response_code' => response.code,
|
35
|
+
'severity' => level,
|
36
|
+
'tags' => [TAG_NAME],
|
37
|
+
}.to_json
|
38
|
+
end
|
39
|
+
|
40
|
+
def message
|
41
|
+
"[#{TAG_NAME}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
|
42
|
+
end
|
43
|
+
|
44
|
+
def current_time
|
45
|
+
Time.now.strftime('%Y-%m-%d %H:%M:%S %z')
|
46
|
+
end
|
47
|
+
|
48
|
+
def http_method
|
49
|
+
@http_method ||= request.http_method.name.split('::').last.upcase
|
50
|
+
end
|
51
|
+
|
52
|
+
def path
|
53
|
+
@path ||= request.path.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
def content_length
|
57
|
+
@content_length ||= response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module HTTParty
|
2
4
|
module ModuleInheritableAttributes #:nodoc:
|
3
5
|
def self.included(base)
|
@@ -9,12 +11,12 @@ module HTTParty
|
|
9
11
|
duplicate = hash.dup
|
10
12
|
|
11
13
|
duplicate.each_pair do |key, value|
|
12
|
-
|
13
|
-
|
14
|
+
if value.is_a?(Hash)
|
15
|
+
duplicate[key] = hash_deep_dup(value)
|
14
16
|
elsif value.is_a?(Proc)
|
15
17
|
duplicate[key] = value.dup
|
16
18
|
else
|
17
|
-
value
|
19
|
+
duplicate[key] = value
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
@@ -36,7 +38,7 @@ module HTTParty
|
|
36
38
|
def inherited(subclass)
|
37
39
|
super
|
38
40
|
@mattr_inheritable_attrs.each do |inheritable_attribute|
|
39
|
-
ivar = "@#{inheritable_attribute}"
|
41
|
+
ivar = :"@#{inheritable_attribute}"
|
40
42
|
subclass.instance_variable_set(ivar, instance_variable_get(ivar).clone)
|
41
43
|
|
42
44
|
if instance_variable_get(ivar).respond_to?(:merge)
|
@@ -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,12 +63,15 @@ 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 = {}
|
71
|
-
|
72
|
-
|
71
|
+
if $1
|
72
|
+
non_quoted = $1.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
|
73
|
+
non_quoted.gsub(/(\w+)=([^,]*)/) { params[$1] = $2 }
|
74
|
+
end
|
73
75
|
params
|
74
76
|
end
|
75
77
|
|
@@ -95,13 +97,13 @@ module Net
|
|
95
97
|
end
|
96
98
|
|
97
99
|
def random
|
98
|
-
format
|
100
|
+
format '%x', (Time.now.to_i + rand(65535))
|
99
101
|
end
|
100
102
|
|
101
103
|
def request_digest
|
102
104
|
a = [md5(a1), @response['nonce'], md5(a2)]
|
103
|
-
a.insert(2,
|
104
|
-
md5(a.join(
|
105
|
+
a.insert(2, '00000001', @cnonce, @response['qop']) if qop_present?
|
106
|
+
md5(a.join(':'))
|
105
107
|
end
|
106
108
|
|
107
109
|
def md5(str)
|
@@ -111,11 +113,11 @@ module Net
|
|
111
113
|
def algorithm_present?
|
112
114
|
@response.key?('algorithm') && !@response['algorithm'].empty?
|
113
115
|
end
|
114
|
-
|
116
|
+
|
115
117
|
def use_md5_sess?
|
116
118
|
algorithm_present? && @response['algorithm'] == 'MD5-sess'
|
117
119
|
end
|
118
|
-
|
120
|
+
|
119
121
|
def a1
|
120
122
|
a1_user_realm_pwd = [@username, @response['realm'], @password].join(':')
|
121
123
|
if use_md5_sess?
|
@@ -126,7 +128,7 @@ module Net
|
|
126
128
|
end
|
127
129
|
|
128
130
|
def a2
|
129
|
-
[@method, @path].join(
|
131
|
+
[@method, @path].join(':')
|
130
132
|
end
|
131
133
|
end
|
132
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,105 @@
|
|
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
|
+
# https://html.spec.whatwg.org/#multipart-form-data
|
36
|
+
MULTIPART_FORM_DATA_REPLACEMENT_TABLE = {
|
37
|
+
'"' => '%22',
|
38
|
+
"\r" => '%0D',
|
39
|
+
"\n" => '%0A'
|
40
|
+
}.freeze
|
41
|
+
|
42
|
+
def generate_multipart
|
43
|
+
normalized_params = params.flat_map { |key, value| HashConversions.normalize_keys(key, value) }
|
44
|
+
|
45
|
+
multipart = normalized_params.inject(''.dup) do |memo, (key, value)|
|
46
|
+
memo << "--#{boundary}#{NEWLINE}"
|
47
|
+
memo << %(Content-Disposition: form-data; name="#{key}")
|
48
|
+
# value.path is used to support ActionDispatch::Http::UploadedFile
|
49
|
+
# https://github.com/jnunemaker/httparty/pull/585
|
50
|
+
memo << %(; filename="#{file_name(value).gsub(/["\r\n]/, MULTIPART_FORM_DATA_REPLACEMENT_TABLE)}") if file?(value)
|
51
|
+
memo << NEWLINE
|
52
|
+
memo << "Content-Type: #{content_type(value)}#{NEWLINE}" if file?(value)
|
53
|
+
memo << NEWLINE
|
54
|
+
memo << content_body(value)
|
55
|
+
memo << NEWLINE
|
56
|
+
end
|
57
|
+
|
58
|
+
multipart << "--#{boundary}--#{NEWLINE}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def has_file?(value)
|
62
|
+
if value.respond_to?(:to_hash)
|
63
|
+
value.to_hash.any? { |_, v| has_file?(v) }
|
64
|
+
elsif value.respond_to?(:to_ary)
|
65
|
+
value.to_ary.any? { |v| has_file?(v) }
|
66
|
+
else
|
67
|
+
file?(value)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def file?(object)
|
72
|
+
object.respond_to?(:path) && object.respond_to?(:read)
|
73
|
+
end
|
74
|
+
|
75
|
+
def normalize_query(query)
|
76
|
+
if query_string_normalizer
|
77
|
+
query_string_normalizer.call(query)
|
78
|
+
else
|
79
|
+
HashConversions.to_params(query)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def content_body(object)
|
84
|
+
if file?(object)
|
85
|
+
object = (file = object).read
|
86
|
+
file.rewind if file.respond_to?(:rewind)
|
87
|
+
end
|
88
|
+
|
89
|
+
object.to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
def content_type(object)
|
93
|
+
return object.content_type if object.respond_to?(:content_type)
|
94
|
+
mime = MiniMime.lookup_by_filename(object.path)
|
95
|
+
mime ? mime.content_type : 'application/octet-stream'
|
96
|
+
end
|
97
|
+
|
98
|
+
def file_name(object)
|
99
|
+
object.respond_to?(:original_filename) ? object.original_filename : File.basename(object.path)
|
100
|
+
end
|
101
|
+
|
102
|
+
attr_reader :params, :query_string_normalizer, :force_multipart
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|