httparty-responsibly 0.17.1
Sign up to get free protection for your applications and to get access to all the features.
- 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 +509 -0
- data/Gemfile +24 -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 +668 -0
- data/lib/httparty/connection_adapter.rb +254 -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/headers_processor.rb +30 -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,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
|
+
}
|
data/website/index.html
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
2
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
5
|
+
<title>HTTParty by John Nunemaker</title>
|
6
|
+
<link rel="stylesheet" href="css/common.css" type="text/css" />
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
|
10
|
+
<div id="wrapper">
|
11
|
+
<div id="header">
|
12
|
+
<h1>HTTParty</h1>
|
13
|
+
<p>Tonight we're gonna HTTParty like it's 1999!</p>
|
14
|
+
|
15
|
+
<ul id="nav">
|
16
|
+
<li><a href="rdoc/">Docs</a></li>
|
17
|
+
<li><a href="http://github.com/jnunemaker/httparty">Github</a></li>
|
18
|
+
<li><a href="http://rubyforge.org/projects/httparty/">Rubyforge</a></li>
|
19
|
+
</ul>
|
20
|
+
</div>
|
21
|
+
|
22
|
+
<div id="content">
|
23
|
+
<h2>Install</h2>
|
24
|
+
<pre><code>$ sudo gem install httparty</code></pre>
|
25
|
+
|
26
|
+
<h2>Some Quick Examples</h2>
|
27
|
+
|
28
|
+
<p>The following is a simple example of wrapping Twitter's API for posting updates.</p>
|
29
|
+
|
30
|
+
<pre><code>class Twitter
|
31
|
+
include HTTParty
|
32
|
+
base_uri 'twitter.com'
|
33
|
+
basic_auth 'username', 'password'
|
34
|
+
end
|
35
|
+
|
36
|
+
Twitter.post('/statuses/update.json', query: {status: "It's an HTTParty and everyone is invited!"})</code></pre>
|
37
|
+
|
38
|
+
<p>That is really it! The object returned is a ruby hash that is decoded from Twitter's json response. JSON parsing is used because of the .json extension in the path of the request. You can also explicitly set a format (see the examples). </p>
|
39
|
+
|
40
|
+
<p>That works and all but what if you don't want to embed your username and password in the class? Below is an example to fix that:</p>
|
41
|
+
|
42
|
+
<pre><code>class Twitter
|
43
|
+
include HTTParty
|
44
|
+
base_uri 'twitter.com'
|
45
|
+
|
46
|
+
def initialize(u, p)
|
47
|
+
@auth = {username: u, password: p}
|
48
|
+
end
|
49
|
+
|
50
|
+
def post(text)
|
51
|
+
options = { query: {status: text}, basic_auth: @auth }
|
52
|
+
self.class.post('/statuses/update.json', options)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Twitter.new('username', 'password').post("It's an HTTParty and everyone is invited!")</code></pre>
|
57
|
+
|
58
|
+
<p><strong>More Examples:</strong> There are <a href="http://github.com/jnunemaker/httparty/tree/master/examples/">several examples in the gem itself</a>.</p>
|
59
|
+
|
60
|
+
<h2>Support</h2>
|
61
|
+
<p>Conversations welcome in the <a href="http://groups.google.com/group/httparty-gem">google group</a> and bugs/features over at <a href="http://github.com/jnunemaker/httparty">Github</a>.</p>
|
62
|
+
|
63
|
+
|
64
|
+
</div>
|
65
|
+
|
66
|
+
<div id="footer">
|
67
|
+
<p>Created by <a href="http://addictedtonew.com/about/">John Nunemaker</a> |
|
68
|
+
<a href="http://orderedlist.com/">Hire Me at Ordered List</a></p>
|
69
|
+
</div>
|
70
|
+
</div>
|
71
|
+
|
72
|
+
</body>
|
73
|
+
</html>
|