dkastner-httparty 0.9.0

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 (80) hide show
  1. data/.gitignore +10 -0
  2. data/.travis.yml +8 -0
  3. data/Gemfile +15 -0
  4. data/Guardfile +16 -0
  5. data/History +293 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +79 -0
  8. data/Rakefile +15 -0
  9. data/bin/httparty +114 -0
  10. data/cucumber.yml +1 -0
  11. data/examples/aaws.rb +32 -0
  12. data/examples/basic.rb +32 -0
  13. data/examples/crack.rb +19 -0
  14. data/examples/custom_parsers.rb +67 -0
  15. data/examples/delicious.rb +37 -0
  16. data/examples/google.rb +16 -0
  17. data/examples/headers_and_user_agents.rb +6 -0
  18. data/examples/nokogiri_html_parser.rb +22 -0
  19. data/examples/rubyurl.rb +14 -0
  20. data/examples/tripit_sign_in.rb +33 -0
  21. data/examples/twitter.rb +31 -0
  22. data/examples/whoismyrep.rb +10 -0
  23. data/features/basic_authentication.feature +20 -0
  24. data/features/command_line.feature +7 -0
  25. data/features/deals_with_http_error_codes.feature +26 -0
  26. data/features/digest_authentication.feature +20 -0
  27. data/features/handles_compressed_responses.feature +19 -0
  28. data/features/handles_multiple_formats.feature +34 -0
  29. data/features/steps/env.rb +22 -0
  30. data/features/steps/httparty_response_steps.rb +26 -0
  31. data/features/steps/httparty_steps.rb +27 -0
  32. data/features/steps/mongrel_helper.rb +94 -0
  33. data/features/steps/remote_service_steps.rb +69 -0
  34. data/features/supports_redirection.feature +22 -0
  35. data/features/supports_timeout_option.feature +13 -0
  36. data/httparty.gemspec +24 -0
  37. data/lib/httparty.rb +503 -0
  38. data/lib/httparty/connection_adapter.rb +116 -0
  39. data/lib/httparty/cookie_hash.rb +22 -0
  40. data/lib/httparty/core_extensions.rb +32 -0
  41. data/lib/httparty/exceptions.rb +26 -0
  42. data/lib/httparty/hash_conversions.rb +51 -0
  43. data/lib/httparty/module_inheritable_attributes.rb +44 -0
  44. data/lib/httparty/net_digest_auth.rb +84 -0
  45. data/lib/httparty/parser.rb +145 -0
  46. data/lib/httparty/request.rb +243 -0
  47. data/lib/httparty/response.rb +62 -0
  48. data/lib/httparty/response/headers.rb +31 -0
  49. data/lib/httparty/version.rb +3 -0
  50. data/spec/fixtures/delicious.xml +23 -0
  51. data/spec/fixtures/empty.xml +0 -0
  52. data/spec/fixtures/google.html +3 -0
  53. data/spec/fixtures/ssl/generate.sh +29 -0
  54. data/spec/fixtures/ssl/generated/1fe462c2.0 +16 -0
  55. data/spec/fixtures/ssl/generated/bogushost.crt +13 -0
  56. data/spec/fixtures/ssl/generated/ca.crt +16 -0
  57. data/spec/fixtures/ssl/generated/ca.key +15 -0
  58. data/spec/fixtures/ssl/generated/selfsigned.crt +14 -0
  59. data/spec/fixtures/ssl/generated/server.crt +13 -0
  60. data/spec/fixtures/ssl/generated/server.key +15 -0
  61. data/spec/fixtures/ssl/openssl-exts.cnf +9 -0
  62. data/spec/fixtures/twitter.json +1 -0
  63. data/spec/fixtures/twitter.xml +403 -0
  64. data/spec/fixtures/undefined_method_add_node_for_nil.xml +2 -0
  65. data/spec/httparty/connection_adapter_spec.rb +206 -0
  66. data/spec/httparty/cookie_hash_spec.rb +70 -0
  67. data/spec/httparty/net_digest_auth_spec.rb +115 -0
  68. data/spec/httparty/parser_spec.rb +171 -0
  69. data/spec/httparty/request_spec.rb +507 -0
  70. data/spec/httparty/response_spec.rb +214 -0
  71. data/spec/httparty/ssl_spec.rb +62 -0
  72. data/spec/httparty_spec.rb +703 -0
  73. data/spec/spec.opts +2 -0
  74. data/spec/spec_helper.rb +30 -0
  75. data/spec/support/ssl_test_helper.rb +47 -0
  76. data/spec/support/ssl_test_server.rb +80 -0
  77. data/spec/support/stub_response.rb +43 -0
  78. data/website/css/common.css +47 -0
  79. data/website/index.html +73 -0
  80. metadata +190 -0
@@ -0,0 +1,503 @@
1
+ require 'pathname'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'uri'
5
+ require 'zlib'
6
+ require 'multi_xml'
7
+ require 'multi_json'
8
+
9
+ require 'httparty/module_inheritable_attributes'
10
+ require 'httparty/cookie_hash'
11
+ require 'httparty/net_digest_auth'
12
+ require 'httparty/version'
13
+ require 'httparty/connection_adapter'
14
+
15
+ # @see HTTParty::ClassMethods
16
+ module HTTParty
17
+ module AllowedFormatsDeprecation
18
+ def const_missing(const)
19
+ if const.to_s =~ /AllowedFormats$/
20
+ Kernel.warn("Deprecated: Use HTTParty::Parser::SupportedFormats")
21
+ HTTParty::Parser::SupportedFormats
22
+ else
23
+ super
24
+ end
25
+ end
26
+ end
27
+
28
+ extend AllowedFormatsDeprecation
29
+
30
+ def self.included(base)
31
+ base.extend ClassMethods
32
+ base.send :include, HTTParty::ModuleInheritableAttributes
33
+ base.send(:mattr_inheritable, :default_options)
34
+ base.send(:mattr_inheritable, :default_cookies)
35
+ base.instance_variable_set("@default_options", {})
36
+ base.instance_variable_set("@default_cookies", CookieHash.new)
37
+ end
38
+
39
+ # == Common Request Options
40
+ # Request methods (get, post, patch, put, delete, head, options) all take a common set of options. These are:
41
+ #
42
+ # [:+body+:] Body of the request. If passed a Hash, will try to normalize it first, by default passing it to ActiveSupport::to_params. Any other kind of object will get used as-is.
43
+ # [:+http_proxyaddr+:] Address of proxy server to use.
44
+ # [:+http_proxyport+:] Port of proxy server to use.
45
+ # [:+http_proxyuser+:] User for proxy server authentication.
46
+ # [:+http_proxypass+:] Password for proxy server authentication.
47
+ # [:+limit+:] Maximum number of redirects to follow. Takes precedences over :+no_follow+.
48
+ # [:+query+:] Query string, or a Hash representing it. Normalized according to the same rules as :+body+. If you specify this on a POST, you must use a Hash. See also HTTParty::ClassMethods.default_params.
49
+ # [:+timeout+:] Timeout for opening connection and reading data.
50
+ #
51
+ # There are also another set of options with names corresponding to various class methods. The methods in question are those that let you set a class-wide default, and the options override the defaults on a request-by-request basis. Those options are:
52
+ # * :+base_uri+: see HTTParty::ClassMethods.base_uri.
53
+ # * :+basic_auth+: see HTTParty::ClassMethods.basic_auth. Only one of :+basic_auth+ and :+digest_auth+ can be used at a time; if you try using both, you'll get an ArgumentError.
54
+ # * :+debug_output+: see HTTParty::ClassMethods.debug_output.
55
+ # * :+digest_auth+: see HTTParty::ClassMethods.digest_auth. Only one of :+basic_auth+ and :+digest_auth+ can be used at a time; if you try using both, you'll get an ArgumentError.
56
+ # * :+format+: see HTTParty::ClassMethods.format.
57
+ # * :+headers+: see HTTParty::ClassMethods.headers. Must be a Hash.
58
+ # * :+maintain_method_across_redirects+: see HTTParty::ClassMethods.maintain_method_across_redirects.
59
+ # * :+no_follow+: see HTTParty::ClassMethods.no_follow.
60
+ # * :+parser+: see HTTParty::ClassMethods.parser.
61
+ # * :+connection_adapter+: see HTTParty::ClassMethods.connection_adapter.
62
+ # * :+pem+: see HTTParty::ClassMethods.pem.
63
+ # * :+query_string_normalizer+: see HTTParty::ClassMethods.query_string_normalizer
64
+ # * :+ssl_ca_file+: see HTTParty::ClassMethods.ssl_ca_file.
65
+ # * :+ssl_ca_path+: see HTTParty::ClassMethods.ssl_ca_path.
66
+
67
+ module ClassMethods
68
+
69
+ extend AllowedFormatsDeprecation
70
+
71
+ # Allows setting http proxy information to be used
72
+ #
73
+ # class Foo
74
+ # include HTTParty
75
+ # http_proxy 'http://foo.com', 80, 'user', 'pass'
76
+ # end
77
+ def http_proxy(addr=nil, port=nil, user=nil, pass=nil)
78
+ default_options[:http_proxyaddr] = addr
79
+ default_options[:http_proxyport] = port
80
+ default_options[:http_proxyuser] = user
81
+ default_options[:http_proxypass] = pass
82
+ end
83
+
84
+ # Allows setting a base uri to be used for each request.
85
+ # Will normalize uri to include http, etc.
86
+ #
87
+ # class Foo
88
+ # include HTTParty
89
+ # base_uri 'twitter.com'
90
+ # end
91
+ def base_uri(uri=nil)
92
+ return default_options[:base_uri] unless uri
93
+ default_options[:base_uri] = HTTParty.normalize_base_uri(uri)
94
+ end
95
+
96
+ # Allows setting basic authentication username and password.
97
+ #
98
+ # class Foo
99
+ # include HTTParty
100
+ # basic_auth 'username', 'password'
101
+ # end
102
+ def basic_auth(u, p)
103
+ default_options[:basic_auth] = {:username => u, :password => p}
104
+ end
105
+
106
+ # Allows setting digest authentication username and password.
107
+ #
108
+ # class Foo
109
+ # include HTTParty
110
+ # digest_auth 'username', 'password'
111
+ # end
112
+ def digest_auth(u, p)
113
+ default_options[:digest_auth] = {:username => u, :password => p}
114
+ end
115
+
116
+ # Do not send rails style query strings.
117
+ # Specically, don't use bracket notation when sending an array
118
+ #
119
+ # For a query:
120
+ # get '/', :query => {:selected_ids => [1,2,3]}
121
+ #
122
+ # The default query string looks like this:
123
+ # /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3
124
+ #
125
+ # Call `disable_rails_query_string_format` to transform the query string
126
+ # into:
127
+ # /?selected_ids=1&selected_ids=2&selected_ids=3
128
+ #
129
+ # @example
130
+ # class Foo
131
+ # include HTTParty
132
+ # disable_rails_query_string_format
133
+ # end
134
+ def disable_rails_query_string_format
135
+ query_string_normalizer Request::NON_RAILS_QUERY_STRING_NORMALIZER
136
+ end
137
+
138
+ # Allows setting default parameters to be appended to each request.
139
+ # Great for api keys and such.
140
+ #
141
+ # class Foo
142
+ # include HTTParty
143
+ # default_params :api_key => 'secret', :another => 'foo'
144
+ # end
145
+ def default_params(h={})
146
+ raise ArgumentError, 'Default params must be a hash' unless h.is_a?(Hash)
147
+ default_options[:default_params] ||= {}
148
+ default_options[:default_params].merge!(h)
149
+ end
150
+
151
+ # Allows setting a default timeout for all HTTP calls
152
+ # Timeout is specified in seconds.
153
+ #
154
+ # class Foo
155
+ # include HTTParty
156
+ # default_timeout 10
157
+ # end
158
+ def default_timeout(t)
159
+ raise ArgumentError, 'Timeout must be an integer or float' unless t && (t.is_a?(Integer) || t.is_a?(Float))
160
+ default_options[:timeout] = t
161
+ end
162
+
163
+ # Set an output stream for debugging, defaults to $stderr.
164
+ # The output stream is passed on to Net::HTTP#set_debug_output.
165
+ #
166
+ # class Foo
167
+ # include HTTParty
168
+ # debug_output $stderr
169
+ # end
170
+ def debug_output(stream = $stderr)
171
+ default_options[:debug_output] = stream
172
+ end
173
+
174
+ # Allows setting HTTP headers to be used for each request.
175
+ #
176
+ # class Foo
177
+ # include HTTParty
178
+ # headers 'Accept' => 'text/html'
179
+ # end
180
+ def headers(h={})
181
+ raise ArgumentError, 'Headers must be a hash' unless h.is_a?(Hash)
182
+ default_options[:headers] ||= {}
183
+ default_options[:headers].merge!(h)
184
+ end
185
+
186
+ def cookies(h={})
187
+ raise ArgumentError, 'Cookies must be a hash' unless h.is_a?(Hash)
188
+ default_cookies.add_cookies(h)
189
+ end
190
+
191
+ # Proceed to the location header when an HTTP response dictates a redirect.
192
+ # Redirects are always followed by default.
193
+ #
194
+ # @example
195
+ # class Foo
196
+ # include HTTParty
197
+ # base_uri 'http://google.com'
198
+ # follow_redirects true
199
+ # end
200
+ def follow_redirects(value = true)
201
+ default_options[:follow_redirects] = value
202
+ end
203
+
204
+ # Allows setting the format with which to parse.
205
+ # Must be one of the allowed formats ie: json, xml
206
+ #
207
+ # class Foo
208
+ # include HTTParty
209
+ # format :json
210
+ # end
211
+ def format(f = nil)
212
+ if f.nil?
213
+ default_options[:format]
214
+ else
215
+ parser(Parser) if parser.nil?
216
+ default_options[:format] = f
217
+ validate_format
218
+ end
219
+ end
220
+
221
+ # Declare whether or not to follow redirects. When true, an
222
+ # {HTTParty::RedirectionTooDeep} error will raise upon encountering a
223
+ # redirect. You can then gain access to the response object via
224
+ # HTTParty::RedirectionTooDeep#response.
225
+ #
226
+ # @see HTTParty::ResponseError#response
227
+ #
228
+ # @example
229
+ # class Foo
230
+ # include HTTParty
231
+ # base_uri 'http://google.com'
232
+ # no_follow true
233
+ # end
234
+ #
235
+ # begin
236
+ # Foo.get('/')
237
+ # rescue HTTParty::RedirectionTooDeep => e
238
+ # puts e.response.body
239
+ # end
240
+ def no_follow(value = false)
241
+ default_options[:no_follow] = value
242
+ end
243
+
244
+ # Declare that you wish to maintain the chosen HTTP method across redirects.
245
+ # The default behavior is to follow redirects via the GET method.
246
+ # If you wish to maintain the original method, you can set this option to true.
247
+ #
248
+ # @example
249
+ # class Foo
250
+ # include HTTParty
251
+ # base_uri 'http://google.com'
252
+ # maintain_method_across_redirects true
253
+ # end
254
+
255
+ def maintain_method_across_redirects(value = true)
256
+ default_options[:maintain_method_across_redirects] = value
257
+ end
258
+
259
+ # Allows setting a PEM file to be used
260
+ #
261
+ # class Foo
262
+ # include HTTParty
263
+ # pem File.read('/home/user/my.pem'), "optional password"
264
+ # end
265
+ def pem(pem_contents, password=nil)
266
+ default_options[:pem] = pem_contents
267
+ default_options[:pem_password] = password
268
+ end
269
+
270
+ # Override the way query strings are normalized.
271
+ # Helpful for overriding the default rails normalization of Array queries.
272
+ #
273
+ # For a query:
274
+ # get '/', :query => {:selected_ids => [1,2,3]}
275
+ #
276
+ # The default query string normalizer returns:
277
+ # /?selected_ids[]=1&selected_ids[]=2&selected_ids[]=3
278
+ #
279
+ # Let's change it to this:
280
+ # /?selected_ids=1&selected_ids=2&selected_ids=3
281
+ #
282
+ # Pass a Proc to the query normalizer which accepts the yielded query.
283
+ #
284
+ # @example Modifying Array query strings
285
+ # class ServiceWrapper
286
+ # include HTTParty
287
+ #
288
+ # query_string_normalizer proc { |query|
289
+ # query.map do |key, value|
290
+ # value.map {|v| "#{key}=#{v}"}
291
+ # end.join('&')
292
+ # }
293
+ # end
294
+ #
295
+ # @param [Proc] normalizer custom query string normalizer.
296
+ # @yield [Hash, String] query string
297
+ # @yieldreturn [Array] an array that will later be joined with '&'
298
+ def query_string_normalizer(normalizer)
299
+ default_options[:query_string_normalizer] = normalizer
300
+ end
301
+
302
+ # Allows setting of SSL version to use. This only works in Ruby 1.9.
303
+ # You can get a list of valid versions from OpenSSL::SSL::SSLContext::METHODS.
304
+ #
305
+ # class Foo
306
+ # include HTTParty
307
+ # ssl_version :SSLv3
308
+ # end
309
+ def ssl_version(version)
310
+ default_options[:ssl_version] = version
311
+ end
312
+
313
+ # Allows setting an OpenSSL certificate authority file
314
+ #
315
+ # class Foo
316
+ # include HTTParty
317
+ # ssl_ca_file '/etc/ssl/certs/ca-certificates.crt'
318
+ # end
319
+ def ssl_ca_file(path)
320
+ default_options[:ssl_ca_file] = path
321
+ end
322
+
323
+ # Allows setting an OpenSSL certificate authority path (directory)
324
+ #
325
+ # class Foo
326
+ # include HTTParty
327
+ # ssl_ca_path '/etc/ssl/certs/'
328
+ # end
329
+ def ssl_ca_path(path)
330
+ default_options[:ssl_ca_path] = path
331
+ end
332
+
333
+ # Allows setting a custom parser for the response.
334
+ #
335
+ # class Foo
336
+ # include HTTParty
337
+ # parser Proc.new {|data| ...}
338
+ # end
339
+ def parser(custom_parser = nil)
340
+ if custom_parser.nil?
341
+ default_options[:parser]
342
+ else
343
+ default_options[:parser] = custom_parser
344
+ validate_format
345
+ end
346
+ end
347
+
348
+ # Allows setting a custom connection_adapter for the http connections
349
+ #
350
+ # @example
351
+ # class Foo
352
+ # include HTTParty
353
+ # connection_adapter Proc.new {|uri, options| ... }
354
+ # end
355
+ #
356
+ # @example provide optional configuration for your connection_adapter
357
+ # class Foo
358
+ # include HTTParty
359
+ # connection_adapter Proc.new {|uri, options| ... }, {:foo => :bar}
360
+ # end
361
+ #
362
+ # @see HTTParty::ConnectionAdapter
363
+ def connection_adapter(custom_adapter = nil, options = nil)
364
+ if custom_adapter.nil?
365
+ default_options[:connection_adapter]
366
+ else
367
+ default_options[:connection_adapter] = custom_adapter
368
+ default_options[:connection_adapter_options] = options
369
+ end
370
+ end
371
+
372
+ # Allows making a get request to a url.
373
+ #
374
+ # class Foo
375
+ # include HTTParty
376
+ # end
377
+ #
378
+ # # Simple get with full url
379
+ # Foo.get('http://foo.com/resource.json')
380
+ #
381
+ # # Simple get with full url and query parameters
382
+ # # ie: http://foo.com/resource.json?limit=10
383
+ # Foo.get('http://foo.com/resource.json', :query => {:limit => 10})
384
+ def get(path, options={}, &block)
385
+ perform_request Net::HTTP::Get, path, options, &block
386
+ end
387
+
388
+ # Allows making a post request to a url.
389
+ #
390
+ # class Foo
391
+ # include HTTParty
392
+ # end
393
+ #
394
+ # # Simple post with full url and setting the body
395
+ # Foo.post('http://foo.com/resources', :body => {:bar => 'baz'})
396
+ #
397
+ # # Simple post with full url using :query option,
398
+ # # which gets set as form data on the request.
399
+ # Foo.post('http://foo.com/resources', :query => {:bar => 'baz'})
400
+ def post(path, options={}, &block)
401
+ perform_request Net::HTTP::Post, path, options, &block
402
+ end
403
+
404
+ # Perform a PATCH request to a path
405
+ def patch(path, options={}, &block)
406
+ perform_request Net::HTTP::Patch, path, options, &block
407
+ end
408
+
409
+ # Perform a PUT request to a path
410
+ def put(path, options={}, &block)
411
+ perform_request Net::HTTP::Put, path, options, &block
412
+ end
413
+
414
+ # Perform a DELETE request to a path
415
+ def delete(path, options={}, &block)
416
+ perform_request Net::HTTP::Delete, path, options, &block
417
+ end
418
+
419
+ # Perform a HEAD request to a path
420
+ def head(path, options={}, &block)
421
+ perform_request Net::HTTP::Head, path, options, &block
422
+ end
423
+
424
+ # Perform an OPTIONS request to a path
425
+ def options(path, options={}, &block)
426
+ perform_request Net::HTTP::Options, path, options, &block
427
+ end
428
+
429
+ def default_options #:nodoc:
430
+ @default_options
431
+ end
432
+
433
+ private
434
+
435
+ def perform_request(http_method, path, options, &block) #:nodoc:
436
+ options = default_options.dup.merge(options)
437
+ process_cookies(options)
438
+ Request.new(http_method, path, options).perform(&block)
439
+ end
440
+
441
+ def process_cookies(options) #:nodoc:
442
+ return unless options[:cookies] || default_cookies.any?
443
+ options[:headers] ||= headers.dup
444
+ options[:headers]["cookie"] = cookies.merge(options.delete(:cookies) || {}).to_cookie_string
445
+ end
446
+
447
+ def validate_format
448
+ if format && parser.respond_to?(:supports_format?) && !parser.supports_format?(format)
449
+ raise UnsupportedFormat, "'#{format.inspect}' Must be one of: #{parser.supported_formats.map{|f| f.to_s}.sort.join(', ')}"
450
+ end
451
+ end
452
+ end
453
+
454
+ def self.normalize_base_uri(url) #:nodoc:
455
+ normalized_url = url.dup
456
+ use_ssl = (normalized_url =~ /^https/) || (normalized_url =~ /:443\b/)
457
+ ends_with_slash = normalized_url =~ /\/$/
458
+
459
+ normalized_url.chop! if ends_with_slash
460
+ normalized_url.gsub!(/^https?:\/\//i, '')
461
+
462
+ "http#{'s' if use_ssl}://#{normalized_url}"
463
+ end
464
+
465
+ class Basement #:nodoc:
466
+ include HTTParty
467
+ end
468
+
469
+ def self.get(*args, &block)
470
+ Basement.get(*args, &block)
471
+ end
472
+
473
+ def self.post(*args, &block)
474
+ Basement.post(*args, &block)
475
+ end
476
+
477
+ def self.patch(*args, &block)
478
+ Basement.patch(*args, &block)
479
+ end
480
+
481
+ def self.put(*args, &block)
482
+ Basement.put(*args, &block)
483
+ end
484
+
485
+ def self.delete(*args, &block)
486
+ Basement.delete(*args, &block)
487
+ end
488
+
489
+ def self.head(*args, &block)
490
+ Basement.head(*args, &block)
491
+ end
492
+
493
+ def self.options(*args, &block)
494
+ Basement.options(*args, &block)
495
+ end
496
+ end
497
+
498
+ require 'httparty/core_extensions'
499
+ require 'httparty/hash_conversions'
500
+ require 'httparty/exceptions'
501
+ require 'httparty/parser'
502
+ require 'httparty/request'
503
+ require 'httparty/response'