httparty 0.16.2 → 0.16.3

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.

Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +18 -0
  3. data/.gitignore +1 -0
  4. data/Changelog.md +11 -0
  5. data/README.md +1 -1
  6. data/examples/README.md +22 -11
  7. data/examples/body_stream.rb +14 -0
  8. data/examples/microsoft_graph.rb +52 -0
  9. data/examples/multipart.rb +22 -0
  10. data/features/steps/mongrel_helper.rb +3 -3
  11. data/httparty.gemspec +2 -1
  12. data/lib/httparty.rb +21 -1
  13. data/lib/httparty/connection_adapter.rb +11 -4
  14. data/lib/httparty/hash_conversions.rb +6 -2
  15. data/lib/httparty/logger/apache_formatter.rb +29 -6
  16. data/lib/httparty/logger/curl_formatter.rb +4 -4
  17. data/lib/httparty/logger/logger.rb +3 -1
  18. data/lib/httparty/logger/logstash_formatter.rb +59 -0
  19. data/lib/httparty/module_inheritable_attributes.rb +3 -3
  20. data/lib/httparty/net_digest_auth.rb +6 -5
  21. data/lib/httparty/request.rb +8 -1
  22. data/lib/httparty/request/body.rb +16 -5
  23. data/lib/httparty/response.rb +19 -5
  24. data/lib/httparty/response/headers.rb +2 -2
  25. data/lib/httparty/utils.rb +11 -0
  26. data/lib/httparty/version.rb +1 -1
  27. data/spec/httparty/connection_adapter_spec.rb +7 -3
  28. data/spec/httparty/cookie_hash_spec.rb +1 -1
  29. data/spec/httparty/exception_spec.rb +1 -1
  30. data/spec/httparty/hash_conversions_spec.rb +2 -0
  31. data/spec/httparty/logger/apache_formatter_spec.rb +1 -2
  32. data/spec/httparty/logger/curl_formatter_spec.rb +1 -1
  33. data/spec/httparty/logger/logger_spec.rb +6 -1
  34. data/spec/httparty/logger/logstash_formatter_spec.rb +44 -0
  35. data/spec/httparty/net_digest_auth_spec.rb +22 -22
  36. data/spec/httparty/parser_spec.rb +1 -1
  37. data/spec/httparty/request/body_spec.rb +57 -8
  38. data/spec/httparty/request_spec.rb +68 -13
  39. data/spec/httparty/response_spec.rb +32 -20
  40. data/spec/httparty/ssl_spec.rb +1 -1
  41. data/spec/httparty_spec.rb +28 -7
  42. data/website/css/common.css +1 -1
  43. metadata +25 -3
@@ -20,7 +20,7 @@ module HTTParty
20
20
  log_request
21
21
  log_response
22
22
 
23
- logger.send level, messages.join("\n")
23
+ logger.public_send level, messages.join("\n")
24
24
  end
25
25
 
26
26
  private
@@ -80,11 +80,11 @@ module HTTParty
80
80
  end
81
81
 
82
82
  def log(direction, line = '')
83
- messages << "[#{TAG_NAME}] [#{time}] #{direction} #{line}"
83
+ messages << "[#{TAG_NAME}] [#{current_time}] #{direction} #{line}"
84
84
  end
85
85
 
86
- def time
87
- @time ||= Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
86
+ def current_time
87
+ Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
88
88
  end
89
89
  end
90
90
  end
@@ -1,12 +1,14 @@
1
1
  require 'httparty/logger/apache_formatter'
2
2
  require 'httparty/logger/curl_formatter'
3
+ require 'httparty/logger/logstash_formatter'
3
4
 
4
5
  module HTTParty
5
6
  module Logger
6
7
  def self.formatters
7
8
  @formatters ||= {
8
9
  :curl => Logger::CurlFormatter,
9
- :apache => Logger::ApacheFormatter
10
+ :apache => Logger::ApacheFormatter,
11
+ :logstash => Logger::LogstashFormatter,
10
12
  }
11
13
  end
12
14
 
@@ -0,0 +1,59 @@
1
+ module HTTParty
2
+ module Logger
3
+ class LogstashFormatter #:nodoc:
4
+ TAG_NAME = HTTParty.name
5
+
6
+ attr_accessor :level, :logger
7
+
8
+ def initialize(logger, level)
9
+ @logger = logger
10
+ @level = level.to_sym
11
+ end
12
+
13
+ def format(request, response)
14
+ @request = request
15
+ @response = response
16
+
17
+ logger.public_send level, logstash_message
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :request, :response
23
+
24
+ def logstash_message
25
+ {
26
+ '@timestamp' => current_time,
27
+ '@version' => 1,
28
+ 'content_length' => content_length || '-',
29
+ 'http_method' => http_method,
30
+ 'message' => message,
31
+ 'path' => path,
32
+ 'response_code' => response.code,
33
+ 'severity' => level,
34
+ 'tags' => [TAG_NAME],
35
+ }.to_json
36
+ end
37
+
38
+ def message
39
+ "[#{TAG_NAME}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
40
+ end
41
+
42
+ def current_time
43
+ Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
44
+ end
45
+
46
+ def http_method
47
+ @http_method ||= request.http_method.name.split("::").last.upcase
48
+ end
49
+
50
+ def path
51
+ @path ||= request.path.to_s
52
+ end
53
+
54
+ def content_length
55
+ @content_length ||= response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
56
+ end
57
+ end
58
+ end
59
+ end
@@ -9,12 +9,12 @@ module HTTParty
9
9
  duplicate = hash.dup
10
10
 
11
11
  duplicate.each_pair do |key, value|
12
- duplicate[key] = if value.is_a?(Hash)
13
- hash_deep_dup(value)
12
+ if value.is_a?(Hash)
13
+ duplicate[key] = hash_deep_dup(value)
14
14
  elsif value.is_a?(Proc)
15
15
  duplicate[key] = value.dup
16
16
  else
17
- value
17
+ duplicate[key] = value
18
18
  end
19
19
  end
20
20
 
@@ -14,11 +14,11 @@ module Net
14
14
 
15
15
  authenticator.authorization_header.each do |v|
16
16
  add_field('Authorization', v)
17
- end
17
+ end
18
18
 
19
19
  authenticator.cookie_header.each do |v|
20
20
  add_field('Cookie', v)
21
- end
21
+ end
22
22
  end
23
23
 
24
24
  class DigestAuthenticator
@@ -64,7 +64,8 @@ module Net
64
64
 
65
65
  def parse(response_header)
66
66
  header = response_header['www-authenticate']
67
- .gsub(/qop=(auth(?:-int)?)/, 'qop="\\1"')
67
+
68
+ header = header.gsub(/qop=(auth(?:-int)?)/, 'qop="\\1"')
68
69
 
69
70
  header =~ /Digest (.*)/
70
71
  params = {}
@@ -113,11 +114,11 @@ module Net
113
114
  def algorithm_present?
114
115
  @response.key?('algorithm') && !@response['algorithm'].empty?
115
116
  end
116
-
117
+
117
118
  def use_md5_sess?
118
119
  algorithm_present? && @response['algorithm'] == 'MD5-sess'
119
120
  end
120
-
121
+
121
122
  def a1
122
123
  a1_user_realm_pwd = [@username, @response['realm'], @password].join(':')
123
124
  if use_md5_sess?
@@ -14,6 +14,8 @@ module HTTParty
14
14
  Net::HTTP::Move,
15
15
  Net::HTTP::Copy,
16
16
  Net::HTTP::Mkcol,
17
+ Net::HTTP::Lock,
18
+ Net::HTTP::Unlock,
17
19
  ]
18
20
 
19
21
  SupportedURISchemes = ['http', 'https', 'webcal', nil]
@@ -219,7 +221,12 @@ module HTTParty
219
221
  end
220
222
 
221
223
  if options[:body]
222
- body = Body.new(options[:body], query_string_normalizer: query_string_normalizer)
224
+ body = Body.new(
225
+ options[:body],
226
+ query_string_normalizer: query_string_normalizer,
227
+ force_multipart: options[:multipart]
228
+ )
229
+
223
230
  if body.multipart?
224
231
  content_type = "multipart/form-data; boundary=#{body.boundary}"
225
232
  @raw_request['Content-Type'] = content_type
@@ -3,9 +3,10 @@ require_relative 'multipart_boundary'
3
3
  module HTTParty
4
4
  class Request
5
5
  class Body
6
- def initialize(params, query_string_normalizer: nil)
6
+ def initialize(params, query_string_normalizer: nil, force_multipart: false)
7
7
  @params = params
8
8
  @query_string_normalizer = query_string_normalizer
9
+ @force_multipart = force_multipart
9
10
  end
10
11
 
11
12
  def call
@@ -21,7 +22,7 @@ module HTTParty
21
22
  end
22
23
 
23
24
  def multipart?
24
- params.respond_to?(:to_hash) && has_file?(params.to_hash)
25
+ params.respond_to?(:to_hash) && (force_multipart || has_file?(params.to_hash))
25
26
  end
26
27
 
27
28
  private
@@ -34,9 +35,9 @@ module HTTParty
34
35
  memo += %(Content-Disposition: form-data; name="#{key}")
35
36
  # value.path is used to support ActionDispatch::Http::UploadedFile
36
37
  # https://github.com/jnunemaker/httparty/pull/585
37
- memo += %(; filename="#{File.basename(value.path)}") if file?(value)
38
+ memo += %(; filename="#{file_name(value)}") if file?(value)
38
39
  memo += "\r\n"
39
- memo += "Content-Type: application/octet-stream\r\n" if file?(value)
40
+ memo += "Content-Type: #{content_type(value)}\r\n" if file?(value)
40
41
  memo += "\r\n"
41
42
  memo += file?(value) ? value.read : value.to_s
42
43
  memo += "\r\n"
@@ -73,7 +74,17 @@ module HTTParty
73
74
  end
74
75
  end
75
76
 
76
- attr_reader :params, :query_string_normalizer
77
+ def content_type(object)
78
+ return object.content_type if object.respond_to?(:content_type)
79
+ mime = MIME::Types.type_for(object.path)
80
+ mime.empty? ? 'application/octet-stream' : mime[0].content_type
81
+ end
82
+
83
+ def file_name(object)
84
+ object.respond_to?(:original_filename) ? object.original_filename : File.basename(object.path)
85
+ end
86
+
87
+ attr_reader :params, :query_string_normalizer, :force_multipart
77
88
  end
78
89
  end
79
90
  end
@@ -4,6 +4,12 @@ module HTTParty
4
4
  string.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z])([A-Z])/, '\1_\2').downcase
5
5
  end
6
6
 
7
+ def self._load(data)
8
+ req, resp, parsed_resp, resp_body = Marshal.load(data)
9
+
10
+ new(req, resp, -> { parsed_resp }, body: resp_body)
11
+ end
12
+
7
13
  attr_reader :request, :response, :body, :headers
8
14
 
9
15
  def initialize(request, response, parsed_block, options = {})
@@ -14,7 +20,11 @@ module HTTParty
14
20
  @headers = Headers.new(response.to_hash)
15
21
 
16
22
  if request.options[:logger]
17
- logger = ::HTTParty::Logger.build(request.options[:logger], request.options[:log_level], request.options[:log_format])
23
+ logger = ::HTTParty::Logger.build(
24
+ request.options[:logger],
25
+ request.options[:log_level],
26
+ request.options[:log_format]
27
+ )
18
28
  logger.format(request, self)
19
29
  end
20
30
 
@@ -59,10 +69,10 @@ module HTTParty
59
69
  response.nil? || response.body.nil? || response.body.empty?
60
70
  end
61
71
 
62
- def to_s
72
+ def to_s
63
73
  if !response.nil? && !response.body.nil? && response.body.respond_to?(:to_s)
64
74
  response.body.to_s
65
- else
75
+ else
66
76
  inspect
67
77
  end
68
78
  end
@@ -80,7 +90,7 @@ module HTTParty
80
90
  parsed_response.display(port)
81
91
  elsif !response.nil? && !response.body.nil? && response.body.respond_to?(:display)
82
92
  response.body.display(port)
83
- else
93
+ else
84
94
  port.write(inspect)
85
95
  end
86
96
  end
@@ -89,7 +99,11 @@ module HTTParty
89
99
  return true if super
90
100
  parsed_response.respond_to?(name) || response.respond_to?(name)
91
101
  end
92
-
102
+
103
+ def _dump(_level)
104
+ Marshal.dump([request, response, parsed_response, body])
105
+ end
106
+
93
107
  protected
94
108
 
95
109
  def method_missing(name, *args, &block)
@@ -22,10 +22,10 @@ module HTTParty
22
22
  end
23
23
 
24
24
  def ==(other)
25
- if other.is_a?(::Net::HTTPHeader)
25
+ if other.is_a?(::Net::HTTPHeader)
26
26
  @header == other.instance_variable_get(:@header)
27
27
  elsif other.is_a?(Hash)
28
- @header == other || @header == Headers.new(other).instance_variable_get(:@header)
28
+ @header == other || @header == Headers.new(other).instance_variable_get(:@header)
29
29
  end
30
30
  end
31
31
  end
@@ -0,0 +1,11 @@
1
+ module HTTParty
2
+ module Utils
3
+ def self.stringify_keys(hash)
4
+ return hash.transform_keys(&:to_s) if hash.respond_to?(:transform_keys)
5
+
6
+ hash.each_with_object({}) do |(key, value), new_hash|
7
+ new_hash[key.to_s] = value
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module HTTParty
2
- VERSION = "0.16.2"
2
+ VERSION = "0.16.3"
3
3
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
1
+ require 'spec_helper'
2
2
 
3
3
  RSpec.describe HTTParty::ConnectionAdapter do
4
4
  describe "initialization" do
@@ -312,8 +312,12 @@ RSpec.describe HTTParty::ConnectionAdapter do
312
312
 
313
313
  context 'as well as proxy user and password' do
314
314
  let(:options) do
315
- {http_proxyaddr: '1.2.3.4', http_proxyport: 8080,
316
- http_proxyuser: 'user', http_proxypass: 'pass'}
315
+ {
316
+ http_proxyaddr: '1.2.3.4',
317
+ http_proxyport: 8080,
318
+ http_proxyuser: 'user',
319
+ http_proxypass: 'pass'
320
+ }
317
321
  end
318
322
 
319
323
  describe '#proxy_user' do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper'))
1
+ require 'spec_helper'
2
2
 
3
3
  RSpec.describe HTTParty::CookieHash do
4
4
  before(:each) do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
1
+ require 'spec_helper'
2
2
 
3
3
  RSpec.describe HTTParty::Error do
4
4
  subject { described_class }
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  RSpec.describe HTTParty::HashConversions do
2
4
  describe ".to_params" do
3
5
  it "creates a params string from a hash" do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
1
+ require 'spec_helper'
2
2
 
3
3
  RSpec.describe HTTParty::Logger::ApacheFormatter do
4
4
  let(:subject) { described_class.new(logger_double, :info) }
@@ -7,7 +7,6 @@ RSpec.describe HTTParty::Logger::ApacheFormatter do
7
7
  let(:request_time) { Time.new.strftime("%Y-%m-%d %H:%M:%S %z") }
8
8
 
9
9
  before do
10
- subject.current_time = request_time
11
10
  expect(logger_double).to receive(:info).with(log_message)
12
11
  end
13
12
 
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
1
+ require 'spec_helper'
2
2
 
3
3
  RSpec.describe HTTParty::Logger::CurlFormatter do
4
4
  describe "#format" do
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
1
+ require 'spec_helper'
2
2
 
3
3
  RSpec.describe HTTParty::Logger do
4
4
  describe ".build" do
@@ -19,6 +19,11 @@ RSpec.describe HTTParty::Logger do
19
19
  expect(subject.build(logger_double, nil, :curl)).to be_an_instance_of(HTTParty::Logger::CurlFormatter)
20
20
  end
21
21
 
22
+ it "builds :logstash style logger" do
23
+ logger_double = double
24
+ expect(subject.build(logger_double, nil, :logstash)).to be_an_instance_of(HTTParty::Logger::LogstashFormatter)
25
+ end
26
+
22
27
  it "builds :custom style logger" do
23
28
  CustomFormatter = Class.new(HTTParty::Logger::CurlFormatter)
24
29
  HTTParty::Logger.add_formatter(:custom, CustomFormatter)
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe HTTParty::Logger::LogstashFormatter do
4
+ let(:severity) { :info }
5
+ let(:http_method) { 'GET' }
6
+ let(:path) { 'http://my.domain.com/my_path' }
7
+ let(:logger_double) { double('Logger') }
8
+ let(:request_double) { double('Request', http_method: Net::HTTP::Get, path: "#{path}") }
9
+ let(:request_time) { Time.new.strftime("%Y-%m-%d %H:%M:%S %z") }
10
+ let(:log_message) do
11
+ {
12
+ '@timestamp' => request_time,
13
+ '@version' => 1,
14
+ 'content_length' => content_length || '-',
15
+ 'http_method' => http_method,
16
+ 'message' => message,
17
+ 'path' => path,
18
+ 'response_code' => response_code,
19
+ 'severity' => severity,
20
+ 'tags' => ['HTTParty'],
21
+ }.to_json
22
+ end
23
+
24
+ subject { described_class.new(logger_double, severity) }
25
+
26
+ before do
27
+ expect(logger_double).to receive(:info).with(log_message)
28
+ end
29
+
30
+ describe "#format" do
31
+ let(:response_code) { 302 }
32
+ let(:content_length) { '-' }
33
+ let(:message) { "[HTTParty] #{response_code} \"#{http_method} #{path}\" #{content_length} " }
34
+
35
+ it "formats a response to be compatible with Logstash" do
36
+ response_double = double(
37
+ code: response_code,
38
+ :[] => nil
39
+ )
40
+
41
+ subject.format(request_double, response_double)
42
+ end
43
+ end
44
+ end