httparty-responsibly 0.17.0.r1
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/.editorconfig +18 -0
- data/.gitignore +13 -0
- data/.rubocop.yml +92 -0
- data/.rubocop_todo.yml +124 -0
- data/.simplecov +1 -0
- data/.travis.yml +11 -0
- data/CONTRIBUTING.md +23 -0
- data/Changelog.md +502 -0
- data/Gemfile +23 -0
- data/Guardfile +16 -0
- data/MIT-LICENSE +20 -0
- data/README.md +78 -0
- data/Rakefile +10 -0
- data/bin/httparty +123 -0
- data/cucumber.yml +1 -0
- data/docs/README.md +106 -0
- data/examples/README.md +86 -0
- data/examples/aaws.rb +32 -0
- data/examples/basic.rb +28 -0
- data/examples/body_stream.rb +14 -0
- data/examples/crack.rb +19 -0
- data/examples/custom_parsers.rb +68 -0
- data/examples/delicious.rb +37 -0
- data/examples/google.rb +16 -0
- data/examples/headers_and_user_agents.rb +10 -0
- data/examples/logging.rb +36 -0
- data/examples/microsoft_graph.rb +52 -0
- data/examples/multipart.rb +22 -0
- data/examples/nokogiri_html_parser.rb +19 -0
- data/examples/peer_cert.rb +9 -0
- data/examples/rescue_json.rb +17 -0
- data/examples/rubyurl.rb +14 -0
- data/examples/stackexchange.rb +24 -0
- data/examples/stream_download.rb +26 -0
- data/examples/tripit_sign_in.rb +44 -0
- data/examples/twitter.rb +31 -0
- data/examples/whoismyrep.rb +10 -0
- data/httparty-responsibly.gemspec +27 -0
- data/lib/httparty.rb +684 -0
- data/lib/httparty/connection_adapter.rb +244 -0
- data/lib/httparty/cookie_hash.rb +21 -0
- data/lib/httparty/exceptions.rb +33 -0
- data/lib/httparty/hash_conversions.rb +69 -0
- data/lib/httparty/logger/apache_formatter.rb +45 -0
- data/lib/httparty/logger/curl_formatter.rb +91 -0
- data/lib/httparty/logger/logger.rb +28 -0
- data/lib/httparty/logger/logstash_formatter.rb +59 -0
- data/lib/httparty/module_inheritable_attributes.rb +56 -0
- data/lib/httparty/net_digest_auth.rb +136 -0
- data/lib/httparty/parser.rb +150 -0
- data/lib/httparty/request.rb +386 -0
- data/lib/httparty/request/body.rb +84 -0
- data/lib/httparty/request/multipart_boundary.rb +11 -0
- data/lib/httparty/response.rb +140 -0
- data/lib/httparty/response/headers.rb +33 -0
- data/lib/httparty/response_fragment.rb +19 -0
- data/lib/httparty/text_encoder.rb +70 -0
- data/lib/httparty/utils.rb +11 -0
- data/lib/httparty/version.rb +3 -0
- data/script/release +42 -0
- data/website/css/common.css +47 -0
- data/website/index.html +73 -0
- metadata +138 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
require_relative 'multipart_boundary'
|
2
|
+
|
3
|
+
module HTTParty
|
4
|
+
class Request
|
5
|
+
class Body
|
6
|
+
def initialize(params, query_string_normalizer: nil, force_multipart: false)
|
7
|
+
@params = params
|
8
|
+
@query_string_normalizer = query_string_normalizer
|
9
|
+
@force_multipart = force_multipart
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
if params.respond_to?(:to_hash)
|
14
|
+
multipart? ? generate_multipart : normalize_query(params)
|
15
|
+
else
|
16
|
+
params
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def boundary
|
21
|
+
@boundary ||= MultipartBoundary.generate
|
22
|
+
end
|
23
|
+
|
24
|
+
def multipart?
|
25
|
+
params.respond_to?(:to_hash) && (force_multipart || has_file?(params))
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def generate_multipart
|
31
|
+
normalized_params = params.flat_map { |key, value| HashConversions.normalize_keys(key, value) }
|
32
|
+
|
33
|
+
multipart = normalized_params.inject('') do |memo, (key, value)|
|
34
|
+
memo += "--#{boundary}\r\n"
|
35
|
+
memo += %(Content-Disposition: form-data; name="#{key}")
|
36
|
+
# value.path is used to support ActionDispatch::Http::UploadedFile
|
37
|
+
# https://github.com/jnunemaker/httparty/pull/585
|
38
|
+
memo += %(; filename="#{file_name(value)}") if file?(value)
|
39
|
+
memo += "\r\n"
|
40
|
+
memo += "Content-Type: #{content_type(value)}\r\n" if file?(value)
|
41
|
+
memo += "\r\n"
|
42
|
+
memo += file?(value) ? value.read : value.to_s
|
43
|
+
memo += "\r\n"
|
44
|
+
end
|
45
|
+
|
46
|
+
multipart += "--#{boundary}--\r\n"
|
47
|
+
end
|
48
|
+
|
49
|
+
def has_file?(value)
|
50
|
+
if value.respond_to?(:to_hash)
|
51
|
+
value.to_hash.any? { |_, v| has_file?(v) }
|
52
|
+
elsif value.respond_to?(:to_ary)
|
53
|
+
value.to_ary.any? { |v| has_file?(v) }
|
54
|
+
else
|
55
|
+
file?(value)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def file?(object)
|
60
|
+
object.respond_to?(:path) && object.respond_to?(:read)
|
61
|
+
end
|
62
|
+
|
63
|
+
def normalize_query(query)
|
64
|
+
if query_string_normalizer
|
65
|
+
query_string_normalizer.call(query)
|
66
|
+
else
|
67
|
+
HashConversions.to_params(query)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def content_type(object)
|
72
|
+
return object.content_type if object.respond_to?(:content_type)
|
73
|
+
mime = MIME::Types.type_for(object.path)
|
74
|
+
mime.empty? ? 'application/octet-stream' : mime[0].content_type
|
75
|
+
end
|
76
|
+
|
77
|
+
def file_name(object)
|
78
|
+
object.respond_to?(:original_filename) ? object.original_filename : File.basename(object.path)
|
79
|
+
end
|
80
|
+
|
81
|
+
attr_reader :params, :query_string_normalizer, :force_multipart
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module HTTParty
|
2
|
+
class Response < Object
|
3
|
+
def self.underscore(string)
|
4
|
+
string.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
5
|
+
end
|
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
|
+
|
13
|
+
attr_reader :request, :response, :body, :headers
|
14
|
+
|
15
|
+
def initialize(request, response, parsed_block, options = {})
|
16
|
+
@request = request
|
17
|
+
@response = response
|
18
|
+
@body = options[:body] || response.body
|
19
|
+
@parsed_block = parsed_block
|
20
|
+
@headers = Headers.new(response.to_hash)
|
21
|
+
|
22
|
+
if request.options[:logger]
|
23
|
+
logger = ::HTTParty::Logger.build(
|
24
|
+
request.options[:logger],
|
25
|
+
request.options[:log_level],
|
26
|
+
request.options[:log_format]
|
27
|
+
)
|
28
|
+
logger.format(request, self)
|
29
|
+
end
|
30
|
+
|
31
|
+
throw_exception
|
32
|
+
end
|
33
|
+
|
34
|
+
def parsed_response
|
35
|
+
@parsed_response ||= @parsed_block.call
|
36
|
+
end
|
37
|
+
|
38
|
+
def code
|
39
|
+
response.code.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
def http_version
|
43
|
+
response.http_version
|
44
|
+
end
|
45
|
+
|
46
|
+
def tap
|
47
|
+
yield self
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def inspect
|
52
|
+
inspect_id = ::Kernel::format "%x", (object_id * 2)
|
53
|
+
%(#<#{self.class}:0x#{inspect_id} parsed_response=#{parsed_response.inspect}, @response=#{response.inspect}, @headers=#{headers.inspect}>)
|
54
|
+
end
|
55
|
+
|
56
|
+
CODES_TO_OBJ = ::Net::HTTPResponse::CODE_CLASS_TO_OBJ.merge ::Net::HTTPResponse::CODE_TO_OBJ
|
57
|
+
|
58
|
+
CODES_TO_OBJ.each do |response_code, klass|
|
59
|
+
name = klass.name.sub("Net::HTTP", '')
|
60
|
+
name = "#{underscore(name)}?".to_sym
|
61
|
+
|
62
|
+
define_method(name) do
|
63
|
+
klass === response
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Support old multiple_choice? method from pre 2.0.0 era.
|
68
|
+
if ::RUBY_VERSION >= "2.0.0" && ::RUBY_PLATFORM != "java"
|
69
|
+
alias_method :multiple_choice?, :multiple_choices?
|
70
|
+
end
|
71
|
+
|
72
|
+
# Support old status codes method from pre 2.6.0 era.
|
73
|
+
if ::RUBY_VERSION >= "2.6.0" && ::RUBY_PLATFORM != "java"
|
74
|
+
alias_method :gateway_time_out?, :gateway_timeout?
|
75
|
+
alias_method :request_entity_too_large?, :payload_too_large?
|
76
|
+
alias_method :request_time_out?, :request_timeout?
|
77
|
+
alias_method :request_uri_too_long?, :uri_too_long?
|
78
|
+
alias_method :requested_range_not_satisfiable?, :range_not_satisfiable?
|
79
|
+
end
|
80
|
+
|
81
|
+
def nil?
|
82
|
+
response.nil? || response.body.nil? || response.body.empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
if !response.nil? && !response.body.nil? && response.body.respond_to?(:to_s)
|
87
|
+
response.body.to_s
|
88
|
+
else
|
89
|
+
inspect
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def pretty_print(pp)
|
94
|
+
if !parsed_response.nil? && parsed_response.respond_to?(:pretty_print)
|
95
|
+
parsed_response.pretty_print(pp)
|
96
|
+
else
|
97
|
+
super
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def display(port=$>)
|
102
|
+
if !parsed_response.nil? && parsed_response.respond_to?(:display)
|
103
|
+
parsed_response.display(port)
|
104
|
+
elsif !response.nil? && !response.body.nil? && response.body.respond_to?(:display)
|
105
|
+
response.body.display(port)
|
106
|
+
else
|
107
|
+
port.write(inspect)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def respond_to_missing?(name, *args)
|
112
|
+
return true if super
|
113
|
+
parsed_response.respond_to?(name) || response.respond_to?(name)
|
114
|
+
end
|
115
|
+
|
116
|
+
def _dump(_level)
|
117
|
+
Marshal.dump([request, response, parsed_response, body])
|
118
|
+
end
|
119
|
+
|
120
|
+
protected
|
121
|
+
|
122
|
+
def method_missing(name, *args, &block)
|
123
|
+
if parsed_response.respond_to?(name)
|
124
|
+
parsed_response.send(name, *args, &block)
|
125
|
+
elsif response.respond_to?(name)
|
126
|
+
response.send(name, *args, &block)
|
127
|
+
else
|
128
|
+
super
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def throw_exception
|
133
|
+
if @request.options[:raise_on] && @request.options[:raise_on].include?(code)
|
134
|
+
::Kernel.raise ::HTTParty::ResponseError.new(@response), "Code #{code} - #{body}"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
require 'httparty/response/headers'
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module HTTParty
|
4
|
+
class Response #:nodoc:
|
5
|
+
class Headers < ::SimpleDelegator
|
6
|
+
include ::Net::HTTPHeader
|
7
|
+
|
8
|
+
def initialize(header_values = nil)
|
9
|
+
@header = {}
|
10
|
+
if header_values
|
11
|
+
header_values.each_pair do |k,v|
|
12
|
+
if v.is_a?(Array)
|
13
|
+
v.each do |sub_v|
|
14
|
+
add_field(k, sub_v)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
add_field(k, v)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
super(@header)
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
if other.is_a?(::Net::HTTPHeader)
|
26
|
+
@header == other.instance_variable_get(:@header)
|
27
|
+
elsif other.is_a?(Hash)
|
28
|
+
@header == other || @header == Headers.new(other).instance_variable_get(:@header)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module HTTParty
|
4
|
+
# Allow access to http_response and code by delegation on fragment
|
5
|
+
class ResponseFragment < SimpleDelegator
|
6
|
+
attr_reader :http_response, :connection
|
7
|
+
|
8
|
+
def code
|
9
|
+
@http_response.code.to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(fragment, http_response, connection)
|
13
|
+
@fragment = fragment
|
14
|
+
@http_response = http_response
|
15
|
+
@connection = connection
|
16
|
+
super fragment
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module HTTParty
|
2
|
+
class TextEncoder
|
3
|
+
attr_reader :text, :content_type, :assume_utf16_is_big_endian
|
4
|
+
|
5
|
+
def initialize(text, assume_utf16_is_big_endian: true, content_type: nil)
|
6
|
+
@text = text.dup
|
7
|
+
@content_type = content_type
|
8
|
+
@assume_utf16_is_big_endian = assume_utf16_is_big_endian
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
if can_encode?
|
13
|
+
encoded_text
|
14
|
+
else
|
15
|
+
text
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def can_encode?
|
22
|
+
''.respond_to?(:encoding) && charset
|
23
|
+
end
|
24
|
+
|
25
|
+
def encoded_text
|
26
|
+
if 'utf-16'.casecmp(charset) == 0
|
27
|
+
encode_utf_16
|
28
|
+
else
|
29
|
+
encode_with_ruby_encoding
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def encode_utf_16
|
34
|
+
if text.bytesize >= 2
|
35
|
+
if text.getbyte(0) == 0xFF && text.getbyte(1) == 0xFE
|
36
|
+
return text.force_encoding("UTF-16LE")
|
37
|
+
elsif text.getbyte(0) == 0xFE && text.getbyte(1) == 0xFF
|
38
|
+
return text.force_encoding("UTF-16BE")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
if assume_utf16_is_big_endian # option
|
43
|
+
text.force_encoding("UTF-16BE")
|
44
|
+
else
|
45
|
+
text.force_encoding("UTF-16LE")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def encode_with_ruby_encoding
|
50
|
+
# NOTE: This will raise an argument error if the
|
51
|
+
# charset does not exist
|
52
|
+
encoding = Encoding.find(charset)
|
53
|
+
text.force_encoding(encoding.to_s)
|
54
|
+
rescue ArgumentError
|
55
|
+
text
|
56
|
+
end
|
57
|
+
|
58
|
+
def charset
|
59
|
+
return nil if content_type.nil?
|
60
|
+
|
61
|
+
if (matchdata = content_type.match(/;\s*charset\s*=\s*([^=,;"\s]+)/i))
|
62
|
+
return matchdata.captures.first
|
63
|
+
end
|
64
|
+
|
65
|
+
if (matchdata = content_type.match(/;\s*charset\s*=\s*"((\\.|[^\\"])+)"/i))
|
66
|
+
return matchdata.captures.first.gsub(/\\(.)/, '\1')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/script/release
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
#/ Usage: release
|
3
|
+
#/
|
4
|
+
#/ Tag the version in the repo and push the gem.
|
5
|
+
#/
|
6
|
+
|
7
|
+
set -e
|
8
|
+
cd $(dirname "$0")/..
|
9
|
+
|
10
|
+
[ "$1" = "--help" -o "$1" = "-h" -o "$1" = "help" ] && {
|
11
|
+
grep '^#/' <"$0"| cut -c4-
|
12
|
+
exit 0
|
13
|
+
}
|
14
|
+
|
15
|
+
gem_name=httparty
|
16
|
+
|
17
|
+
# Build a new gem archive.
|
18
|
+
rm -rf $gem_name-*.gem
|
19
|
+
gem build -q $gem_name.gemspec
|
20
|
+
|
21
|
+
# Make sure we're on the master branch.
|
22
|
+
(git branch | grep -q '* master') || {
|
23
|
+
echo "Only release from the master branch."
|
24
|
+
exit 1
|
25
|
+
}
|
26
|
+
|
27
|
+
# Figure out what version we're releasing.
|
28
|
+
tag=v`ls $gem_name-*.gem | sed "s/^$gem_name-\(.*\)\.gem$/\1/"`
|
29
|
+
|
30
|
+
echo "Releasing $tag"
|
31
|
+
|
32
|
+
# Make sure we haven't released this version before.
|
33
|
+
git fetch -t origin
|
34
|
+
|
35
|
+
(git tag -l | grep -q "$tag") && {
|
36
|
+
echo "Whoops, there's already a '${tag}' tag."
|
37
|
+
exit 1
|
38
|
+
}
|
39
|
+
|
40
|
+
# Tag it and bag it.
|
41
|
+
gem push $gem_name-*.gem && git tag "$tag" &&
|
42
|
+
git push origin master && git push origin "$tag"
|
@@ -0,0 +1,47 @@
|
|
1
|
+
@media screen, projection {
|
2
|
+
/*
|
3
|
+
Copyright (c) 2007, Yahoo! Inc. All rights reserved.
|
4
|
+
Code licensed under the BSD License:
|
5
|
+
http://developer.yahoo.net/yui/license.txt
|
6
|
+
version: 2.2.0
|
7
|
+
*/
|
8
|
+
body {font:13px arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small;}table {font-size:inherit;font:100%;}select, input, textarea {font:99% arial,helvetica,clean,sans-serif;}pre, code {font:115% monospace;*font-size:100%;}body * {line-height:1.22em;}
|
9
|
+
body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var{font-style:normal;font-weight:normal;}/*ol,ul {list-style:none;}*/caption,th {text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym {border:0;}
|
10
|
+
/* end of yahoo reset and fonts */
|
11
|
+
|
12
|
+
body {color:#333; background:#4b1a1a; line-height:1.3;}
|
13
|
+
p {margin:0 0 20px;}
|
14
|
+
a {color:#4b1a1a;}
|
15
|
+
a:hover {text-decoration:none;}
|
16
|
+
strong {font-weight:bold;}
|
17
|
+
em {font-style:italics;}
|
18
|
+
h1,h2,h3,h4,h5,h6 {font-weight:bold;}
|
19
|
+
h1 {font-size:197%; margin:30px 0; color:#4b1a1a;}
|
20
|
+
h2 {font-size:174%; margin:20px 0; color:#b8111a;}
|
21
|
+
h3 {font-size:152%; margin:10px 0;}
|
22
|
+
h4 {font-size:129%; margin:10px 0;}
|
23
|
+
pre {background:#eee; margin:0 0 20px; padding:20px; border:1px solid #ccc; font-size:100%; overflow:auto;}
|
24
|
+
code {font-size:100%; margin:0; padding:0;}
|
25
|
+
ul, ol {margin:10px 0 10px 25px;}
|
26
|
+
ol li {margin:0 0 10px;}
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
|
32
|
+
div#wrapper {background:#fff; width:560px; margin:0 auto; padding:20px; border:10px solid #bc8c46; border-width:0 10px;}
|
33
|
+
div#header {position:relative; border-bottom:1px dotted; margin:0 0 10px; padding:0 0 10px;}
|
34
|
+
div#header p {margin:0; padding:0;}
|
35
|
+
div#header h1 {margin:0; padding:0;}
|
36
|
+
ul#nav {position:absolute; top:0; right:0; list-style:none; margin:0; padding:0;}
|
37
|
+
ul#nav li {display:inline; padding:0 0 0 5px;}
|
38
|
+
ul#nav li a {}
|
39
|
+
div#content {}
|
40
|
+
div#footer {margin:40px 0 0; border-top:1px dotted; padding:10px 0 0;}
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
}
|