httpserious 0.13.5.lstoll1

Sign up to get free protection for your applications and to get access to all the features.
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,57 @@
1
+ Feature: Handles Multiple Formats
2
+
3
+ As a developer
4
+ I want to be able to consume remote services of many different formats
5
+ And I want those formats to be automatically detected and handled
6
+ Because web services take many forms
7
+ And I don't want to have to do any extra work
8
+
9
+ Scenario: An HTML service
10
+ Given a remote service that returns '<h1>Some HTML</h1>'
11
+ And that service is accessed at the path '/html_service.html'
12
+ And the response from the service has a Content-Type of 'text/html'
13
+ When I call HTTParty#get with '/html_service.html'
14
+ Then it should return a String
15
+ And the return value should match '<h1>Some HTML</h1>'
16
+
17
+ Scenario: A CSV service
18
+ Given a remote service that returns:
19
+ """
20
+ "Last Name","Name"
21
+ "jennings","waylon"
22
+ "cash","johnny"
23
+ """
24
+ And that service is accessed at the path '/service.csv'
25
+ And the response from the service has a Content-Type of 'application/csv'
26
+ When I call HTTParty#get with '/service.csv'
27
+ Then it should return an Array equaling:
28
+ | Last Name | Name |
29
+ | jennings | waylon |
30
+ | cash | johnny |
31
+
32
+ Scenario: A JSON service
33
+ Given a remote service that returns '{ "jennings": "waylon", "cash": "johnny" }'
34
+ And that service is accessed at the path '/service.json'
35
+ And the response from the service has a Content-Type of 'application/json'
36
+ When I call HTTParty#get with '/service.json'
37
+ Then it should return a Hash equaling:
38
+ | key | value |
39
+ | jennings | waylon |
40
+ | cash | johnny |
41
+
42
+ Scenario: An XML Service
43
+ Given a remote service that returns '<singer>waylon jennings</singer>'
44
+ And that service is accessed at the path '/service.xml'
45
+ And the response from the service has a Content-Type of 'text/xml'
46
+ When I call HTTParty#get with '/service.xml'
47
+ Then it should return a Hash equaling:
48
+ | key | value |
49
+ | singer | waylon jennings |
50
+
51
+ Scenario: A Javascript remote file
52
+ Given a remote service that returns '$(function() { alert("hi"); });'
53
+ And that service is accessed at the path '/service.js'
54
+ And the response from the service has a Content-Type of 'application/javascript'
55
+ When I call HTTParty#get with '/service.js'
56
+ Then it should return a String
57
+ And the return value should match '$(function() { alert("hi"); });'
@@ -0,0 +1,27 @@
1
+ require 'mongrel'
2
+ require './lib/httparty'
3
+ require 'rspec/expectations'
4
+ require 'aruba/cucumber'
5
+
6
+ def run_server(port)
7
+ @host_and_port = "0.0.0.0:#{port}"
8
+ @server = Mongrel::HttpServer.new("0.0.0.0", port)
9
+ @server.run
10
+ @request_options = {}
11
+ end
12
+
13
+ def new_port
14
+ server = TCPServer.new('0.0.0.0', nil)
15
+ port = server.addr[1]
16
+ ensure
17
+ server.close
18
+ end
19
+
20
+ Before('~@command_line') do
21
+ port = ENV["HTTPARTY_PORT"] || new_port
22
+ run_server(port)
23
+ end
24
+
25
+ After do
26
+ @server.stop if @server
27
+ end
@@ -0,0 +1,52 @@
1
+ # Not needed anymore in ruby 2.0, but needed to resolve constants
2
+ # in nested namespaces. This is taken from rails :)
3
+ def constantize(camel_cased_word)
4
+ names = camel_cased_word.split('::')
5
+ names.shift if names.empty? || names.first.empty?
6
+
7
+ constant = Object
8
+ names.each do |name|
9
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
10
+ end
11
+ constant
12
+ end
13
+
14
+ Then /it should return an? (\w+)$/ do |class_string|
15
+ expect(@response_from_httparty).to be_a(class_string.class)
16
+ end
17
+
18
+ Then /the return value should match '(.*)'/ do |expected_text|
19
+ expect(@response_from_httparty.parsed_response).to eq(expected_text)
20
+ end
21
+
22
+ Then /it should return a Hash equaling:/ do |hash_table|
23
+ expect(@response_from_httparty).to be_a(Hash)
24
+ expect(@response_from_httparty.keys.length).to eq(hash_table.rows.length)
25
+ hash_table.hashes.each do |pair|
26
+ key, value = pair["key"], pair["value"]
27
+ expect(@response_from_httparty.keys).to include(key)
28
+ expect(@response_from_httparty[key]).to eq(value)
29
+ end
30
+ end
31
+
32
+ Then /it should return an Array equaling:/ do |array|
33
+ expect(@response_from_httparty).to be_a(Array)
34
+ expect(@response_from_httparty.parsed_response).to eq(array.raw)
35
+ end
36
+
37
+ Then /it should return a response with a (\d+) response code/ do |code|
38
+ expect(@response_from_httparty.code).to eq(code.to_i)
39
+ end
40
+
41
+ Then /it should return a response with a (.*) content\-encoding$/ do |content_type|
42
+ expect(@response_from_httparty.headers['content-encoding']).to eq('gzip')
43
+ end
44
+
45
+ Then /it should return a response with a blank body$/ do
46
+ expect(@response_from_httparty.body).to be_nil
47
+ end
48
+
49
+ Then /it should raise (?:an|a) ([\w:]+) exception/ do |exception|
50
+ expect(@exception_from_httparty).to_not be_nil
51
+ expect(@exception_from_httparty).to be_a constantize(exception)
52
+ end
@@ -0,0 +1,43 @@
1
+ When /^I set my HTTParty timeout option to (\d+)$/ do |timeout|
2
+ @request_options[:timeout] = timeout.to_i
3
+ end
4
+
5
+ When /^I set my HTTParty open_timeout option to (\d+)$/ do |timeout|
6
+ @request_options[:open_timeout] = timeout.to_i
7
+ end
8
+
9
+ When /^I set my HTTParty read_timeout option to (\d+)$/ do |timeout|
10
+ @request_options[:read_timeout] = timeout.to_i
11
+ end
12
+
13
+ When /I call HTTParty#get with '(.*)'$/ do |url|
14
+ begin
15
+ @response_from_httparty = HTTParty.get("http://#{@host_and_port}#{url}", @request_options)
16
+ rescue HTTParty::RedirectionTooDeep, Timeout::Error => e
17
+ @exception_from_httparty = e
18
+ end
19
+ end
20
+
21
+ When /^I call HTTParty#head with '(.*)'$/ do |url|
22
+ begin
23
+ @response_from_httparty = HTTParty.head("http://#{@host_and_port}#{url}", @request_options)
24
+ rescue HTTParty::RedirectionTooDeep, Timeout::Error => e
25
+ @exception_from_httparty = e
26
+ end
27
+ end
28
+
29
+ When /I call HTTParty#get with '(.*)' and a basic_auth hash:/ do |url, auth_table|
30
+ h = auth_table.hashes.first
31
+ @response_from_httparty = HTTParty.get(
32
+ "http://#{@host_and_port}#{url}",
33
+ basic_auth: { username: h["username"], password: h["password"] }
34
+ )
35
+ end
36
+
37
+ When /I call HTTParty#get with '(.*)' and a digest_auth hash:/ do |url, auth_table|
38
+ h = auth_table.hashes.first
39
+ @response_from_httparty = HTTParty.get(
40
+ "http://#{@host_and_port}#{url}",
41
+ digest_auth: { username: h["username"], password: h["password"] }
42
+ )
43
+ end
@@ -0,0 +1,94 @@
1
+ require 'base64'
2
+ class BasicMongrelHandler < Mongrel::HttpHandler
3
+ attr_accessor :content_type, :custom_headers, :response_body, :response_code, :preprocessor, :username, :password
4
+
5
+ def initialize
6
+ @content_type = "text/html"
7
+ @response_body = ""
8
+ @response_code = 200
9
+ @custom_headers = {}
10
+ end
11
+
12
+ def process(request, response)
13
+ instance_eval(&preprocessor) if preprocessor
14
+ reply_with(response, response_code, response_body)
15
+ end
16
+
17
+ def reply_with(response, code, response_body)
18
+ response.start(code) do |head, body|
19
+ head["Content-Type"] = content_type
20
+ custom_headers.each { |k, v| head[k] = v }
21
+ body.write(response_body)
22
+ end
23
+ end
24
+ end
25
+
26
+ class DeflateHandler < BasicMongrelHandler
27
+ def process(request, response)
28
+ response.start do |head, body|
29
+ head['Content-Encoding'] = 'deflate'
30
+ body.write Zlib::Deflate.deflate(response_body)
31
+ end
32
+ end
33
+ end
34
+
35
+ class GzipHandler < BasicMongrelHandler
36
+ def process(request, response)
37
+ response.start do |head, body|
38
+ head['Content-Encoding'] = 'gzip'
39
+ body.write gzip(response_body)
40
+ end
41
+ end
42
+
43
+ protected
44
+
45
+ def gzip(string)
46
+ sio = StringIO.new('', 'r+')
47
+ gz = Zlib::GzipWriter.new sio
48
+ gz.write string
49
+ gz.finish
50
+ sio.rewind
51
+ sio.read
52
+ end
53
+ end
54
+
55
+ module BasicAuthentication
56
+ def self.extended(base)
57
+ base.custom_headers["WWW-Authenticate"] = 'Basic Realm="Super Secret Page"'
58
+ end
59
+
60
+ def process(request, response)
61
+ if authorized?(request)
62
+ super
63
+ else
64
+ reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
65
+ end
66
+ end
67
+
68
+ def authorized?(request)
69
+ request.params["HTTP_AUTHORIZATION"] == "Basic " + Base64.encode64("#{@username}:#{@password}").strip
70
+ end
71
+ end
72
+
73
+ module DigestAuthentication
74
+ def self.extended(base)
75
+ base.custom_headers["WWW-Authenticate"] = 'Digest realm="testrealm@host.com",qop="auth,auth-int",nonce="nonce",opaque="opaque"'
76
+ end
77
+
78
+ def process(request, response)
79
+ if authorized?(request)
80
+ super
81
+ else
82
+ reply_with(response, 401, "Incorrect. You have 20 seconds to comply.")
83
+ end
84
+ end
85
+
86
+ def authorized?(request)
87
+ request.params["HTTP_AUTHORIZATION"] =~ /Digest.*uri=/
88
+ end
89
+ end
90
+
91
+ def new_mongrel_redirector(target_url, relative_path = false)
92
+ target_url = "http://#{@host_and_port}#{target_url}" unless relative_path
93
+ Mongrel::RedirectHandler.new(target_url)
94
+ end
@@ -0,0 +1,86 @@
1
+ Given /a remote service that returns '(.*)'/ do |response_body|
2
+ @handler = BasicMongrelHandler.new
3
+ step "the response from the service has a body of '#{response_body}'"
4
+ end
5
+
6
+ Given /^a remote service that returns:$/ do |response_body|
7
+ @handler = BasicMongrelHandler.new
8
+ @handler.response_body = response_body
9
+ end
10
+
11
+ Given /a remote service that returns a (\d+) status code/ do |code|
12
+ @handler = BasicMongrelHandler.new
13
+ @handler.response_code = code
14
+ end
15
+
16
+ Given /that service is accessed at the path '(.*)'/ do |path|
17
+ @server.register(path, @handler)
18
+ end
19
+
20
+ Given /^that service takes (\d+) seconds to generate a response$/ do |time|
21
+ @server_response_time = time.to_i
22
+ @handler.preprocessor = proc { sleep time.to_i }
23
+ end
24
+
25
+ Given /^a remote deflate service$/ do
26
+ @handler = DeflateHandler.new
27
+ end
28
+
29
+ Given /^a remote deflate service on port '(\d+)'/ do |port|
30
+ run_server(port)
31
+ @handler = DeflateHandler.new
32
+ end
33
+
34
+ Given /^a remote gzip service$/ do
35
+ @handler = GzipHandler.new
36
+ end
37
+
38
+ Given /the response from the service has a Content-Type of '(.*)'/ do |content_type|
39
+ @handler.content_type = content_type
40
+ end
41
+
42
+ Given /the response from the service has a body of '(.*)'/ do |response_body|
43
+ @handler.response_body = response_body
44
+ end
45
+
46
+ Given /the url '(.*)' redirects to '(.*)'/ do |redirection_url, target_url|
47
+ @server.register redirection_url, new_mongrel_redirector(target_url)
48
+ end
49
+
50
+ Given /that service is protected by Basic Authentication/ do
51
+ @handler.extend BasicAuthentication
52
+ end
53
+
54
+ Given /that service is protected by Digest Authentication/ do
55
+ @handler.extend DigestAuthentication
56
+ end
57
+
58
+ Given /that service requires the username '(.*)' with the password '(.*)'/ do |username, password|
59
+ @handler.username = username
60
+ @handler.password = password
61
+ end
62
+
63
+ # customize aruba cucumber step
64
+ Then /^the output should contain '(.*)'$/ do |expected|
65
+ assert_partial_output(expected, all_output)
66
+ end
67
+
68
+ Given /a restricted page at '(.*)'/ do |url|
69
+ steps "
70
+ Given a remote service that returns 'A response I will never see'
71
+ And that service is accessed at the path '#{url}'
72
+ And that service is protected by Basic Authentication
73
+ And that service requires the username 'something' with the password 'secret'
74
+ "
75
+ end
76
+
77
+ # This joins the server thread, and halts cucumber, so you can actually hit the
78
+ # server with a browser. Runs until you kill it with Ctrl-c
79
+ Given /I want to hit this in a browser/ do
80
+ @server.acceptor.join
81
+ end
82
+
83
+ Then /I wait for the server to recover/ do
84
+ timeout = @request_options[:timeout] || 0
85
+ sleep @server_response_time - timeout
86
+ end
@@ -0,0 +1,13 @@
1
+ Feature: Supports the read timeout option
2
+ In order to handle inappropriately slow response times
3
+ As a developer
4
+ I want my request to raise an exception after my specified read_timeout as elapsed
5
+
6
+ Scenario: A long running response
7
+ Given a remote service that returns '<h1>Some HTML</h1>'
8
+ And that service is accessed at the path '/long_running_service.html'
9
+ And that service takes 2 seconds to generate a response
10
+ When I set my HTTParty read_timeout option to 1
11
+ And I call HTTParty#get with '/long_running_service.html'
12
+ Then it should raise a Timeout::Error exception
13
+ And I wait for the server to recover
@@ -0,0 +1,22 @@
1
+ Feature: Supports Redirection
2
+
3
+ As a developer
4
+ I want to work with services that may redirect me
5
+ And I want it to follow a reasonable number of redirects
6
+ Because sometimes web services do that
7
+
8
+ Scenario: A service that redirects once
9
+ Given a remote service that returns 'Service Response'
10
+ And that service is accessed at the path '/landing_service.html'
11
+ And the url '/redirector.html' redirects to '/landing_service.html'
12
+ When I call HTTParty#get with '/redirector.html'
13
+ Then the return value should match 'Service Response'
14
+
15
+ # TODO: Look in to why this actually fails...
16
+ Scenario: A service that redirects to a relative URL
17
+
18
+ Scenario: A service that redirects infinitely
19
+ Given the url '/first.html' redirects to '/second.html'
20
+ And the url '/second.html' redirects to '/first.html'
21
+ When I call HTTParty#get with '/first.html'
22
+ Then it should raise an HTTParty::RedirectionTooDeep exception
@@ -0,0 +1,13 @@
1
+ Feature: Supports the timeout option
2
+ In order to handle inappropriately slow response times
3
+ As a developer
4
+ I want my request to raise an exception after my specified timeout as elapsed
5
+
6
+ Scenario: A long running response
7
+ Given a remote service that returns '<h1>Some HTML</h1>'
8
+ And that service is accessed at the path '/long_running_service.html'
9
+ And that service takes 2 seconds to generate a response
10
+ When I set my HTTParty timeout option to 1
11
+ And I call HTTParty#get with '/long_running_service.html'
12
+ Then it should raise a Timeout::Error exception
13
+ And I wait for the server to recover
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.push File.expand_path("../lib", __FILE__)
3
+ require "httparty/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "httparty"
7
+ s.version = HTTParty::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.licenses = ['MIT']
10
+ s.authors = ["John Nunemaker", "Sandro Turriate"]
11
+ s.email = ["nunemaker@gmail.com"]
12
+ s.homepage = "http://jnunemaker.github.com/httparty"
13
+ s.summary = 'Makes http fun! Also, makes consuming restful web services dead easy.'
14
+ s.description = 'Makes http fun! Also, makes consuming restful web services dead easy.'
15
+
16
+ s.required_ruby_version = '>= 1.9.3'
17
+
18
+ s.add_dependency 'json', "~> 1.8"
19
+ s.add_dependency 'multi_xml', ">= 0.5.2"
20
+
21
+ # If this line is removed, all hard partying will cease.
22
+ s.post_install_message = "When you HTTParty, you must party hard! 🎉"
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end