httparty 0.10.0 → 0.14.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 +7 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +92 -0
- data/.rubocop_todo.yml +124 -0
- data/.simplecov +1 -0
- data/.travis.yml +5 -4
- data/CONTRIBUTING.md +23 -0
- data/Gemfile +9 -5
- data/Guardfile +3 -3
- data/History +109 -8
- data/README.md +21 -21
- data/Rakefile +5 -10
- data/bin/httparty +21 -14
- data/docs/README.md +100 -0
- data/examples/README.md +67 -0
- data/examples/aaws.rb +9 -9
- data/examples/basic.rb +6 -10
- data/examples/crack.rb +3 -3
- data/examples/custom_parsers.rb +1 -4
- data/examples/delicious.rb +12 -12
- data/examples/google.rb +2 -2
- data/examples/headers_and_user_agents.rb +2 -2
- data/examples/logging.rb +36 -0
- data/examples/nokogiri_html_parser.rb +0 -3
- data/examples/rescue_json.rb +17 -0
- data/examples/rubyurl.rb +3 -3
- data/examples/stackexchange.rb +24 -0
- data/examples/tripit_sign_in.rb +20 -9
- data/examples/twitter.rb +11 -11
- data/examples/whoismyrep.rb +2 -2
- data/features/command_line.feature +90 -2
- data/features/digest_authentication.feature +10 -0
- data/features/handles_compressed_responses.feature +8 -0
- data/features/handles_multiple_formats.feature +23 -0
- data/features/steps/env.rb +16 -11
- data/features/steps/httparty_response_steps.rb +40 -10
- data/features/steps/httparty_steps.rb +19 -3
- data/features/steps/mongrel_helper.rb +35 -2
- data/features/steps/remote_service_steps.rb +31 -8
- data/features/supports_read_timeout_option.feature +13 -0
- data/httparty.gemspec +9 -6
- data/lib/httparty/connection_adapter.rb +76 -11
- data/lib/httparty/cookie_hash.rb +3 -4
- data/lib/httparty/exceptions.rb +10 -4
- data/lib/httparty/hash_conversions.rb +19 -17
- data/lib/httparty/logger/apache_formatter.rb +22 -0
- data/lib/httparty/logger/curl_formatter.rb +91 -0
- data/lib/httparty/logger/logger.rb +26 -0
- data/lib/httparty/module_inheritable_attributes.rb +1 -1
- data/lib/httparty/net_digest_auth.rb +69 -18
- data/lib/httparty/parser.rb +15 -11
- data/lib/httparty/request.rb +186 -47
- data/lib/httparty/response/headers.rb +2 -2
- data/lib/httparty/response.rb +44 -9
- data/lib/httparty/version.rb +1 -1
- data/lib/httparty.rb +187 -65
- data/script/release +42 -0
- data/spec/fixtures/twitter.csv +2 -0
- data/spec/httparty/connection_adapter_spec.rb +334 -62
- data/spec/httparty/cookie_hash_spec.rb +53 -23
- data/spec/httparty/exception_spec.rb +45 -0
- data/spec/httparty/hash_conversions_spec.rb +49 -0
- data/spec/httparty/logger/apache_formatter_spec.rb +41 -0
- data/spec/httparty/logger/curl_formatter_spec.rb +119 -0
- data/spec/httparty/logger/logger_spec.rb +38 -0
- data/spec/httparty/net_digest_auth_spec.rb +148 -23
- data/spec/httparty/parser_spec.rb +48 -41
- data/spec/httparty/request_spec.rb +845 -151
- data/spec/httparty/response_spec.rb +147 -70
- data/spec/httparty/ssl_spec.rb +33 -21
- data/spec/httparty_spec.rb +337 -186
- data/spec/spec_helper.rb +38 -9
- data/spec/support/ssl_test_helper.rb +10 -10
- data/spec/support/ssl_test_server.rb +21 -21
- data/spec/support/stub_response.rb +20 -14
- data/website/index.html +3 -3
- metadata +46 -37
- data/lib/httparty/core_extensions.rb +0 -32
- data/spec/spec.opts +0 -2
@@ -0,0 +1,22 @@
|
|
1
|
+
module HTTParty
|
2
|
+
module Logger
|
3
|
+
class ApacheFormatter #:nodoc:
|
4
|
+
TAG_NAME = HTTParty.name
|
5
|
+
|
6
|
+
attr_accessor :level, :logger, :current_time
|
7
|
+
|
8
|
+
def initialize(logger, level)
|
9
|
+
@logger = logger
|
10
|
+
@level = level.to_sym
|
11
|
+
end
|
12
|
+
|
13
|
+
def format(request, response)
|
14
|
+
current_time = Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
|
15
|
+
http_method = request.http_method.name.split("::").last.upcase
|
16
|
+
path = request.path.to_s
|
17
|
+
content_length = response.respond_to?(:headers) ? response.headers['Content-Length'] : response['Content-Length']
|
18
|
+
@logger.send @level, "[#{TAG_NAME}] [#{current_time}] #{response.code} \"#{http_method} #{path}\" #{content_length || '-'} "
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module HTTParty
|
2
|
+
module Logger
|
3
|
+
class CurlFormatter #:nodoc:
|
4
|
+
TAG_NAME = HTTParty.name
|
5
|
+
OUT = '>'.freeze
|
6
|
+
IN = '<'.freeze
|
7
|
+
|
8
|
+
attr_accessor :level, :logger
|
9
|
+
|
10
|
+
def initialize(logger, level)
|
11
|
+
@logger = logger
|
12
|
+
@level = level.to_sym
|
13
|
+
@messages = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def format(request, response)
|
17
|
+
@request = request
|
18
|
+
@response = response
|
19
|
+
|
20
|
+
log_request
|
21
|
+
log_response
|
22
|
+
|
23
|
+
logger.send level, messages.join("\n")
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :request, :response
|
29
|
+
attr_accessor :messages
|
30
|
+
|
31
|
+
def log_request
|
32
|
+
log_url
|
33
|
+
log_headers
|
34
|
+
log_query
|
35
|
+
log OUT, request.raw_body if request.raw_body
|
36
|
+
log OUT
|
37
|
+
end
|
38
|
+
|
39
|
+
def log_response
|
40
|
+
log IN, "HTTP/#{response.http_version} #{response.code}"
|
41
|
+
log_response_headers
|
42
|
+
log IN, "\n#{response.body}"
|
43
|
+
log IN
|
44
|
+
end
|
45
|
+
|
46
|
+
def log_url
|
47
|
+
http_method = request.http_method.name.split("::").last.upcase
|
48
|
+
uri = if request.options[:base_uri]
|
49
|
+
request.options[:base_uri] + request.path.path
|
50
|
+
else
|
51
|
+
request.path.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
log OUT, "#{http_method} #{uri}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def log_headers
|
58
|
+
return unless request.options[:headers] && request.options[:headers].size > 0
|
59
|
+
|
60
|
+
log OUT, 'Headers: '
|
61
|
+
log_hash request.options[:headers]
|
62
|
+
end
|
63
|
+
|
64
|
+
def log_query
|
65
|
+
return unless request.options[:query]
|
66
|
+
|
67
|
+
log OUT, 'Query: '
|
68
|
+
log_hash request.options[:query]
|
69
|
+
end
|
70
|
+
|
71
|
+
def log_response_headers
|
72
|
+
headers = response.respond_to?(:headers) ? response.headers : response
|
73
|
+
response.each_header do |response_header|
|
74
|
+
log IN, "#{response_header.capitalize}: #{headers[response_header]}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def log_hash(hash)
|
79
|
+
hash.each { |k, v| log(OUT, "#{k}: #{v}") }
|
80
|
+
end
|
81
|
+
|
82
|
+
def log(direction, line = '')
|
83
|
+
messages << "[#{TAG_NAME}] [#{time}] #{direction} #{line}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def time
|
87
|
+
@time ||= Time.now.strftime("%Y-%m-%d %H:%M:%S %z")
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'httparty/logger/apache_formatter'
|
2
|
+
require 'httparty/logger/curl_formatter'
|
3
|
+
|
4
|
+
module HTTParty
|
5
|
+
module Logger
|
6
|
+
def self.formatters
|
7
|
+
@formatters ||= {
|
8
|
+
:curl => Logger::CurlFormatter,
|
9
|
+
:apache => Logger::ApacheFormatter
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.add_formatter(name, formatter)
|
14
|
+
raise HTTParty::Error.new("Log Formatter with name #{name} already exists") if formatters.include?(name)
|
15
|
+
formatters.merge!(name.to_sym => formatter)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.build(logger, level, formatter)
|
19
|
+
level ||= :info
|
20
|
+
formatter ||= :apache
|
21
|
+
|
22
|
+
logger_klass = formatters[formatter] || Logger::ApacheFormatter
|
23
|
+
logger_klass.new(logger, level)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -4,10 +4,22 @@ require 'net/http'
|
|
4
4
|
module Net
|
5
5
|
module HTTPHeader
|
6
6
|
def digest_auth(username, password, response)
|
7
|
-
|
8
|
-
|
7
|
+
authenticator = DigestAuthenticator.new(
|
8
|
+
username,
|
9
|
+
password,
|
10
|
+
@method,
|
11
|
+
@path,
|
12
|
+
response
|
13
|
+
)
|
14
|
+
|
15
|
+
@header['Authorization'] = authenticator.authorization_header
|
16
|
+
@header['cookie'] = append_cookies(authenticator) if response['Set-Cookie']
|
9
17
|
end
|
10
18
|
|
19
|
+
def append_cookies(authenticator)
|
20
|
+
cookies = @header['cookie'] ? @header['cookie'] : []
|
21
|
+
cookies.concat(authenticator.cookie_header)
|
22
|
+
end
|
11
23
|
|
12
24
|
class DigestAuthenticator
|
13
25
|
def initialize(username, password, method, path, response_header)
|
@@ -16,50 +28,76 @@ module Net
|
|
16
28
|
@method = method
|
17
29
|
@path = path
|
18
30
|
@response = parse(response_header)
|
31
|
+
@cookies = parse_cookies(response_header)
|
19
32
|
end
|
20
33
|
|
21
34
|
def authorization_header
|
22
35
|
@cnonce = md5(random)
|
23
36
|
header = [
|
24
|
-
%
|
25
|
-
%
|
26
|
-
%
|
27
|
-
%
|
28
|
-
%
|
37
|
+
%(Digest username="#{@username}"),
|
38
|
+
%(realm="#{@response['realm']}"),
|
39
|
+
%(nonce="#{@response['nonce']}"),
|
40
|
+
%(uri="#{@path}"),
|
41
|
+
%(response="#{request_digest}")
|
29
42
|
]
|
30
43
|
|
44
|
+
header << %(algorithm="#{@response['algorithm']}") if algorithm_present?
|
45
|
+
|
31
46
|
if qop_present?
|
32
47
|
fields = [
|
33
|
-
%
|
34
|
-
%
|
35
|
-
|
48
|
+
%(cnonce="#{@cnonce}"),
|
49
|
+
%(qop="#{@response['qop']}"),
|
50
|
+
"nc=00000001"
|
36
51
|
]
|
37
52
|
fields.each { |field| header << field }
|
38
53
|
end
|
39
54
|
|
40
|
-
header << %
|
55
|
+
header << %(opaque="#{@response['opaque']}") if opaque_present?
|
41
56
|
header
|
42
57
|
end
|
43
58
|
|
44
|
-
|
59
|
+
def cookie_header
|
60
|
+
@cookies
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
45
64
|
|
46
65
|
def parse(response_header)
|
47
|
-
response_header['www-authenticate']
|
66
|
+
header = response_header['www-authenticate']
|
67
|
+
.gsub(/qop=(auth(?:-int)?)/, 'qop="\\1"')
|
68
|
+
|
69
|
+
header =~ /Digest (.*)/
|
48
70
|
params = {}
|
49
|
-
|
71
|
+
if $1
|
72
|
+
non_quoted = $1.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
|
73
|
+
non_quoted.gsub(/(\w+)=([^,]*)/) { params[$1] = $2 }
|
74
|
+
end
|
50
75
|
params
|
51
76
|
end
|
52
77
|
|
78
|
+
def parse_cookies(response_header)
|
79
|
+
return [] unless response_header['Set-Cookie']
|
80
|
+
|
81
|
+
cookies = response_header['Set-Cookie'].split('; ')
|
82
|
+
|
83
|
+
cookies.reduce([]) do |ret, cookie|
|
84
|
+
ret << cookie
|
85
|
+
ret
|
86
|
+
end
|
87
|
+
|
88
|
+
cookies
|
89
|
+
end
|
90
|
+
|
53
91
|
def opaque_present?
|
54
|
-
@response.
|
92
|
+
@response.key?('opaque') && !@response['opaque'].empty?
|
55
93
|
end
|
56
94
|
|
57
95
|
def qop_present?
|
58
|
-
@response.
|
96
|
+
@response.key?('qop') && !@response['qop'].empty?
|
59
97
|
end
|
60
98
|
|
61
99
|
def random
|
62
|
-
"%x"
|
100
|
+
format "%x", (Time.now.to_i + rand(65535))
|
63
101
|
end
|
64
102
|
|
65
103
|
def request_digest
|
@@ -72,8 +110,21 @@ module Net
|
|
72
110
|
Digest::MD5.hexdigest(str)
|
73
111
|
end
|
74
112
|
|
113
|
+
def algorithm_present?
|
114
|
+
@response.key?('algorithm') && !@response['algorithm'].empty?
|
115
|
+
end
|
116
|
+
|
117
|
+
def use_md5_sess?
|
118
|
+
algorithm_present? && @response['algorithm'] == 'MD5-sess'
|
119
|
+
end
|
120
|
+
|
75
121
|
def a1
|
76
|
-
[@username, @response['realm'], @password].join(
|
122
|
+
a1_user_realm_pwd = [@username, @response['realm'], @password].join(':')
|
123
|
+
if use_md5_sess?
|
124
|
+
[ md5(a1_user_realm_pwd), @response['nonce'], @cnonce ].join(':')
|
125
|
+
else
|
126
|
+
a1_user_realm_pwd
|
127
|
+
end
|
77
128
|
end
|
78
129
|
|
79
130
|
def a2
|
data/lib/httparty/parser.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module HTTParty
|
2
|
-
# The default parser used by HTTParty, supports xml, json, html, and
|
2
|
+
# The default parser used by HTTParty, supports xml, json, html, csv and
|
3
3
|
# plain text.
|
4
4
|
#
|
5
5
|
# == Custom Parsers
|
@@ -42,10 +42,13 @@ module HTTParty
|
|
42
42
|
'application/xml' => :xml,
|
43
43
|
'application/json' => :json,
|
44
44
|
'text/json' => :json,
|
45
|
-
'application/javascript' => :
|
46
|
-
'text/javascript' => :
|
45
|
+
'application/javascript' => :plain,
|
46
|
+
'text/javascript' => :plain,
|
47
47
|
'text/html' => :html,
|
48
|
-
'text/plain' => :plain
|
48
|
+
'text/plain' => :plain,
|
49
|
+
'text/csv' => :csv,
|
50
|
+
'application/csv' => :csv,
|
51
|
+
'text/comma-separated-values' => :csv
|
49
52
|
}
|
50
53
|
|
51
54
|
# The response body of the request
|
@@ -95,7 +98,9 @@ module HTTParty
|
|
95
98
|
# @return [Object] the parsed body
|
96
99
|
# @return [nil] when the response body is nil, an empty string, spaces only or "null"
|
97
100
|
def parse
|
98
|
-
return nil if body.nil?
|
101
|
+
return nil if body.nil?
|
102
|
+
return nil if body == "null"
|
103
|
+
return nil if body.valid_encoding? && body.strip.empty?
|
99
104
|
if supports_format?
|
100
105
|
parse_supported_format
|
101
106
|
else
|
@@ -110,12 +115,11 @@ module HTTParty
|
|
110
115
|
end
|
111
116
|
|
112
117
|
def json
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end
|
118
|
+
JSON.parse(body, :quirks_mode => true, :allow_nan => true)
|
119
|
+
end
|
120
|
+
|
121
|
+
def csv
|
122
|
+
CSV.parse(body)
|
119
123
|
end
|
120
124
|
|
121
125
|
def html
|