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.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.rubocop.yml +92 -0
  4. data/.rubocop_todo.yml +124 -0
  5. data/.simplecov +1 -0
  6. data/.travis.yml +7 -0
  7. data/CONTRIBUTING.md +23 -0
  8. data/Gemfile +19 -0
  9. data/Guardfile +16 -0
  10. data/History +370 -0
  11. data/MIT-LICENSE +20 -0
  12. data/README.md +78 -0
  13. data/Rakefile +10 -0
  14. data/bin/httparty +116 -0
  15. data/cucumber.yml +1 -0
  16. data/examples/README.md +67 -0
  17. data/examples/aaws.rb +32 -0
  18. data/examples/basic.rb +28 -0
  19. data/examples/crack.rb +19 -0
  20. data/examples/custom_parsers.rb +64 -0
  21. data/examples/delicious.rb +37 -0
  22. data/examples/google.rb +16 -0
  23. data/examples/headers_and_user_agents.rb +6 -0
  24. data/examples/logging.rb +36 -0
  25. data/examples/nokogiri_html_parser.rb +19 -0
  26. data/examples/rescue_json.rb +17 -0
  27. data/examples/rubyurl.rb +14 -0
  28. data/examples/stackexchange.rb +24 -0
  29. data/examples/tripit_sign_in.rb +33 -0
  30. data/examples/twitter.rb +31 -0
  31. data/examples/whoismyrep.rb +10 -0
  32. data/features/basic_authentication.feature +20 -0
  33. data/features/command_line.feature +90 -0
  34. data/features/deals_with_http_error_codes.feature +26 -0
  35. data/features/digest_authentication.feature +20 -0
  36. data/features/handles_compressed_responses.feature +27 -0
  37. data/features/handles_multiple_formats.feature +57 -0
  38. data/features/steps/env.rb +27 -0
  39. data/features/steps/httparty_response_steps.rb +52 -0
  40. data/features/steps/httparty_steps.rb +43 -0
  41. data/features/steps/mongrel_helper.rb +94 -0
  42. data/features/steps/remote_service_steps.rb +86 -0
  43. data/features/supports_read_timeout_option.feature +13 -0
  44. data/features/supports_redirection.feature +22 -0
  45. data/features/supports_timeout_option.feature +13 -0
  46. data/httparty.gemspec +28 -0
  47. data/httpserious.gemspec +25 -0
  48. data/lib/httparty.rb +612 -0
  49. data/lib/httparty/connection_adapter.rb +190 -0
  50. data/lib/httparty/cookie_hash.rb +21 -0
  51. data/lib/httparty/exceptions.rb +29 -0
  52. data/lib/httparty/hash_conversions.rb +49 -0
  53. data/lib/httparty/logger/apache_formatter.rb +22 -0
  54. data/lib/httparty/logger/curl_formatter.rb +48 -0
  55. data/lib/httparty/logger/logger.rb +26 -0
  56. data/lib/httparty/module_inheritable_attributes.rb +56 -0
  57. data/lib/httparty/net_digest_auth.rb +117 -0
  58. data/lib/httparty/parser.rb +141 -0
  59. data/lib/httparty/request.rb +361 -0
  60. data/lib/httparty/response.rb +77 -0
  61. data/lib/httparty/response/headers.rb +31 -0
  62. data/lib/httparty/version.rb +3 -0
  63. data/lib/httpserious.rb +1 -0
  64. data/script/release +42 -0
  65. data/spec/fixtures/delicious.xml +23 -0
  66. data/spec/fixtures/empty.xml +0 -0
  67. data/spec/fixtures/google.html +3 -0
  68. data/spec/fixtures/ssl/generate.sh +29 -0
  69. data/spec/fixtures/ssl/generated/1fe462c2.0 +16 -0
  70. data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
  71. data/spec/fixtures/ssl/generated/ca.crt +16 -0
  72. data/spec/fixtures/ssl/generated/ca.key +15 -0
  73. data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
  74. data/spec/fixtures/ssl/generated/server.crt +13 -0
  75. data/spec/fixtures/ssl/generated/server.key +15 -0
  76. data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
  77. data/spec/fixtures/twitter.csv +2 -0
  78. data/spec/fixtures/twitter.json +1 -0
  79. data/spec/fixtures/twitter.xml +403 -0
  80. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  81. data/spec/httparty/connection_adapter_spec.rb +468 -0
  82. data/spec/httparty/cookie_hash_spec.rb +83 -0
  83. data/spec/httparty/exception_spec.rb +38 -0
  84. data/spec/httparty/hash_conversions_spec.rb +41 -0
  85. data/spec/httparty/logger/apache_formatter_spec.rb +41 -0
  86. data/spec/httparty/logger/curl_formatter_spec.rb +18 -0
  87. data/spec/httparty/logger/logger_spec.rb +38 -0
  88. data/spec/httparty/net_digest_auth_spec.rb +191 -0
  89. data/spec/httparty/parser_spec.rb +167 -0
  90. data/spec/httparty/request_spec.rb +872 -0
  91. data/spec/httparty/response_spec.rb +241 -0
  92. data/spec/httparty/ssl_spec.rb +74 -0
  93. data/spec/httparty_spec.rb +823 -0
  94. data/spec/spec_helper.rb +59 -0
  95. data/spec/support/ssl_test_helper.rb +47 -0
  96. data/spec/support/ssl_test_server.rb +80 -0
  97. data/spec/support/stub_response.rb +43 -0
  98. data/website/css/common.css +47 -0
  99. data/website/index.html +73 -0
  100. metadata +219 -0
@@ -0,0 +1,83 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../spec_helper'))
2
+
3
+ RSpec.describe HTTParty::CookieHash do
4
+ before(:each) do
5
+ @cookie_hash = HTTParty::CookieHash.new
6
+ end
7
+
8
+ describe "#add_cookies" do
9
+ describe "with a hash" do
10
+ it "should add new key/value pairs to the hash" do
11
+ @cookie_hash.add_cookies(foo: "bar")
12
+ @cookie_hash.add_cookies(rofl: "copter")
13
+ expect(@cookie_hash.length).to eql(2)
14
+ end
15
+
16
+ it "should overwrite any existing key" do
17
+ @cookie_hash.add_cookies(foo: "bar")
18
+ @cookie_hash.add_cookies(foo: "copter")
19
+ expect(@cookie_hash.length).to eql(1)
20
+ expect(@cookie_hash[:foo]).to eql("copter")
21
+ end
22
+ end
23
+
24
+ describe "with a string" do
25
+ it "should add new key/value pairs to the hash" do
26
+ @cookie_hash.add_cookies("first=one; second=two; third")
27
+ expect(@cookie_hash[:first]).to eq('one')
28
+ expect(@cookie_hash[:second]).to eq('two')
29
+ expect(@cookie_hash[:third]).to eq(nil)
30
+ end
31
+
32
+ it "should overwrite any existing key" do
33
+ @cookie_hash[:foo] = 'bar'
34
+ @cookie_hash.add_cookies("foo=tar")
35
+ expect(@cookie_hash.length).to eql(1)
36
+ expect(@cookie_hash[:foo]).to eql("tar")
37
+ end
38
+
39
+ it "should handle '=' within cookie value" do
40
+ @cookie_hash.add_cookies("first=one=1; second=two=2==")
41
+ expect(@cookie_hash.keys).to include(:first, :second)
42
+ expect(@cookie_hash[:first]).to eq('one=1')
43
+ expect(@cookie_hash[:second]).to eq('two=2==')
44
+ end
45
+ end
46
+
47
+ describe 'with other class' do
48
+ it "should error" do
49
+ expect {
50
+ @cookie_hash.add_cookies([])
51
+ }.to raise_error
52
+ end
53
+ end
54
+ end
55
+
56
+ # The regexen are required because Hashes aren't ordered, so a test against
57
+ # a hardcoded string was randomly failing.
58
+ describe "#to_cookie_string" do
59
+ before(:each) do
60
+ @cookie_hash.add_cookies(foo: "bar")
61
+ @cookie_hash.add_cookies(rofl: "copter")
62
+ @s = @cookie_hash.to_cookie_string
63
+ end
64
+
65
+ it "should format the key/value pairs, delimited by semi-colons" do
66
+ expect(@s).to match(/foo=bar/)
67
+ expect(@s).to match(/rofl=copter/)
68
+ expect(@s).to match(/^\w+=\w+; \w+=\w+$/)
69
+ end
70
+
71
+ it "should not include client side only cookies" do
72
+ @cookie_hash.add_cookies(path: "/")
73
+ @s = @cookie_hash.to_cookie_string
74
+ expect(@s).not_to match(/path=\//)
75
+ end
76
+
77
+ it "should not include client side only cookies even when attributes use camal case" do
78
+ @cookie_hash.add_cookies(Path: "/")
79
+ @s = @cookie_hash.to_cookie_string
80
+ expect(@s).not_to match(/Path=\//)
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,38 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ RSpec.describe HTTParty::Error do
4
+ subject { described_class }
5
+
6
+ describe '#ancestors' do
7
+ subject { super().ancestors }
8
+ it { is_expected.to include(StandardError) }
9
+ end
10
+
11
+ describe HTTParty::UnsupportedFormat do
12
+ describe '#ancestors' do
13
+ subject { super().ancestors }
14
+ it { is_expected.to include(HTTParty::Error) }
15
+ end
16
+ end
17
+
18
+ describe HTTParty::UnsupportedURIScheme do
19
+ describe '#ancestors' do
20
+ subject { super().ancestors }
21
+ it { is_expected.to include(HTTParty::Error) }
22
+ end
23
+ end
24
+
25
+ describe HTTParty::ResponseError do
26
+ describe '#ancestors' do
27
+ subject { super().ancestors }
28
+ it { is_expected.to include(HTTParty::Error) }
29
+ end
30
+ end
31
+
32
+ describe HTTParty::RedirectionTooDeep do
33
+ describe '#ancestors' do
34
+ subject { super().ancestors }
35
+ it { is_expected.to include(HTTParty::ResponseError) }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ RSpec.describe HTTParty::HashConversions do
2
+ describe ".to_params" do
3
+ it "creates a params string from a hash" do
4
+ hash = {
5
+ name: "bob",
6
+ address: {
7
+ street: '111 ruby ave.',
8
+ city: 'ruby central',
9
+ phones: ['111-111-1111', '222-222-2222']
10
+ }
11
+ }
12
+ expect(HTTParty::HashConversions.to_params(hash)).to eq("name=bob&address[street]=111%20ruby%20ave.&address[city]=ruby%20central&address[phones][]=111-111-1111&address[phones][]=222-222-2222")
13
+ end
14
+ end
15
+
16
+ describe ".normalize_param" do
17
+ context "value is an array" do
18
+ it "creates a params string" do
19
+ expect(
20
+ HTTParty::HashConversions.normalize_param(:people, ["Bob Jones", "Mike Smith"])
21
+ ).to eq("people[]=Bob%20Jones&people[]=Mike%20Smith&")
22
+ end
23
+ end
24
+
25
+ context "value is hash" do
26
+ it "creates a params string" do
27
+ expect(
28
+ HTTParty::HashConversions.normalize_param(:person, { name: "Bob Jones" })
29
+ ).to eq("person[name]=Bob%20Jones&")
30
+ end
31
+ end
32
+
33
+ context "value is a string" do
34
+ it "creates a params string" do
35
+ expect(
36
+ HTTParty::HashConversions.normalize_param(:name, "Bob Jones")
37
+ ).to eq("name=Bob%20Jones&")
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ RSpec.describe HTTParty::Logger::ApacheFormatter do
4
+ let(:subject) { described_class.new(logger_double, :info) }
5
+ let(:logger_double) { double('Logger') }
6
+ let(:request_double) { double('Request', http_method: Net::HTTP::Get, path: "http://my.domain.com/my_path") }
7
+ let(:request_time) { Time.new.strftime("%Y-%m-%d %H:%M:%S %z") }
8
+
9
+ before do
10
+ subject.current_time = request_time
11
+ expect(logger_double).to receive(:info).with(log_message)
12
+ end
13
+
14
+ describe "#format" do
15
+ let(:log_message) { "[HTTParty] [#{request_time}] 302 \"GET http://my.domain.com/my_path\" - " }
16
+
17
+ it "formats a response in a style that resembles apache's access log" do
18
+ response_double = double(
19
+ code: 302,
20
+ :[] => nil
21
+ )
22
+
23
+ subject.format(request_double, response_double)
24
+ end
25
+
26
+ context 'when there is a parsed response' do
27
+ let(:log_message) { "[HTTParty] [#{request_time}] 200 \"GET http://my.domain.com/my_path\" 512 "}
28
+
29
+ it "can handle the Content-Length header" do
30
+ # Simulate a parsed response that is an array, where accessing a string key will raise an error. See Issue #299.
31
+ response_double = double(
32
+ code: 200,
33
+ headers: { 'Content-Length' => 512 }
34
+ )
35
+ allow(response_double).to receive(:[]).with('Content-Length').and_raise(TypeError.new('no implicit conversion of String into Integer'))
36
+
37
+ subject.format(request_double, response_double)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,18 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ RSpec.describe HTTParty::Logger::CurlFormatter do
4
+ describe "#format" do
5
+ it "formats a response in a style that resembles a -v curl" do
6
+ logger_double = double
7
+ expect(logger_double).to receive(:info).with(
8
+ /\[HTTParty\] \[\d{4}-\d\d-\d\d \d\d:\d\d:\d\d\ [+-]\d{4}\] > GET http:\/\/localhost/)
9
+
10
+ subject = described_class.new(logger_double, :info)
11
+
12
+ stub_http_response_with("google.html")
13
+
14
+ response = HTTParty::Request.new.perform
15
+ subject.format(response.request, response)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,38 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'spec_helper'))
2
+
3
+ RSpec.describe HTTParty::Logger do
4
+ describe ".build" do
5
+ subject { HTTParty::Logger }
6
+
7
+ it "defaults level to :info" do
8
+ logger_double = double
9
+ expect(subject.build(logger_double, nil, nil).level).to eq(:info)
10
+ end
11
+
12
+ it "defaults format to :apache" do
13
+ logger_double = double
14
+ expect(subject.build(logger_double, nil, nil)).to be_an_instance_of(HTTParty::Logger::ApacheFormatter)
15
+ end
16
+
17
+ it "builds :curl style logger" do
18
+ logger_double = double
19
+ expect(subject.build(logger_double, nil, :curl)).to be_an_instance_of(HTTParty::Logger::CurlFormatter)
20
+ end
21
+
22
+ it "builds :custom style logger" do
23
+ CustomFormatter = Class.new(HTTParty::Logger::CurlFormatter)
24
+ HTTParty::Logger.add_formatter(:custom, CustomFormatter)
25
+
26
+ logger_double = double
27
+ expect(subject.build(logger_double, nil, :custom)).
28
+ to be_an_instance_of(CustomFormatter)
29
+ end
30
+ it "raises error when formatter exists" do
31
+ CustomFormatter2= Class.new(HTTParty::Logger::CurlFormatter)
32
+ HTTParty::Logger.add_formatter(:custom2, CustomFormatter2)
33
+
34
+ expect{ HTTParty::Logger.add_formatter(:custom2, CustomFormatter2) }.
35
+ to raise_error HTTParty::Error
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,191 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
+
3
+ RSpec.describe Net::HTTPHeader::DigestAuthenticator do
4
+ def setup_digest(response)
5
+ digest = Net::HTTPHeader::DigestAuthenticator.new("Mufasa",
6
+ "Circle Of Life", "GET", "/dir/index.html", response)
7
+ allow(digest).to receive(:random).and_return("deadbeef")
8
+ allow(Digest::MD5).to receive(:hexdigest) { |str| "md5(#{str})" }
9
+ digest
10
+ end
11
+
12
+ def authorization_header
13
+ @digest.authorization_header.join(", ")
14
+ end
15
+
16
+ def cookie_header
17
+ @digest.cookie_header
18
+ end
19
+
20
+ context "with a cookie value in the response header" do
21
+ before do
22
+ @digest = setup_digest({
23
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com"',
24
+ 'Set-Cookie' => 'custom-cookie=1234567'
25
+ })
26
+ end
27
+
28
+ it "should set cookie header" do
29
+ expect(cookie_header).to include('custom-cookie=1234567')
30
+ end
31
+ end
32
+
33
+ context "without a cookie value in the response header" do
34
+ before do
35
+ @digest = setup_digest({
36
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com"'
37
+ })
38
+ end
39
+
40
+ it "should set empty cookie header array" do
41
+ expect(cookie_header).to eql []
42
+ end
43
+ end
44
+
45
+ context "with an opaque value in the response header" do
46
+ before do
47
+ @digest = setup_digest({
48
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", opaque="solid"'
49
+ })
50
+ end
51
+
52
+ it "should set opaque" do
53
+ expect(authorization_header).to include('opaque="solid"')
54
+ end
55
+ end
56
+
57
+ context "without an opaque valid in the response header" do
58
+ before do
59
+ @digest = setup_digest({
60
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com"'
61
+ })
62
+ end
63
+
64
+ it "should not set opaque" do
65
+ expect(authorization_header).not_to include("opaque=")
66
+ end
67
+ end
68
+
69
+ context "with specified quality of protection (qop)" do
70
+ before do
71
+ @digest = setup_digest({
72
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"'
73
+ })
74
+ end
75
+
76
+ it "should set prefix" do
77
+ expect(authorization_header).to match(/^Digest /)
78
+ end
79
+
80
+ it "should set username" do
81
+ expect(authorization_header).to include('username="Mufasa"')
82
+ end
83
+
84
+ it "should set digest-uri" do
85
+ expect(authorization_header).to include('uri="/dir/index.html"')
86
+ end
87
+
88
+ it "should set qop" do
89
+ expect(authorization_header).to include('qop="auth"')
90
+ end
91
+
92
+ it "should set cnonce" do
93
+ expect(authorization_header).to include('cnonce="md5(deadbeef)"')
94
+ end
95
+
96
+ it "should set nonce-count" do
97
+ expect(authorization_header).to include("nc=00000001")
98
+ end
99
+
100
+ it "should set response" do
101
+ request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))"
102
+ expect(authorization_header).to include(%(response="#{request_digest}"))
103
+ end
104
+ end
105
+
106
+ context "when quality of protection (qop) is unquoted" do
107
+ before do
108
+ @digest = setup_digest({
109
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE", qop=auth'
110
+ })
111
+ end
112
+
113
+ it "should still set qop" do
114
+ expect(authorization_header).to include('qop="auth"')
115
+ end
116
+ end
117
+
118
+ context "with unspecified quality of protection (qop)" do
119
+ before do
120
+ @digest = setup_digest({
121
+ 'www-authenticate' => 'Digest realm="myhost@testrealm.com", nonce="NONCE"'
122
+ })
123
+ end
124
+
125
+ it "should set prefix" do
126
+ expect(authorization_header).to match(/^Digest /)
127
+ end
128
+
129
+ it "should set username" do
130
+ expect(authorization_header).to include('username="Mufasa"')
131
+ end
132
+
133
+ it "should set digest-uri" do
134
+ expect(authorization_header).to include('uri="/dir/index.html"')
135
+ end
136
+
137
+ it "should not set qop" do
138
+ expect(authorization_header).not_to include("qop=")
139
+ end
140
+
141
+ it "should not set cnonce" do
142
+ expect(authorization_header).not_to include("cnonce=")
143
+ end
144
+
145
+ it "should not set nonce-count" do
146
+ expect(authorization_header).not_to include("nc=")
147
+ end
148
+
149
+ it "should set response" do
150
+ request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:md5(GET:/dir/index.html))"
151
+ expect(authorization_header).to include(%(response="#{request_digest}"))
152
+ end
153
+ end
154
+
155
+ context "with multiple authenticate headers" do
156
+ before do
157
+ @digest = setup_digest({
158
+ 'www-authenticate' => 'NTLM, Digest realm="myhost@testrealm.com", nonce="NONCE", qop="auth"'
159
+ })
160
+ end
161
+
162
+ it "should set prefix" do
163
+ expect(authorization_header).to match(/^Digest /)
164
+ end
165
+
166
+ it "should set username" do
167
+ expect(authorization_header).to include('username="Mufasa"')
168
+ end
169
+
170
+ it "should set digest-uri" do
171
+ expect(authorization_header).to include('uri="/dir/index.html"')
172
+ end
173
+
174
+ it "should set qop" do
175
+ expect(authorization_header).to include('qop="auth"')
176
+ end
177
+
178
+ it "should set cnonce" do
179
+ expect(authorization_header).to include('cnonce="md5(deadbeef)"')
180
+ end
181
+
182
+ it "should set nonce-count" do
183
+ expect(authorization_header).to include("nc=00000001")
184
+ end
185
+
186
+ it "should set response" do
187
+ request_digest = "md5(md5(Mufasa:myhost@testrealm.com:Circle Of Life):NONCE:00000001:md5(deadbeef):auth:md5(GET:/dir/index.html))"
188
+ expect(authorization_header).to include(%(response="#{request_digest}"))
189
+ end
190
+ end
191
+ end