httpserious 0.13.5.lstoll1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +92 -0
- data/.rubocop_todo.yml +124 -0
- data/.simplecov +1 -0
- data/.travis.yml +7 -0
- data/CONTRIBUTING.md +23 -0
- data/Gemfile +19 -0
- data/Guardfile +16 -0
- data/History +370 -0
- data/MIT-LICENSE +20 -0
- data/README.md +78 -0
- data/Rakefile +10 -0
- data/bin/httparty +116 -0
- data/cucumber.yml +1 -0
- data/examples/README.md +67 -0
- data/examples/aaws.rb +32 -0
- data/examples/basic.rb +28 -0
- data/examples/crack.rb +19 -0
- data/examples/custom_parsers.rb +64 -0
- data/examples/delicious.rb +37 -0
- data/examples/google.rb +16 -0
- data/examples/headers_and_user_agents.rb +6 -0
- data/examples/logging.rb +36 -0
- data/examples/nokogiri_html_parser.rb +19 -0
- data/examples/rescue_json.rb +17 -0
- data/examples/rubyurl.rb +14 -0
- data/examples/stackexchange.rb +24 -0
- data/examples/tripit_sign_in.rb +33 -0
- data/examples/twitter.rb +31 -0
- data/examples/whoismyrep.rb +10 -0
- data/features/basic_authentication.feature +20 -0
- data/features/command_line.feature +90 -0
- data/features/deals_with_http_error_codes.feature +26 -0
- data/features/digest_authentication.feature +20 -0
- data/features/handles_compressed_responses.feature +27 -0
- data/features/handles_multiple_formats.feature +57 -0
- data/features/steps/env.rb +27 -0
- data/features/steps/httparty_response_steps.rb +52 -0
- data/features/steps/httparty_steps.rb +43 -0
- data/features/steps/mongrel_helper.rb +94 -0
- data/features/steps/remote_service_steps.rb +86 -0
- data/features/supports_read_timeout_option.feature +13 -0
- data/features/supports_redirection.feature +22 -0
- data/features/supports_timeout_option.feature +13 -0
- data/httparty.gemspec +28 -0
- data/httpserious.gemspec +25 -0
- data/lib/httparty.rb +612 -0
- data/lib/httparty/connection_adapter.rb +190 -0
- data/lib/httparty/cookie_hash.rb +21 -0
- data/lib/httparty/exceptions.rb +29 -0
- data/lib/httparty/hash_conversions.rb +49 -0
- data/lib/httparty/logger/apache_formatter.rb +22 -0
- data/lib/httparty/logger/curl_formatter.rb +48 -0
- data/lib/httparty/logger/logger.rb +26 -0
- data/lib/httparty/module_inheritable_attributes.rb +56 -0
- data/lib/httparty/net_digest_auth.rb +117 -0
- data/lib/httparty/parser.rb +141 -0
- data/lib/httparty/request.rb +361 -0
- data/lib/httparty/response.rb +77 -0
- data/lib/httparty/response/headers.rb +31 -0
- data/lib/httparty/version.rb +3 -0
- data/lib/httpserious.rb +1 -0
- data/script/release +42 -0
- data/spec/fixtures/delicious.xml +23 -0
- data/spec/fixtures/empty.xml +0 -0
- data/spec/fixtures/google.html +3 -0
- data/spec/fixtures/ssl/generate.sh +29 -0
- data/spec/fixtures/ssl/generated/1fe462c2.0 +16 -0
- data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
- data/spec/fixtures/ssl/generated/ca.crt +16 -0
- data/spec/fixtures/ssl/generated/ca.key +15 -0
- data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
- data/spec/fixtures/ssl/generated/server.crt +13 -0
- data/spec/fixtures/ssl/generated/server.key +15 -0
- data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
- data/spec/fixtures/twitter.csv +2 -0
- data/spec/fixtures/twitter.json +1 -0
- data/spec/fixtures/twitter.xml +403 -0
- data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
- data/spec/httparty/connection_adapter_spec.rb +468 -0
- data/spec/httparty/cookie_hash_spec.rb +83 -0
- data/spec/httparty/exception_spec.rb +38 -0
- data/spec/httparty/hash_conversions_spec.rb +41 -0
- data/spec/httparty/logger/apache_formatter_spec.rb +41 -0
- data/spec/httparty/logger/curl_formatter_spec.rb +18 -0
- data/spec/httparty/logger/logger_spec.rb +38 -0
- data/spec/httparty/net_digest_auth_spec.rb +191 -0
- data/spec/httparty/parser_spec.rb +167 -0
- data/spec/httparty/request_spec.rb +872 -0
- data/spec/httparty/response_spec.rb +241 -0
- data/spec/httparty/ssl_spec.rb +74 -0
- data/spec/httparty_spec.rb +823 -0
- data/spec/spec_helper.rb +59 -0
- data/spec/support/ssl_test_helper.rb +47 -0
- data/spec/support/ssl_test_server.rb +80 -0
- data/spec/support/stub_response.rb +43 -0
- data/website/css/common.css +47 -0
- data/website/index.html +73 -0
- metadata +219 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
module Net
|
5
|
+
module HTTPHeader
|
6
|
+
def digest_auth(username, password, response)
|
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']
|
17
|
+
end
|
18
|
+
|
19
|
+
def append_cookies(authenticator)
|
20
|
+
cookies = @header['cookie'] ? @header['cookie'] : []
|
21
|
+
cookies.concat(authenticator.cookie_header)
|
22
|
+
end
|
23
|
+
|
24
|
+
class DigestAuthenticator
|
25
|
+
def initialize(username, password, method, path, response_header)
|
26
|
+
@username = username
|
27
|
+
@password = password
|
28
|
+
@method = method
|
29
|
+
@path = path
|
30
|
+
@response = parse(response_header)
|
31
|
+
@cookies = parse_cookies(response_header)
|
32
|
+
end
|
33
|
+
|
34
|
+
def authorization_header
|
35
|
+
@cnonce = md5(random)
|
36
|
+
header = [
|
37
|
+
%(Digest username="#{@username}"),
|
38
|
+
%(realm="#{@response['realm']}"),
|
39
|
+
%(nonce="#{@response['nonce']}"),
|
40
|
+
%(uri="#{@path}"),
|
41
|
+
%(response="#{request_digest}")
|
42
|
+
]
|
43
|
+
|
44
|
+
if qop_present?
|
45
|
+
fields = [
|
46
|
+
%(cnonce="#{@cnonce}"),
|
47
|
+
%(qop="#{@response['qop']}"),
|
48
|
+
"nc=00000001"
|
49
|
+
]
|
50
|
+
fields.each { |field| header << field }
|
51
|
+
end
|
52
|
+
|
53
|
+
header << %(opaque="#{@response['opaque']}") if opaque_present?
|
54
|
+
header
|
55
|
+
end
|
56
|
+
|
57
|
+
def cookie_header
|
58
|
+
@cookies
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def parse(response_header)
|
64
|
+
header = response_header['www-authenticate']
|
65
|
+
.gsub(/qop=(auth(?:-int)?)/, 'qop="\\1"')
|
66
|
+
|
67
|
+
header =~ /Digest (.*)/
|
68
|
+
params = {}
|
69
|
+
$1.gsub(/(\w+)="(.*?)"/) { params[$1] = $2 }
|
70
|
+
params
|
71
|
+
end
|
72
|
+
|
73
|
+
def parse_cookies(response_header)
|
74
|
+
return [] unless response_header['Set-Cookie']
|
75
|
+
|
76
|
+
cookies = response_header['Set-Cookie'].split('; ')
|
77
|
+
|
78
|
+
cookies.reduce([]) do |ret, cookie|
|
79
|
+
ret << cookie
|
80
|
+
ret
|
81
|
+
end
|
82
|
+
|
83
|
+
cookies
|
84
|
+
end
|
85
|
+
|
86
|
+
def opaque_present?
|
87
|
+
@response.key?('opaque') && !@response['opaque'].empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
def qop_present?
|
91
|
+
@response.key?('qop') && !@response['qop'].empty?
|
92
|
+
end
|
93
|
+
|
94
|
+
def random
|
95
|
+
format "%x", (Time.now.to_i + rand(65535))
|
96
|
+
end
|
97
|
+
|
98
|
+
def request_digest
|
99
|
+
a = [md5(a1), @response['nonce'], md5(a2)]
|
100
|
+
a.insert(2, "00000001", @cnonce, @response['qop']) if qop_present?
|
101
|
+
md5(a.join(":"))
|
102
|
+
end
|
103
|
+
|
104
|
+
def md5(str)
|
105
|
+
Digest::MD5.hexdigest(str)
|
106
|
+
end
|
107
|
+
|
108
|
+
def a1
|
109
|
+
[@username, @response['realm'], @password].join(":")
|
110
|
+
end
|
111
|
+
|
112
|
+
def a2
|
113
|
+
[@method, @path].join(":")
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
module HTTParty
|
2
|
+
# The default parser used by HTTParty, supports xml, json, html, csv and
|
3
|
+
# plain text.
|
4
|
+
#
|
5
|
+
# == Custom Parsers
|
6
|
+
#
|
7
|
+
# If you'd like to do your own custom parsing, subclassing HTTParty::Parser
|
8
|
+
# will make that process much easier. There are a few different ways you can
|
9
|
+
# utilize HTTParty::Parser as a superclass.
|
10
|
+
#
|
11
|
+
# @example Intercept the parsing for all formats
|
12
|
+
# class SimpleParser < HTTParty::Parser
|
13
|
+
# def parse
|
14
|
+
# perform_parsing
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# @example Add the atom format and parsing method to the default parser
|
19
|
+
# class AtomParsingIncluded < HTTParty::Parser
|
20
|
+
# SupportedFormats.merge!(
|
21
|
+
# {"application/atom+xml" => :atom}
|
22
|
+
# )
|
23
|
+
#
|
24
|
+
# def atom
|
25
|
+
# perform_atom_parsing
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# @example Only support the atom format
|
30
|
+
# class ParseOnlyAtom < HTTParty::Parser
|
31
|
+
# SupportedFormats = {"application/atom+xml" => :atom}
|
32
|
+
#
|
33
|
+
# def atom
|
34
|
+
# perform_atom_parsing
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# @abstract Read the Custom Parsers section for more information.
|
39
|
+
class Parser
|
40
|
+
SupportedFormats = {
|
41
|
+
'text/xml' => :xml,
|
42
|
+
'application/xml' => :xml,
|
43
|
+
'application/json' => :json,
|
44
|
+
'text/json' => :json,
|
45
|
+
'application/javascript' => :plain,
|
46
|
+
'text/javascript' => :plain,
|
47
|
+
'text/html' => :html,
|
48
|
+
'text/plain' => :plain,
|
49
|
+
'text/csv' => :csv,
|
50
|
+
'application/csv' => :csv,
|
51
|
+
'text/comma-separated-values' => :csv
|
52
|
+
}
|
53
|
+
|
54
|
+
# The response body of the request
|
55
|
+
# @return [String]
|
56
|
+
attr_reader :body
|
57
|
+
|
58
|
+
# The intended parsing format for the request
|
59
|
+
# @return [Symbol] e.g. :json
|
60
|
+
attr_reader :format
|
61
|
+
|
62
|
+
# Instantiate the parser and call {#parse}.
|
63
|
+
# @param [String] body the response body
|
64
|
+
# @param [Symbol] format the response format
|
65
|
+
# @return parsed response
|
66
|
+
def self.call(body, format)
|
67
|
+
new(body, format).parse
|
68
|
+
end
|
69
|
+
|
70
|
+
# @return [Hash] the SupportedFormats hash
|
71
|
+
def self.formats
|
72
|
+
const_get(:SupportedFormats)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param [String] mimetype response MIME type
|
76
|
+
# @return [Symbol]
|
77
|
+
# @return [nil] mime type not supported
|
78
|
+
def self.format_from_mimetype(mimetype)
|
79
|
+
formats[formats.keys.detect {|k| mimetype.include?(k)}]
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Array<Symbol>] list of supported formats
|
83
|
+
def self.supported_formats
|
84
|
+
formats.values.uniq
|
85
|
+
end
|
86
|
+
|
87
|
+
# @param [Symbol] format e.g. :json, :xml
|
88
|
+
# @return [Boolean]
|
89
|
+
def self.supports_format?(format)
|
90
|
+
supported_formats.include?(format)
|
91
|
+
end
|
92
|
+
|
93
|
+
def initialize(body, format)
|
94
|
+
@body = body
|
95
|
+
@format = format
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [Object] the parsed body
|
99
|
+
# @return [nil] when the response body is nil, an empty string, spaces only or "null"
|
100
|
+
def parse
|
101
|
+
return nil if body.nil? || body.strip.empty? || body == "null"
|
102
|
+
if supports_format?
|
103
|
+
parse_supported_format
|
104
|
+
else
|
105
|
+
body
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
|
111
|
+
def xml
|
112
|
+
MultiXml.parse(body)
|
113
|
+
end
|
114
|
+
|
115
|
+
def json
|
116
|
+
JSON.parse(body, :quirks_mode => true, :allow_nan => true)
|
117
|
+
end
|
118
|
+
|
119
|
+
def csv
|
120
|
+
CSV.parse(body)
|
121
|
+
end
|
122
|
+
|
123
|
+
def html
|
124
|
+
body
|
125
|
+
end
|
126
|
+
|
127
|
+
def plain
|
128
|
+
body
|
129
|
+
end
|
130
|
+
|
131
|
+
def supports_format?
|
132
|
+
self.class.supports_format?(format)
|
133
|
+
end
|
134
|
+
|
135
|
+
def parse_supported_format
|
136
|
+
send(format)
|
137
|
+
rescue NoMethodError => e
|
138
|
+
raise NotImplementedError, "#{self.class.name} has not implemented a parsing method for the #{format.inspect} format.", e.backtrace
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,361 @@
|
|
1
|
+
module HTTParty
|
2
|
+
class Request #:nodoc:
|
3
|
+
SupportedHTTPMethods = [
|
4
|
+
Net::HTTP::Get,
|
5
|
+
Net::HTTP::Post,
|
6
|
+
Net::HTTP::Patch,
|
7
|
+
Net::HTTP::Put,
|
8
|
+
Net::HTTP::Delete,
|
9
|
+
Net::HTTP::Head,
|
10
|
+
Net::HTTP::Options,
|
11
|
+
Net::HTTP::Move,
|
12
|
+
Net::HTTP::Copy
|
13
|
+
]
|
14
|
+
|
15
|
+
SupportedURISchemes = ['http', 'https', 'webcal', nil]
|
16
|
+
|
17
|
+
NON_RAILS_QUERY_STRING_NORMALIZER = proc do |query|
|
18
|
+
Array(query).sort_by { |a| a[0].to_s }.map do |key, value|
|
19
|
+
if value.nil?
|
20
|
+
key.to_s
|
21
|
+
elsif value.respond_to?(:to_ary)
|
22
|
+
value.to_ary.map {|v| "#{key}=#{ERB::Util.url_encode(v.to_s)}"}
|
23
|
+
else
|
24
|
+
HashConversions.to_params(key => value)
|
25
|
+
end
|
26
|
+
end.flatten.join('&')
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_accessor :http_method, :options, :last_response, :redirect, :last_uri
|
30
|
+
attr_reader :path
|
31
|
+
|
32
|
+
def initialize(http_method, path, o = {})
|
33
|
+
self.http_method = http_method
|
34
|
+
self.options = {
|
35
|
+
limit: o.delete(:no_follow) ? 1 : 5,
|
36
|
+
assume_utf16_is_big_endian: true,
|
37
|
+
default_params: {},
|
38
|
+
follow_redirects: true,
|
39
|
+
parser: Parser,
|
40
|
+
uri_adapter: URI,
|
41
|
+
connection_adapter: ConnectionAdapter
|
42
|
+
}.merge(o)
|
43
|
+
self.path = path
|
44
|
+
set_basic_auth_from_uri
|
45
|
+
end
|
46
|
+
|
47
|
+
def path=(uri)
|
48
|
+
uri_adapter = options[:uri_adapter]
|
49
|
+
|
50
|
+
@path = if uri.is_a?(uri_adapter)
|
51
|
+
uri
|
52
|
+
elsif String.try_convert(uri)
|
53
|
+
uri_adapter.parse uri
|
54
|
+
else
|
55
|
+
raise ArgumentError,
|
56
|
+
"bad argument (expected #{uri_adapter} object or URI string)"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def request_uri(uri)
|
61
|
+
if uri.respond_to? :request_uri
|
62
|
+
uri.request_uri
|
63
|
+
else
|
64
|
+
uri.path
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def uri
|
69
|
+
if redirect && path.relative? && path.path[0] != "/"
|
70
|
+
last_uri_host = @last_uri.path.gsub(/[^\/]+$/, "")
|
71
|
+
|
72
|
+
path.path = "/#{path.path}" if last_uri_host[-1] != "/"
|
73
|
+
path.path = last_uri_host + path.path
|
74
|
+
end
|
75
|
+
|
76
|
+
new_uri = path.relative? ? options[:uri_adapter].parse("#{base_uri}#{path}") : path.clone
|
77
|
+
|
78
|
+
# avoid double query string on redirects [#12]
|
79
|
+
unless redirect
|
80
|
+
new_uri.query = query_string(new_uri)
|
81
|
+
end
|
82
|
+
|
83
|
+
unless SupportedURISchemes.include? new_uri.scheme
|
84
|
+
raise UnsupportedURIScheme, "'#{new_uri}' Must be HTTP, HTTPS or Generic"
|
85
|
+
end
|
86
|
+
|
87
|
+
@last_uri = new_uri
|
88
|
+
end
|
89
|
+
|
90
|
+
def base_uri
|
91
|
+
redirect ? "#{@last_uri.scheme}://#{@last_uri.host}" : options[:base_uri]
|
92
|
+
end
|
93
|
+
|
94
|
+
def format
|
95
|
+
options[:format] || (format_from_mimetype(last_response['content-type']) if last_response)
|
96
|
+
end
|
97
|
+
|
98
|
+
def parser
|
99
|
+
options[:parser]
|
100
|
+
end
|
101
|
+
|
102
|
+
def connection_adapter
|
103
|
+
options[:connection_adapter]
|
104
|
+
end
|
105
|
+
|
106
|
+
def perform(&block)
|
107
|
+
validate
|
108
|
+
setup_raw_request
|
109
|
+
chunked_body = nil
|
110
|
+
|
111
|
+
self.last_response = http.request(@raw_request) do |http_response|
|
112
|
+
if block
|
113
|
+
chunks = []
|
114
|
+
|
115
|
+
http_response.read_body do |fragment|
|
116
|
+
chunks << fragment unless options[:stream_body]
|
117
|
+
block.call(fragment)
|
118
|
+
end
|
119
|
+
|
120
|
+
chunked_body = chunks.join
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
handle_deflation unless http_method == Net::HTTP::Head
|
125
|
+
handle_response(chunked_body, &block)
|
126
|
+
end
|
127
|
+
|
128
|
+
def raw_body
|
129
|
+
@raw_request.body
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def http
|
135
|
+
connection_adapter.call(uri, options)
|
136
|
+
end
|
137
|
+
|
138
|
+
def body
|
139
|
+
options[:body].respond_to?(:to_hash) ? normalize_query(options[:body]) : options[:body]
|
140
|
+
end
|
141
|
+
|
142
|
+
def credentials
|
143
|
+
(options[:basic_auth] || options[:digest_auth]).to_hash
|
144
|
+
end
|
145
|
+
|
146
|
+
def username
|
147
|
+
credentials[:username]
|
148
|
+
end
|
149
|
+
|
150
|
+
def password
|
151
|
+
credentials[:password]
|
152
|
+
end
|
153
|
+
|
154
|
+
def normalize_query(query)
|
155
|
+
if query_string_normalizer
|
156
|
+
query_string_normalizer.call(query)
|
157
|
+
else
|
158
|
+
HashConversions.to_params(query)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def query_string_normalizer
|
163
|
+
options[:query_string_normalizer]
|
164
|
+
end
|
165
|
+
|
166
|
+
def setup_raw_request
|
167
|
+
@raw_request = http_method.new(request_uri(uri))
|
168
|
+
@raw_request.body = body if body
|
169
|
+
@raw_request.body_stream = options[:body_stream] if options[:body_stream]
|
170
|
+
@raw_request.initialize_http_header(options[:headers].to_hash) if options[:headers].respond_to?(:to_hash)
|
171
|
+
@raw_request.basic_auth(username, password) if options[:basic_auth]
|
172
|
+
setup_digest_auth if options[:digest_auth]
|
173
|
+
end
|
174
|
+
|
175
|
+
def setup_digest_auth
|
176
|
+
auth_request = http_method.new(uri.request_uri)
|
177
|
+
auth_request.initialize_http_header(options[:headers].to_hash) if options[:headers].respond_to?(:to_hash)
|
178
|
+
res = http.request(auth_request)
|
179
|
+
|
180
|
+
if !res['www-authenticate'].nil? && res['www-authenticate'].length > 0
|
181
|
+
@raw_request.digest_auth(username, password, res)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def query_string(uri)
|
186
|
+
query_string_parts = []
|
187
|
+
query_string_parts << uri.query unless uri.query.nil?
|
188
|
+
|
189
|
+
if options[:query].respond_to?(:to_hash)
|
190
|
+
query_string_parts << normalize_query(options[:default_params].merge(options[:query].to_hash))
|
191
|
+
else
|
192
|
+
query_string_parts << normalize_query(options[:default_params]) unless options[:default_params].empty?
|
193
|
+
query_string_parts << options[:query] unless options[:query].nil?
|
194
|
+
end
|
195
|
+
|
196
|
+
query_string_parts.reject!(&:empty?) unless query_string_parts == [""]
|
197
|
+
query_string_parts.size > 0 ? query_string_parts.join('&') : nil
|
198
|
+
end
|
199
|
+
|
200
|
+
def get_charset
|
201
|
+
content_type = last_response["content-type"]
|
202
|
+
if content_type.nil?
|
203
|
+
return nil
|
204
|
+
end
|
205
|
+
|
206
|
+
if content_type =~ /;\s*charset\s*=\s*([^=,;"\s]+)/i
|
207
|
+
return $1
|
208
|
+
end
|
209
|
+
|
210
|
+
if content_type =~ /;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i
|
211
|
+
return $1.gsub(/\\(.)/, '\1')
|
212
|
+
end
|
213
|
+
|
214
|
+
nil
|
215
|
+
end
|
216
|
+
|
217
|
+
def encode_with_ruby_encoding(body, charset)
|
218
|
+
encoding = Encoding.find(charset)
|
219
|
+
body.force_encoding(encoding)
|
220
|
+
rescue
|
221
|
+
body
|
222
|
+
end
|
223
|
+
|
224
|
+
def assume_utf16_is_big_endian
|
225
|
+
options[:assume_utf16_is_big_endian]
|
226
|
+
end
|
227
|
+
|
228
|
+
def encode_utf_16(body)
|
229
|
+
if body.bytesize >= 2
|
230
|
+
if body.getbyte(0) == 0xFF && body.getbyte(1) == 0xFE
|
231
|
+
return body.force_encoding("UTF-16LE")
|
232
|
+
elsif body.getbyte(0) == 0xFE && body.getbyte(1) == 0xFF
|
233
|
+
return body.force_encoding("UTF-16BE")
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
if assume_utf16_is_big_endian
|
238
|
+
body.force_encoding("UTF-16BE")
|
239
|
+
else
|
240
|
+
body.force_encoding("UTF-16LE")
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def _encode_body(body)
|
245
|
+
charset = get_charset
|
246
|
+
|
247
|
+
if charset.nil?
|
248
|
+
return body
|
249
|
+
end
|
250
|
+
|
251
|
+
if "utf-16".casecmp(charset) == 0
|
252
|
+
encode_utf_16(body)
|
253
|
+
else
|
254
|
+
encode_with_ruby_encoding(body, charset)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
def encode_body(body)
|
259
|
+
if "".respond_to?(:encoding)
|
260
|
+
_encode_body(body)
|
261
|
+
else
|
262
|
+
body
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def handle_response(body, &block)
|
267
|
+
if response_redirects?
|
268
|
+
options[:limit] -= 1
|
269
|
+
if options[:logger]
|
270
|
+
logger = HTTParty::Logger.build(options[:logger], options[:log_level], options[:log_format])
|
271
|
+
logger.format(self, last_response)
|
272
|
+
end
|
273
|
+
self.path = last_response['location']
|
274
|
+
self.redirect = true
|
275
|
+
if last_response.class == Net::HTTPSeeOther
|
276
|
+
unless options[:maintain_method_across_redirects] && options[:resend_on_redirect]
|
277
|
+
self.http_method = Net::HTTP::Get
|
278
|
+
end
|
279
|
+
else
|
280
|
+
unless options[:maintain_method_across_redirects]
|
281
|
+
self.http_method = Net::HTTP::Get
|
282
|
+
end
|
283
|
+
end
|
284
|
+
capture_cookies(last_response)
|
285
|
+
perform(&block)
|
286
|
+
else
|
287
|
+
body ||= last_response.body
|
288
|
+
body = encode_body(body)
|
289
|
+
Response.new(self, last_response, lambda { parse_response(body) }, body: body)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# Inspired by Ruby 1.9
|
294
|
+
def handle_deflation
|
295
|
+
case last_response["content-encoding"]
|
296
|
+
when "gzip", "x-gzip"
|
297
|
+
body_io = StringIO.new(last_response.body)
|
298
|
+
last_response.body.replace Zlib::GzipReader.new(body_io).read
|
299
|
+
last_response.delete('content-encoding')
|
300
|
+
when "deflate"
|
301
|
+
last_response.body.replace Zlib::Inflate.inflate(last_response.body)
|
302
|
+
last_response.delete('content-encoding')
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def response_redirects?
|
307
|
+
case last_response
|
308
|
+
when Net::HTTPMultipleChoice, # 300
|
309
|
+
Net::HTTPMovedPermanently, # 301
|
310
|
+
Net::HTTPFound, # 302
|
311
|
+
Net::HTTPSeeOther, # 303
|
312
|
+
Net::HTTPUseProxy, # 305
|
313
|
+
Net::HTTPTemporaryRedirect
|
314
|
+
options[:follow_redirects] && last_response.key?('location')
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
def parse_response(body)
|
319
|
+
parser.call(body, format)
|
320
|
+
end
|
321
|
+
|
322
|
+
def capture_cookies(response)
|
323
|
+
return unless response['Set-Cookie']
|
324
|
+
cookies_hash = HTTParty::CookieHash.new
|
325
|
+
cookies_hash.add_cookies(options[:headers].to_hash['Cookie']) if options[:headers] && options[:headers].to_hash['Cookie']
|
326
|
+
response.get_fields('Set-Cookie').each { |cookie| cookies_hash.add_cookies(cookie) }
|
327
|
+
options[:headers] ||= {}
|
328
|
+
options[:headers]['Cookie'] = cookies_hash.to_cookie_string
|
329
|
+
end
|
330
|
+
|
331
|
+
# Uses the HTTP Content-Type header to determine the format of the
|
332
|
+
# response It compares the MIME type returned to the types stored in the
|
333
|
+
# SupportedFormats hash
|
334
|
+
def format_from_mimetype(mimetype)
|
335
|
+
if mimetype && parser.respond_to?(:format_from_mimetype)
|
336
|
+
parser.format_from_mimetype(mimetype)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
def validate
|
341
|
+
raise HTTParty::RedirectionTooDeep.new(last_response), 'HTTP redirects too deep' if options[:limit].to_i <= 0
|
342
|
+
raise ArgumentError, 'only get, post, patch, put, delete, head, and options methods are supported' unless SupportedHTTPMethods.include?(http_method)
|
343
|
+
raise ArgumentError, ':headers must be a hash' if options[:headers] && !options[:headers].respond_to?(:to_hash)
|
344
|
+
raise ArgumentError, 'only one authentication method, :basic_auth or :digest_auth may be used at a time' if options[:basic_auth] && options[:digest_auth]
|
345
|
+
raise ArgumentError, ':basic_auth must be a hash' if options[:basic_auth] && !options[:basic_auth].respond_to?(:to_hash)
|
346
|
+
raise ArgumentError, ':digest_auth must be a hash' if options[:digest_auth] && !options[:digest_auth].respond_to?(:to_hash)
|
347
|
+
raise ArgumentError, ':query must be hash if using HTTP Post' if post? && !options[:query].nil? && !options[:query].respond_to?(:to_hash)
|
348
|
+
end
|
349
|
+
|
350
|
+
def post?
|
351
|
+
Net::HTTP::Post == http_method
|
352
|
+
end
|
353
|
+
|
354
|
+
def set_basic_auth_from_uri
|
355
|
+
if path.userinfo
|
356
|
+
username, password = path.userinfo.split(':')
|
357
|
+
options[:basic_auth] = {username: username, password: password}
|
358
|
+
end
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|