httparty 0.15.7 → 0.16.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8dd9a56f1d2f44c1625e80e31683c4e14d333495
4
- data.tar.gz: 41a140c3370a9ce0bceaaff0c07fa1ac32491660
3
+ metadata.gz: 3b3d9cd5bf2c8c65e7c422ee6424b08fabde5bee
4
+ data.tar.gz: c5e5164791c0c4ab0c1dca1ad1b87385b382d36a
5
5
  SHA512:
6
- metadata.gz: 14861c965bdad3631ee5e4ae9e51e044e65f30d5884ce32571e0add19c1fb7ba8962e6119e40cc112928744477c4187ca01e7bd2c8764d7a8f2c69609d1ffbeb
7
- data.tar.gz: 2918c91eef01e6f88a6d7cb905998d35a9c18589835cad7b5668ba47a2f549fd1dc15d6db08e73b031701fcd8021d955971e82707e3b8d9d37610da1de4eba6d
6
+ metadata.gz: 27268caca0723458d76464e7e510eb737feb425a9a9f2007337d9f1e12976b0bfb5c39850a5654f990fe66d1f7a4a53d2de4de80b57bfd670e470c9269c25b1f
7
+ data.tar.gz: 3c7360f0232df51be5491f3c98bba7998b9321c42a5a845680be055b7929edc024a4a9fdad7aad14501ea3dba8875a5d9debfd24ba31e6a9779accf2e1935a9f
@@ -1,3 +1,7 @@
1
+ ## 0.16.0
2
+
3
+ * [Add multipart support](https://github.com/jnunemaker/httparty/pull/569)
4
+
1
5
  ## 0.15.7
2
6
 
3
7
  Fixed
@@ -1,3 +1,7 @@
1
+ dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require File.join(dir, 'httparty')
3
+ require 'pp'
4
+
1
5
  class ParseAtom
2
6
  include HTTParty
3
7
 
@@ -1,6 +1,10 @@
1
1
  # To send custom user agents to identify your application to a web service (or mask as a specific browser for testing), send "User-Agent" as a hash to headers as shown below.
2
2
 
3
- require 'httparty'
3
+ dir = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+ require File.join(dir, 'httparty')
5
+ require 'pp'
4
6
 
5
- APPLICATION_NAME = "Httparty"
6
- response = HTTParty.get('http://example.com', headers: {"User-Agent" => APPLICATION_NAME})
7
+ response = HTTParty.get('http://example.com', {
8
+ headers: {"User-Agent" => "Httparty"},
9
+ debug_output: STDOUT, # To show that User-Agent is Httparty
10
+ })
@@ -3,7 +3,7 @@ require File.join(dir, 'httparty')
3
3
  require 'logger'
4
4
  require 'pp'
5
5
 
6
- my_logger = Logger.new "httparty.log"
6
+ my_logger = Logger.new STDOUT
7
7
 
8
8
  my_logger.info "Logging can be used on the main HTTParty class. It logs redirects too."
9
9
  HTTParty.get "http://google.com", logger: my_logger
@@ -14,7 +14,7 @@ my_logger.info "It can be used also on a custom class."
14
14
 
15
15
  class Google
16
16
  include HTTParty
17
- logger ::Logger.new "httparty.log"
17
+ logger ::Logger.new STDOUT
18
18
  end
19
19
 
20
20
  Google.get "http://google.com"
@@ -30,7 +30,7 @@ my_logger.info '*' * 70
30
30
  my_logger.info "These configs are also available on custom classes."
31
31
  class Google
32
32
  include HTTParty
33
- logger ::Logger.new("httparty.log"), :debug, :curl
33
+ logger ::Logger.new(STDOUT), :debug, :curl
34
34
  end
35
35
 
36
36
  Google.get "http://google.com"
@@ -24,32 +24,43 @@ module HTTParty
24
24
  #
25
25
  # @example normalize_param(:name, "Bob Jones") #=> "name=Bob%20Jones&"
26
26
  def self.normalize_param(key, value)
27
- param = ''
27
+ normalized_keys = normalize_keys(key, value)
28
+
29
+ normalized_keys.inject('') do |string, (k, v)|
30
+ string + "#{k}=#{ERB::Util.url_encode(v.to_s)}&"
31
+ end
32
+ end
33
+
34
+ def self.normalize_keys(key, value)
28
35
  stack = []
36
+ normalized_keys = []
29
37
 
30
38
  if value.respond_to?(:to_ary)
31
- param << if value.empty?
32
- "#{key}[]=&"
33
- else
34
- value.to_ary.map { |element| normalize_param("#{key}[]", element) }.join
35
- end
39
+ if value.empty?
40
+ normalized_keys << ["#{key}[]", '']
41
+ else
42
+ normalized_keys = value.to_ary.flat_map { |element| normalize_keys("#{key}[]", element) }
43
+ end
36
44
  elsif value.respond_to?(:to_hash)
37
45
  stack << [key, value.to_hash]
38
46
  else
39
- param << "#{key}=#{ERB::Util.url_encode(value.to_s)}&"
47
+ normalized_keys << [key.to_s, value]
40
48
  end
41
49
 
50
+
42
51
  stack.each do |parent, hash|
43
- hash.each do |k, v|
44
- if v.respond_to?(:to_hash)
45
- stack << ["#{parent}[#{k}]", v.to_hash]
52
+ hash.each do |key, value|
53
+ if value.respond_to?(:to_hash)
54
+ stack << ["#{parent}[#{key}]", value.to_hash]
55
+ elsif value.respond_to?(:to_ary)
56
+ value.to_ary.each { |v| normalized_keys << normalize_keys("#{parent}[#{key}][]", v).flatten }
46
57
  else
47
- param << normalize_param("#{parent}[#{k}]", v)
58
+ normalized_keys << normalize_keys("#{parent}[#{key}]", value).flatten
48
59
  end
49
60
  end
50
61
  end
51
62
 
52
- param
63
+ normalized_keys
53
64
  end
54
65
  end
55
66
  end
@@ -1,4 +1,5 @@
1
1
  require 'erb'
2
+ require 'httparty/request/body'
2
3
 
3
4
  module HTTParty
4
5
  class Request #:nodoc:
@@ -176,10 +177,6 @@ module HTTParty
176
177
  connection_adapter.call(uri, options)
177
178
  end
178
179
 
179
- def body
180
- options[:body].respond_to?(:to_hash) ? normalize_query(options[:body]) : options[:body]
181
- end
182
-
183
180
  def credentials
184
181
  (options[:basic_auth] || options[:digest_auth]).to_hash
185
182
  end
@@ -206,8 +203,17 @@ module HTTParty
206
203
 
207
204
  def setup_raw_request
208
205
  @raw_request = http_method.new(request_uri(uri))
209
- @raw_request.body = body if body
210
206
  @raw_request.body_stream = options[:body_stream] if options[:body_stream]
207
+
208
+ if options[:body]
209
+ body = Body.new(options[:body], query_string_normalizer: query_string_normalizer)
210
+ if body.multipart?
211
+ content_type = "multipart/form-data; boundary=#{body.boundary}"
212
+ @raw_request.initialize_http_header('Content-Type' => content_type)
213
+ end
214
+ @raw_request.body = body.call
215
+ end
216
+
211
217
  if options[:headers].respond_to?(:to_hash)
212
218
  headers_hash = options[:headers].to_hash
213
219
 
@@ -220,6 +226,7 @@ module HTTParty
220
226
  @raw_request['accept-encoding'] = @raw_request['accept-encoding']
221
227
  end
222
228
  end
229
+
223
230
  if options[:basic_auth] && send_authorization_header?
224
231
  @raw_request.basic_auth(username, password)
225
232
  @credentials_sent = true
@@ -0,0 +1,77 @@
1
+ require_relative 'multipart_boundary'
2
+
3
+ module HTTParty
4
+ class Request
5
+ class Body
6
+ def initialize(params, query_string_normalizer: nil)
7
+ @params = params
8
+ @query_string_normalizer = query_string_normalizer
9
+ end
10
+
11
+ def call
12
+ if params.respond_to?(:to_hash)
13
+ multipart? ? generate_multipart : normalize_query(params)
14
+ else
15
+ params
16
+ end
17
+ end
18
+
19
+ def boundary
20
+ @boundary ||= MultipartBoundary.generate
21
+ end
22
+
23
+ def multipart?
24
+ params.respond_to?(:to_hash) && has_file?(params.to_hash)
25
+ end
26
+
27
+ private
28
+
29
+ def generate_multipart
30
+ normalized_params = params.flat_map { |key, value| HashConversions.normalize_keys(key, value) }
31
+
32
+ multipart = normalized_params.inject('') do |memo, (key, value)|
33
+ memo += "--#{boundary}\r\n"
34
+ memo += %(Content-Disposition: form-data; name="#{key}")
35
+ memo += %(; filename="#{File.basename(value)}") if file?(value)
36
+ memo += "\r\n"
37
+ memo += "Content-Type: application/octet-stream\r\n" if file?(value)
38
+ memo += "\r\n"
39
+ memo += file?(value) ? value.read : value
40
+ memo += "\r\n"
41
+ end
42
+
43
+ multipart += "--#{boundary}--\r\n"
44
+ end
45
+
46
+ def has_file?(hash)
47
+ hash.detect do |key, value|
48
+ if value.respond_to?(:to_hash) || includes_hash?(value)
49
+ has_file?(value)
50
+ elsif value.respond_to?(:to_ary)
51
+ value.any? { |e| file?(e) }
52
+ else
53
+ file?(value)
54
+ end
55
+ end
56
+ end
57
+
58
+ def file?(object)
59
+ object.respond_to?(:path) && object.respond_to?(:read) # add memoization
60
+ end
61
+
62
+ def includes_hash?(object)
63
+ object.respond_to?(:to_ary) && object.any? { |e| e.respond_to?(:hash) }
64
+ end
65
+
66
+ def normalize_query(query)
67
+ if query_string_normalizer
68
+ query_string_normalizer.call(query)
69
+ else
70
+ HashConversions.to_params(query)
71
+ end
72
+ end
73
+
74
+ attr_reader :params, :query_string_normalizer
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,11 @@
1
+ require 'securerandom'
2
+
3
+ module HTTParty
4
+ class Request
5
+ class MultipartBoundary
6
+ def self.generate
7
+ "------------------------#{SecureRandom.urlsafe_base64(12)}"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module HTTParty
2
- VERSION = "0.15.7"
2
+ VERSION = "0.16.0"
3
3
  end
Binary file
@@ -0,0 +1,55 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ RSpec.describe HTTParty::Request::Body do
4
+ describe '#call' do
5
+ subject { described_class.new(params).call }
6
+
7
+ context 'when params is string' do
8
+ let(:params) { 'name=Bob%20Jones' }
9
+
10
+ it { is_expected.to eq params }
11
+ end
12
+
13
+ context 'when params is hash' do
14
+ let(:params) { { people: ["Bob Jones", "Mike Smith"] } }
15
+ let(:converted_params) { "people[]=Bob%20Jones&people[]=Mike%20Smith"}
16
+
17
+ it { is_expected.to eq converted_params }
18
+
19
+ context 'when params has file' do
20
+ before do
21
+ allow(HTTParty::Request::MultipartBoundary).
22
+ to receive(:generate).and_return("------------------------c772861a5109d5ef")
23
+ end
24
+
25
+ let(:params) do
26
+ {
27
+ user: {
28
+ avatar: File.open('spec/fixtures/tiny.gif'),
29
+ first_name: 'John',
30
+ last_name: 'Doe'
31
+ }
32
+ }
33
+ end
34
+ let(:multipart_params) do
35
+ "--------------------------c772861a5109d5ef\r\n" \
36
+ "Content-Disposition: form-data; name=\"user[avatar]\"; filename=\"tiny.gif\"\r\n" \
37
+ "Content-Type: application/octet-stream\r\n" \
38
+ "\r\n" \
39
+ "GIF89a\u0001\u0000\u0001\u0000\u0000\xFF\u0000,\u0000\u0000\u0000\u0000\u0001\u0000\u0001\u0000\u0000\u0002\u0000;\r\n" \
40
+ "--------------------------c772861a5109d5ef\r\n" \
41
+ "Content-Disposition: form-data; name=\"user[first_name]\"\r\n" \
42
+ "\r\n" \
43
+ "John\r\n" \
44
+ "--------------------------c772861a5109d5ef\r\n" \
45
+ "Content-Disposition: form-data; name=\"user[last_name]\"\r\n" \
46
+ "\r\n" \
47
+ "Doe\r\n" \
48
+ "--------------------------c772861a5109d5ef--\r\n"
49
+ end
50
+
51
+ it { is_expected.to eq multipart_params }
52
+ end
53
+ end
54
+ end
55
+ end
@@ -188,6 +188,23 @@ RSpec.describe HTTParty do
188
188
  @klass.get('', cookies: {type: 'snickerdoodle'}, headers: {baz: 'spax'})
189
189
  end
190
190
  end
191
+
192
+ context 'when posting file' do
193
+ let(:boundary) { '------------------------c772861a5109d5ef' }
194
+ let(:headers) do
195
+ { 'Content-Type'=>"multipart/form-data; boundary=#{boundary}" }
196
+ end
197
+
198
+ before do
199
+ expect(HTTParty::Request::MultipartBoundary).to receive(:generate).and_return(boundary)
200
+ end
201
+
202
+ it 'changes content-type headers to multipart/form-data' do
203
+ stub_request(:post, "http://example.com/").with(headers: headers)
204
+
205
+ @klass.post('http://example.com', body: { file: File.open('spec/fixtures/tiny.gif')})
206
+ end
207
+ end
191
208
  end
192
209
 
193
210
  describe "cookies" do
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.15.7
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Nunemaker
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-02-07 00:00:00.000000000 Z
12
+ date: 2018-02-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_xml
@@ -92,6 +92,8 @@ files:
92
92
  - lib/httparty/net_digest_auth.rb
93
93
  - lib/httparty/parser.rb
94
94
  - lib/httparty/request.rb
95
+ - lib/httparty/request/body.rb
96
+ - lib/httparty/request/multipart_boundary.rb
95
97
  - lib/httparty/response.rb
96
98
  - lib/httparty/response/headers.rb
97
99
  - lib/httparty/version.rb
@@ -107,6 +109,7 @@ files:
107
109
  - spec/fixtures/ssl/generated/server.crt
108
110
  - spec/fixtures/ssl/generated/server.key
109
111
  - spec/fixtures/ssl/openssl-exts.cnf
112
+ - spec/fixtures/tiny.gif
110
113
  - spec/fixtures/twitter.csv
111
114
  - spec/fixtures/twitter.json
112
115
  - spec/fixtures/twitter.xml
@@ -120,6 +123,7 @@ files:
120
123
  - spec/httparty/logger/logger_spec.rb
121
124
  - spec/httparty/net_digest_auth_spec.rb
122
125
  - spec/httparty/parser_spec.rb
126
+ - spec/httparty/request/body_spec.rb
123
127
  - spec/httparty/request_spec.rb
124
128
  - spec/httparty/response_spec.rb
125
129
  - spec/httparty/ssl_spec.rb
@@ -180,6 +184,7 @@ test_files:
180
184
  - spec/fixtures/ssl/generated/server.crt
181
185
  - spec/fixtures/ssl/generated/server.key
182
186
  - spec/fixtures/ssl/openssl-exts.cnf
187
+ - spec/fixtures/tiny.gif
183
188
  - spec/fixtures/twitter.csv
184
189
  - spec/fixtures/twitter.json
185
190
  - spec/fixtures/twitter.xml
@@ -193,6 +198,7 @@ test_files:
193
198
  - spec/httparty/logger/logger_spec.rb
194
199
  - spec/httparty/net_digest_auth_spec.rb
195
200
  - spec/httparty/parser_spec.rb
201
+ - spec/httparty/request/body_spec.rb
196
202
  - spec/httparty/request_spec.rb
197
203
  - spec/httparty/response_spec.rb
198
204
  - spec/httparty/ssl_spec.rb