rest-man 1.0.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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/multi-matrix-test.yml +35 -0
  3. data/.github/workflows/single-matrix-test.yml +27 -0
  4. data/.gitignore +13 -0
  5. data/.mailmap +10 -0
  6. data/.rspec +2 -0
  7. data/.rubocop +2 -0
  8. data/.rubocop-disables.yml +386 -0
  9. data/.rubocop.yml +8 -0
  10. data/AUTHORS +106 -0
  11. data/CHANGELOG.md +7 -0
  12. data/Gemfile +11 -0
  13. data/LICENSE +21 -0
  14. data/README.md +843 -0
  15. data/Rakefile +140 -0
  16. data/exe/restman +92 -0
  17. data/lib/rest-man.rb +2 -0
  18. data/lib/rest_man.rb +2 -0
  19. data/lib/restman/abstract_response.rb +252 -0
  20. data/lib/restman/exceptions.rb +238 -0
  21. data/lib/restman/params_array.rb +72 -0
  22. data/lib/restman/payload.rb +234 -0
  23. data/lib/restman/platform.rb +49 -0
  24. data/lib/restman/raw_response.rb +49 -0
  25. data/lib/restman/request.rb +859 -0
  26. data/lib/restman/resource.rb +178 -0
  27. data/lib/restman/response.rb +90 -0
  28. data/lib/restman/utils.rb +274 -0
  29. data/lib/restman/version.rb +8 -0
  30. data/lib/restman/windows/root_certs.rb +105 -0
  31. data/lib/restman/windows.rb +8 -0
  32. data/lib/restman.rb +183 -0
  33. data/matrixeval.yml +73 -0
  34. data/rest-man.gemspec +41 -0
  35. data/spec/ISS.jpg +0 -0
  36. data/spec/cassettes/request_httpbin_with_basic_auth.yml +83 -0
  37. data/spec/cassettes/request_httpbin_with_cookies.yml +49 -0
  38. data/spec/cassettes/request_httpbin_with_cookies_2.yml +94 -0
  39. data/spec/cassettes/request_httpbin_with_cookies_3.yml +49 -0
  40. data/spec/cassettes/request_httpbin_with_encoding_deflate.yml +45 -0
  41. data/spec/cassettes/request_httpbin_with_encoding_deflate_and_accept_headers.yml +44 -0
  42. data/spec/cassettes/request_httpbin_with_encoding_gzip.yml +45 -0
  43. data/spec/cassettes/request_httpbin_with_encoding_gzip_and_accept_headers.yml +44 -0
  44. data/spec/cassettes/request_httpbin_with_user_agent.yml +44 -0
  45. data/spec/cassettes/request_mozilla_org.yml +151 -0
  46. data/spec/cassettes/request_mozilla_org_callback_returns_true.yml +178 -0
  47. data/spec/cassettes/request_mozilla_org_with_system_cert.yml +152 -0
  48. data/spec/cassettes/request_mozilla_org_with_system_cert_and_callback.yml +151 -0
  49. data/spec/helpers.rb +54 -0
  50. data/spec/integration/_lib.rb +1 -0
  51. data/spec/integration/capath_digicert/README +8 -0
  52. data/spec/integration/capath_digicert/ce5e74ef.0 +1 -0
  53. data/spec/integration/capath_digicert/digicert.crt +20 -0
  54. data/spec/integration/capath_digicert/update +1 -0
  55. data/spec/integration/capath_verisign/415660c1.0 +14 -0
  56. data/spec/integration/capath_verisign/7651b327.0 +14 -0
  57. data/spec/integration/capath_verisign/README +8 -0
  58. data/spec/integration/capath_verisign/verisign.crt +14 -0
  59. data/spec/integration/certs/digicert.crt +20 -0
  60. data/spec/integration/certs/verisign.crt +14 -0
  61. data/spec/integration/httpbin_spec.rb +137 -0
  62. data/spec/integration/integration_spec.rb +118 -0
  63. data/spec/integration/request_spec.rb +134 -0
  64. data/spec/spec_helper.rb +40 -0
  65. data/spec/unit/_lib.rb +1 -0
  66. data/spec/unit/abstract_response_spec.rb +145 -0
  67. data/spec/unit/exceptions_spec.rb +108 -0
  68. data/spec/unit/params_array_spec.rb +36 -0
  69. data/spec/unit/payload_spec.rb +295 -0
  70. data/spec/unit/raw_response_spec.rb +22 -0
  71. data/spec/unit/request2_spec.rb +54 -0
  72. data/spec/unit/request_spec.rb +1205 -0
  73. data/spec/unit/resource_spec.rb +134 -0
  74. data/spec/unit/response_spec.rb +252 -0
  75. data/spec/unit/restclient_spec.rb +80 -0
  76. data/spec/unit/utils_spec.rb +147 -0
  77. data/spec/unit/windows/root_certs_spec.rb +22 -0
  78. metadata +336 -0
data/README.md ADDED
@@ -0,0 +1,843 @@
1
+ # REST Man -- simple DSL for accessing HTTP and REST resources
2
+
3
+ A simple HTTP and REST client for Ruby, inspired by the Sinatra's microframework style
4
+ of specifying actions: get, put, post, delete.
5
+
6
+ This is a fork version of [rest-client](https://github.com/rest-client/rest-client)
7
+
8
+ ## Requirements
9
+
10
+ MRI Ruby 2.0 and newer are supported. Alternative interpreters compatible with
11
+ 2.0+ should work as well.
12
+
13
+ Earlier Ruby versions such as 1.8.7, 1.9.2, and 1.9.3 are no longer supported. These
14
+ versions no longer have any official support, and do not receive security
15
+ updates.
16
+
17
+ The rest-man gem depends on these other gems for usage at runtime:
18
+
19
+ * [mime-types](http://rubygems.org/gems/mime-types)
20
+ * [netrc](http://rubygems.org/gems/netrc)
21
+ * [http-accept](https://rubygems.org/gems/http-accept)
22
+ * [http-cookie](https://rubygems.org/gems/http-cookie)
23
+
24
+ There are also several development dependencies. It's recommended to use
25
+ [bundler](http://bundler.io/) to manage these dependencies for hacking on
26
+ rest-man.
27
+
28
+ ## Usage: Raw URL
29
+
30
+ Basic usage:
31
+
32
+ ```ruby
33
+ require 'rest-man'
34
+
35
+ RestMan.get(url, headers={})
36
+
37
+ RestMan.post(url, payload, headers={})
38
+ ```
39
+
40
+ In the high level helpers, only POST, PATCH, and PUT take a payload argument.
41
+ To pass a payload with other HTTP verbs or to pass more advanced options, use
42
+ `RestMan::Request.execute` instead.
43
+
44
+ More detailed examples:
45
+
46
+ ```ruby
47
+ require 'rest-man'
48
+
49
+ RestMan.get 'http://example.com/resource'
50
+
51
+ RestMan.get 'http://example.com/resource', {params: {id: 50, 'foo' => 'bar'}}
52
+
53
+ RestMan.get 'https://user:password@example.com/private/resource', {accept: :json}
54
+
55
+ RestMan.post 'http://example.com/resource', {param1: 'one', nested: {param2: 'two'}}
56
+
57
+ RestMan.post "http://example.com/resource", {'x' => 1}.to_json, {content_type: :json, accept: :json}
58
+
59
+ RestMan.delete 'http://example.com/resource'
60
+
61
+ >> response = RestMan.get 'http://example.com/resource'
62
+ => <RestMan::Response 200 "<!doctype h...">
63
+ >> response.code
64
+ => 200
65
+ >> response.cookies
66
+ => {"Foo"=>"BAR", "QUUX"=>"QUUUUX"}
67
+ >> response.headers
68
+ => {:content_type=>"text/html; charset=utf-8", :cache_control=>"private" ... }
69
+ >> response.body
70
+ => "<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n ..."
71
+
72
+ RestMan.post( url,
73
+ {
74
+ :transfer => {
75
+ :path => '/foo/bar',
76
+ :owner => 'that_guy',
77
+ :group => 'those_guys'
78
+ },
79
+ :upload => {
80
+ :file => File.new(path, 'rb')
81
+ }
82
+ })
83
+ ```
84
+ ## Passing advanced options
85
+
86
+ The top level helper methods like RestMan.get accept a headers hash as
87
+ their last argument and don't allow passing more complex options. But these
88
+ helpers are just thin wrappers around `RestMan::Request.execute`.
89
+
90
+ ```ruby
91
+ RestMan::Request.execute(method: :get, url: 'http://example.com/resource',
92
+ timeout: 10)
93
+
94
+ RestMan::Request.execute(method: :get, url: 'http://example.com/resource',
95
+ ssl_ca_file: 'myca.pem',
96
+ ssl_ciphers: 'AESGCM:!aNULL')
97
+ ```
98
+ You can also use this to pass a payload for HTTP verbs like DELETE, where the
99
+ `RestMan.delete` helper doesn't accept a payload.
100
+
101
+ ```ruby
102
+ RestMan::Request.execute(method: :delete, url: 'http://example.com/resource',
103
+ payload: 'foo', headers: {myheader: 'bar'})
104
+ ```
105
+
106
+ Due to unfortunate choices in the original API, the params used to populate the
107
+ query string are actually taken out of the headers hash. So if you want to pass
108
+ both the params hash and more complex options, use the special key
109
+ `:params` in the headers hash. This design may change in a future major
110
+ release.
111
+
112
+ ```ruby
113
+ RestMan::Request.execute(method: :get, url: 'http://example.com/resource',
114
+ timeout: 10, headers: {params: {foo: 'bar'}})
115
+
116
+ ➔ GET http://example.com/resource?foo=bar
117
+ ```
118
+
119
+ ## Multipart
120
+
121
+ Yeah, that's right! This does multipart sends for you!
122
+
123
+ ```ruby
124
+ RestMan.post '/data', :myfile => File.new("/path/to/image.jpg", 'rb')
125
+ ```
126
+
127
+ This does two things for you:
128
+
129
+ - Auto-detects that you have a File value sends it as multipart
130
+ - Auto-detects the mime of the file and sets it in the HEAD of the payload for each entry
131
+
132
+ If you are sending params that do not contain a File object but the payload needs to be multipart then:
133
+
134
+ ```ruby
135
+ RestMan.post '/data', {:foo => 'bar', :multipart => true}
136
+ ```
137
+
138
+ ## Usage: ActiveResource-Style
139
+
140
+ ```ruby
141
+ resource = RestMan::Resource.new 'http://example.com/resource'
142
+ resource.get
143
+
144
+ private_resource = RestMan::Resource.new 'https://example.com/private/resource', 'user', 'pass'
145
+ private_resource.put File.read('pic.jpg'), :content_type => 'image/jpg'
146
+ ```
147
+
148
+ See RestMan::Resource module docs for details.
149
+
150
+ ## Usage: Resource Nesting
151
+
152
+ ```ruby
153
+ site = RestMan::Resource.new('http://example.com')
154
+ site['posts/1/comments'].post 'Good article.', :content_type => 'text/plain'
155
+ ```
156
+ See `RestMan::Resource` docs for details.
157
+
158
+ ## Exceptions (see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html)
159
+
160
+ - for result codes between `200` and `207`, a `RestMan::Response` will be returned
161
+ - for result codes `301`, `302` or `307`, the redirection will be followed if the request is a `GET` or a `HEAD`
162
+ - for result code `303`, the redirection will be followed and the request transformed into a `GET`
163
+ - for other cases, a `RestMan::ExceptionWithResponse` holding the Response will be raised; a specific exception class will be thrown for known error codes
164
+ - call `.response` on the exception to get the server's response
165
+
166
+ ```ruby
167
+ >> RestMan.get 'http://example.com/nonexistent'
168
+ Exception: RestMan::NotFound: 404 Not Found
169
+
170
+ >> begin
171
+ RestMan.get 'http://example.com/nonexistent'
172
+ rescue RestMan::ExceptionWithResponse => e
173
+ e.response
174
+ end
175
+ => <RestMan::Response 404 "<!doctype h...">
176
+ ```
177
+
178
+ ### Other exceptions
179
+
180
+ While most exceptions have been collected under `RestMan::RequestFailed` aka
181
+ `RestMan::ExceptionWithResponse`, there are a few quirky exceptions that
182
+ have been kept for backwards compatibility.
183
+
184
+ RestMan will propagate up exceptions like socket errors without modification:
185
+
186
+ ```ruby
187
+ >> RestMan.get 'http://localhost:12345'
188
+ Exception: Errno::ECONNREFUSED: Connection refused - connect(2) for "localhost" port 12345
189
+ ```
190
+
191
+ RestMan handles a few specific error cases separately in order to give
192
+ better error messages. These will hopefully be cleaned up in a future major
193
+ release.
194
+
195
+ `RestMan::ServerBrokeConnection` is translated from `EOFError` to give a
196
+ better error message.
197
+
198
+ `RestMan::SSLCertificateNotVerified` is raised when HTTPS validation fails.
199
+ Other `OpenSSL::SSL::SSLError` errors are raised as is.
200
+
201
+ ### Redirection
202
+
203
+ By default, rest-man will follow HTTP 30x redirection requests.
204
+
205
+ __New in 2.0:__ `RestMan::Response` exposes a `#history` method that returns
206
+ a list of each response received in a redirection chain.
207
+
208
+ ```ruby
209
+ >> r = RestMan.get('http://httpbin.org/redirect/2')
210
+ => <RestMan::Response 200 "{\n \"args\":...">
211
+
212
+ # see each response in the redirect chain
213
+ >> r.history
214
+ => [<RestMan::Response 302 "<!DOCTYPE H...">, <RestMan::Response 302 "">]
215
+
216
+ # see each requested URL
217
+ >> r.request.url
218
+ => "http://httpbin.org/get"
219
+ >> r.history.map {|x| x.request.url}
220
+ => ["http://httpbin.org/redirect/2", "http://httpbin.org/relative-redirect/1"]
221
+ ```
222
+
223
+ #### Manually following redirection
224
+
225
+ To disable automatic redirection, set `:max_redirects => 0`.
226
+
227
+ __New in 2.0:__ Prior versions of rest-man would raise
228
+ `RestMan::MaxRedirectsReached`, with no easy way to access the server's
229
+ response. In 2.0, rest-man raises the normal
230
+ `RestMan::ExceptionWithResponse` as it would with any other non-HTTP-20x
231
+ response.
232
+
233
+ ```ruby
234
+ >> RestMan::Request.execute(method: :get, url: 'http://httpbin.org/redirect/1')
235
+ => RestMan::Response 200 "{\n "args":..."
236
+
237
+ >> RestMan::Request.execute(method: :get, url: 'http://httpbin.org/redirect/1', max_redirects: 0)
238
+ RestMan::Found: 302 Found
239
+ ```
240
+
241
+ To manually follow redirection, you can call `Response#follow_redirection`. Or
242
+ you could of course inspect the result and choose custom behavior.
243
+
244
+ ```ruby
245
+ >> RestMan::Request.execute(method: :get, url: 'http://httpbin.org/redirect/1', max_redirects: 0)
246
+ RestMan::Found: 302 Found
247
+ >> begin
248
+ RestMan::Request.execute(method: :get, url: 'http://httpbin.org/redirect/1', max_redirects: 0)
249
+ rescue RestMan::ExceptionWithResponse => err
250
+ end
251
+ >> err
252
+ => #<RestMan::Found: 302 Found>
253
+ >> err.response
254
+ => RestMan::Response 302 "<!DOCTYPE H..."
255
+ >> err.response.headers[:location]
256
+ => "/get"
257
+ >> err.response.follow_redirection
258
+ => RestMan::Response 200 "{\n "args":..."
259
+ ```
260
+
261
+ ## Result handling
262
+
263
+ The result of a `RestMan::Request` is a `RestMan::Response` object.
264
+
265
+ `RestMan::Response` objects are a subclass of `String`.
266
+
267
+ Response objects have several useful methods. (See the class rdoc for more details.)
268
+
269
+ - `Response#code`: The HTTP response code
270
+ - `Response#body`: The response body as a string. (AKA .to_s)
271
+ - `Response#headers`: A hash of HTTP response headers
272
+ - `Response#raw_headers`: A hash of HTTP response headers as unprocessed arrays
273
+ - `Response#cookies`: A hash of HTTP cookies set by the server
274
+ - `Response#cookie_jar`: <em>New in 1.8</em> An HTTP::CookieJar of cookies
275
+ - `Response#request`: The RestMan::Request object used to make the request
276
+ - `Response#history`: <em>New in 2.0</em> If redirection was followed, a list of prior Response objects
277
+
278
+ ```ruby
279
+ RestMan.get('http://example.com')
280
+ ➔ <RestMan::Response 200 "<!doctype h...">
281
+
282
+ begin
283
+ RestMan.get('http://example.com/notfound')
284
+ rescue RestMan::ExceptionWithResponse => err
285
+ err.response
286
+ end
287
+ ➔ <RestMan::Response 404 "<!doctype h...">
288
+ ```
289
+
290
+ ### Response callbacks, error handling
291
+
292
+ A block can be passed to the RestMan method. This block will then be called with the Response.
293
+ Response.return! can be called to invoke the default response's behavior.
294
+
295
+ ```ruby
296
+ # Don't raise exceptions but return the response
297
+ >> RestMan.get('http://example.com/nonexistent') {|response, request, result| response }
298
+ => <RestMan::Response 404 "<!doctype h...">
299
+ ```
300
+
301
+ ```ruby
302
+ # Manage a specific error code
303
+ RestMan.get('http://example.com/resource') { |response, request, result, &block|
304
+ case response.code
305
+ when 200
306
+ p "It worked !"
307
+ response
308
+ when 423
309
+ raise SomeCustomExceptionIfYouWant
310
+ else
311
+ response.return!(&block)
312
+ end
313
+ }
314
+ ```
315
+
316
+ But note that it may be more straightforward to use exceptions to handle
317
+ different HTTP error response cases:
318
+
319
+ ```ruby
320
+ begin
321
+ resp = RestMan.get('http://example.com/resource')
322
+ rescue RestMan::Unauthorized, RestMan::Forbidden => err
323
+ puts 'Access denied'
324
+ return err.response
325
+ rescue RestMan::ImATeapot => err
326
+ puts 'The server is a teapot! # RFC 2324'
327
+ return err.response
328
+ else
329
+ puts 'It worked!'
330
+ return resp
331
+ end
332
+ ```
333
+
334
+ For GET and HEAD requests, rest-man automatically follows redirection. For
335
+ other HTTP verbs, call `.follow_redirection` on the response object (works both
336
+ in block form and in exception form).
337
+
338
+ ```ruby
339
+ # Follow redirections for all request types and not only for get and head
340
+ # RFC : "If the 301, 302 or 307 status code is received in response to a request other than GET or HEAD,
341
+ # the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user,
342
+ # since this might change the conditions under which the request was issued."
343
+
344
+ # block style
345
+ RestMan.post('http://example.com/redirect', 'body') { |response, request, result|
346
+ case response.code
347
+ when 301, 302, 307
348
+ response.follow_redirection
349
+ else
350
+ response.return!
351
+ end
352
+ }
353
+
354
+ # exception style by explicit classes
355
+ begin
356
+ RestMan.post('http://example.com/redirect', 'body')
357
+ rescue RestMan::MovedPermanently,
358
+ RestMan::Found,
359
+ RestMan::TemporaryRedirect => err
360
+ err.response.follow_redirection
361
+ end
362
+
363
+ # exception style by response code
364
+ begin
365
+ RestMan.post('http://example.com/redirect', 'body')
366
+ rescue RestMan::ExceptionWithResponse => err
367
+ case err.http_code
368
+ when 301, 302, 307
369
+ err.response.follow_redirection
370
+ else
371
+ raise
372
+ end
373
+ end
374
+ ```
375
+
376
+ ## Non-normalized URIs
377
+
378
+ If you need to normalize URIs, e.g. to work with International Resource Identifiers (IRIs),
379
+ use the Addressable gem (https://github.com/sporkmonger/addressable/) in your code:
380
+
381
+ ```ruby
382
+ require 'addressable/uri'
383
+ RestMan.get(Addressable::URI.parse("http://www.詹姆斯.com/").normalize.to_str)
384
+ ```
385
+
386
+ ## Lower-level access
387
+
388
+ For cases not covered by the general API, you can use the `RestMan::Request` class, which provides a lower-level API.
389
+
390
+ You can:
391
+
392
+ - specify ssl parameters
393
+ - override cookies
394
+ - manually handle the response (e.g. to operate on it as a stream rather than reading it all into memory)
395
+
396
+ See `RestMan::Request`'s documentation for more information.
397
+
398
+ ### Streaming request payload
399
+
400
+ RestMan will try to stream any file-like payload rather than reading it into
401
+ memory. This happens through `RestMan::Payload::Streamed`, which is
402
+ automatically called internally by `RestMan::Payload.generate` on anything
403
+ with a `read` method.
404
+
405
+ ```ruby
406
+ >> r = RestMan.put('http://httpbin.org/put', File.open('/tmp/foo.txt', 'r'),
407
+ content_type: 'text/plain')
408
+ => <RestMan::Response 200 "{\n \"args\":...">
409
+ ```
410
+
411
+ In Multipart requests, RestMan will also stream file handles passed as Hash
412
+ (or ParamsArray).
413
+
414
+ ```ruby
415
+ >> r = RestMan.put('http://httpbin.org/put',
416
+ {file_a: File.open('a.txt', 'r'),
417
+ file_b: File.open('b.txt', 'r')})
418
+ => <RestMan::Response 200 "{\n \"args\":...">
419
+
420
+ # received by server as two file uploads with multipart/form-data
421
+ >> JSON.parse(r)['files'].keys
422
+ => ['file_a', 'file_b']
423
+ ```
424
+
425
+ ### Streaming responses
426
+
427
+ Normally, when you use `RestMan.get` or the lower level
428
+ `RestMan::Request.execute method: :get` to retrieve data, the entire
429
+ response is buffered in memory and returned as the response to the call.
430
+
431
+ However, if you are retrieving a large amount of data, for example a Docker
432
+ image, an iso, or any other large file, you may want to stream the response
433
+ directly to disk rather than loading it in memory. If you have a very large
434
+ file, it may become *impossible* to load it into memory.
435
+
436
+ There are two main ways to do this:
437
+
438
+ #### `raw_response`, saves into Tempfile
439
+
440
+ If you pass `raw_response: true` to `RestMan::Request.execute`, it will save
441
+ the response body to a temporary file (using `Tempfile`) and return a
442
+ `RestMan::RawResponse` object rather than a `RestMan::Response`.
443
+
444
+ Note that the tempfile created by `Tempfile.new` will be in `Dir.tmpdir`
445
+ (usually `/tmp/`), which you can override to store temporary files in a
446
+ different location. This file will be unlinked when it is dereferenced.
447
+
448
+ If logging is enabled, this will also print download progress.
449
+ Customize the interval with `:stream_log_percent` (defaults to
450
+ 10 for printing a message every 10% complete).
451
+
452
+ For example:
453
+
454
+ ```ruby
455
+ >> raw = RestMan::Request.execute(
456
+ method: :get,
457
+ url: 'http://releases.ubuntu.com/16.04.2/ubuntu-16.04.2-desktop-amd64.iso',
458
+ raw_response: true)
459
+ => <RestMan::RawResponse @code=200, @file=#<Tempfile:/tmp/rest-man.20170522-5346-1pptjm1>, @request=<RestMan::Request @method="get", @url="http://releases.ubuntu.com/16.04.2/ubuntu-16.04.2-desktop-amd64.iso">>
460
+ >> raw.file.size
461
+ => 1554186240
462
+ >> raw.file.path
463
+ => "/tmp/rest-man.20170522-5346-1pptjm1"
464
+ raw.file.path
465
+ => "/tmp/rest-man.20170522-5346-1pptjm1"
466
+
467
+ >> require 'digest/sha1'
468
+ >> Digest::SHA1.file(raw.file.path).hexdigest
469
+ => "4375b73e3a1aa305a36320ffd7484682922262b3"
470
+ ```
471
+
472
+ #### `block_response`, receives raw Net::HTTPResponse
473
+
474
+ If you want to stream the data from the response to a file as it comes, rather
475
+ than entirely in memory, you can also pass `RestMan::Request.execute` a
476
+ parameter `:block_response` to which you pass a block/proc. This block receives
477
+ the raw unmodified Net::HTTPResponse object from Net::HTTP, which you can use
478
+ to stream directly to a file as each chunk is received.
479
+
480
+ Note that this bypasses all the usual HTTP status code handling, so you will
481
+ want to do you own checking for HTTP 20x response codes, redirects, etc.
482
+
483
+ The following is an example:
484
+
485
+ ````ruby
486
+ File.open('/some/output/file', 'w') {|f|
487
+ block = proc { |response|
488
+ response.read_body do |chunk|
489
+ f.write chunk
490
+ end
491
+ }
492
+ RestMan::Request.execute(method: :get,
493
+ url: 'http://example.com/some/really/big/file.img',
494
+ block_response: block)
495
+ }
496
+ ````
497
+
498
+ ## Shell
499
+
500
+ The restman shell command gives an IRB session with RestMan already loaded:
501
+
502
+ ```ruby
503
+ $ restman
504
+ >> RestMan.get 'http://example.com'
505
+ ```
506
+
507
+ Specify a URL argument for get/post/put/delete on that resource:
508
+
509
+ ```ruby
510
+ $ restman http://example.com
511
+ >> put '/resource', 'data'
512
+ ```
513
+
514
+ Add a user and password for authenticated resources:
515
+
516
+ ```ruby
517
+ $ restman https://example.com user pass
518
+ >> delete '/private/resource'
519
+ ```
520
+
521
+ Create ~/.restman for named sessions:
522
+
523
+ ```ruby
524
+ sinatra:
525
+ url: http://localhost:4567
526
+ rack:
527
+ url: http://localhost:9292
528
+ private_site:
529
+ url: http://example.com
530
+ username: user
531
+ password: pass
532
+ ```
533
+
534
+ Then invoke:
535
+
536
+ ```ruby
537
+ $ restman private_site
538
+ ```
539
+
540
+ Use as a one-off, curl-style:
541
+
542
+ ```ruby
543
+ $ restman get http://example.com/resource > output_body
544
+
545
+ $ restman put http://example.com/resource < input_body
546
+ ```
547
+
548
+ ## Logging
549
+
550
+ To enable logging globally you can:
551
+
552
+ - set RestMan.log with a Ruby Logger
553
+
554
+ ```ruby
555
+ RestMan.log = STDOUT
556
+ ```
557
+
558
+ - or set an environment variable to avoid modifying the code (in this case you can use a file name, "stdout" or "stderr"):
559
+
560
+ ```ruby
561
+ $ RESTCLIENT_LOG=stdout path/to/my/program
562
+ ```
563
+
564
+ You can also set individual loggers when instantiating a Resource or making an
565
+ individual request:
566
+
567
+ ```ruby
568
+ resource = RestMan::Resource.new 'http://example.com/resource', log: Logger.new(STDOUT)
569
+ ```
570
+
571
+ ```ruby
572
+ RestMan::Request.execute(method: :get, url: 'http://example.com/foo', log: Logger.new(STDERR))
573
+ ```
574
+
575
+ All options produce logs like this:
576
+
577
+ ```ruby
578
+ RestMan.get "http://some/resource"
579
+ # => 200 OK | text/html 250 bytes
580
+ RestMan.put "http://some/resource", "payload"
581
+ # => 401 Unauthorized | application/xml 340 bytes
582
+ ```
583
+
584
+ Note that these logs are valid Ruby, so you can paste them into the `restman`
585
+ shell or a script to replay your sequence of rest calls.
586
+
587
+ ## Proxy
588
+
589
+ All calls to RestMan, including Resources, will use the proxy specified by
590
+ `RestMan.proxy`:
591
+
592
+ ```ruby
593
+ RestMan.proxy = "http://proxy.example.com/"
594
+ RestMan.get "http://some/resource"
595
+ # => response from some/resource as proxied through proxy.example.com
596
+ ```
597
+
598
+ Often the proxy URL is set in an environment variable, so you can do this to
599
+ use whatever proxy the system is configured to use:
600
+
601
+ ```ruby
602
+ RestMan.proxy = ENV['http_proxy']
603
+ ```
604
+
605
+ __New in 2.0:__ Specify a per-request proxy by passing the :proxy option to
606
+ RestMan::Request. This will override any proxies set by environment variable
607
+ or by the global `RestMan.proxy` value.
608
+
609
+ ```ruby
610
+ RestMan::Request.execute(method: :get, url: 'http://example.com',
611
+ proxy: 'http://proxy.example.com')
612
+ # => single request proxied through the proxy
613
+ ```
614
+
615
+ This can be used to disable the use of a proxy for a particular request.
616
+
617
+ ```ruby
618
+ RestMan.proxy = "http://proxy.example.com/"
619
+ RestMan::Request.execute(method: :get, url: 'http://example.com', proxy: nil)
620
+ # => single request sent without a proxy
621
+ ```
622
+
623
+ ## Query parameters
624
+
625
+ Rest-client can render a hash as HTTP query parameters for GET/HEAD/DELETE
626
+ requests or as HTTP post data in `x-www-form-urlencoded` format for POST
627
+ requests.
628
+
629
+ Even though there is no standard specifying how this should
630
+ work, rest-man follows a similar convention to the one used by Rack / Rails
631
+ servers for handling arrays, nested hashes, and null values.
632
+
633
+ The implementation in
634
+ [./lib/rest-man/utils.rb](RestMan::Utils.encode_query_string)
635
+ closely follows
636
+ [Rack::Utils.build_nested_query](http://www.rubydoc.info/gems/rack/Rack/Utils#build_nested_query-class_method),
637
+ but treats empty arrays and hashes as `nil`. (Rack drops them entirely, which
638
+ is confusing behavior.)
639
+
640
+ If you don't like this behavior and want more control, just serialize params
641
+ yourself (e.g. with `URI.encode_www_form`) and add the query string to the URL
642
+ directly for GET parameters or pass the payload as a string for POST requests.
643
+
644
+ Basic GET params:
645
+ ```ruby
646
+ RestMan.get('https://httpbin.org/get', params: {foo: 'bar', baz: 'qux'})
647
+ # GET "https://httpbin.org/get?foo=bar&baz=qux"
648
+ ```
649
+
650
+ Basic `x-www-form-urlencoded` POST params:
651
+ ```ruby
652
+ >> r = RestMan.post('https://httpbin.org/post', {foo: 'bar', baz: 'qux'})
653
+ # POST "https://httpbin.org/post", data: "foo=bar&baz=qux"
654
+ => <RestMan::Response 200 "{\n \"args\":...">
655
+ >> JSON.parse(r.body)
656
+ => {"args"=>{},
657
+ "data"=>"",
658
+ "files"=>{},
659
+ "form"=>{"baz"=>"qux", "foo"=>"bar"},
660
+ "headers"=>
661
+ {"Accept"=>"*/*",
662
+ "Accept-Encoding"=>"gzip, deflate",
663
+ "Content-Length"=>"15",
664
+ "Content-Type"=>"application/x-www-form-urlencoded",
665
+ "Host"=>"httpbin.org"},
666
+ "json"=>nil,
667
+ "url"=>"https://httpbin.org/post"}
668
+ ```
669
+
670
+ JSON payload: rest-man does not speak JSON natively, so serialize your
671
+ payload to a string before passing it to rest-man.
672
+ ```ruby
673
+ >> payload = {'name' => 'newrepo', 'description': 'A new repo'}
674
+ >> RestMan.post('https://api.github.com/user/repos', payload.to_json, content_type: :json)
675
+ => <RestMan::Response 201 "{\"id\":75149...">
676
+ ```
677
+
678
+ Advanced GET params (arrays):
679
+ ```ruby
680
+ >> r = RestMan.get('https://http-params.herokuapp.com/get', params: {foo: [1,2,3]})
681
+ # GET "https://http-params.herokuapp.com/get?foo[]=1&foo[]=2&foo[]=3"
682
+ => <RestMan::Response 200 "Method: GET...">
683
+ >> puts r.body
684
+ query_string: "foo[]=1&foo[]=2&foo[]=3"
685
+ decoded: "foo[]=1&foo[]=2&foo[]=3"
686
+
687
+ GET:
688
+ {"foo"=>["1", "2", "3"]}
689
+ ```
690
+
691
+ Advanced GET params (nested hashes):
692
+ ```ruby
693
+ >> r = RestMan.get('https://http-params.herokuapp.com/get', params: {outer: {foo: 123, bar: 456}})
694
+ # GET "https://http-params.herokuapp.com/get?outer[foo]=123&outer[bar]=456"
695
+ => <RestMan::Response 200 "Method: GET...">
696
+ >> puts r.body
697
+ ...
698
+ query_string: "outer[foo]=123&outer[bar]=456"
699
+ decoded: "outer[foo]=123&outer[bar]=456"
700
+
701
+ GET:
702
+ {"outer"=>{"foo"=>"123", "bar"=>"456"}}
703
+ ```
704
+
705
+ __New in 2.0:__ The new `RestMan::ParamsArray` class allows callers to
706
+ provide ordering even to structured parameters. This is useful for unusual
707
+ cases where the server treats the order of parameters as significant or you
708
+ want to pass a particular key multiple times.
709
+
710
+ Multiple fields with the same name using ParamsArray:
711
+ ```ruby
712
+ >> RestMan.get('https://httpbin.org/get', params:
713
+ RestMan::ParamsArray.new([[:foo, 1], [:foo, 2]]))
714
+ # GET "https://httpbin.org/get?foo=1&foo=2"
715
+ ```
716
+
717
+ Nested ParamsArray:
718
+ ```ruby
719
+ >> RestMan.get('https://httpbin.org/get', params:
720
+ {foo: RestMan::ParamsArray.new([[:a, 1], [:a, 2]])})
721
+ # GET "https://httpbin.org/get?foo[a]=1&foo[a]=2"
722
+ ```
723
+
724
+ ## Headers
725
+
726
+ Request headers can be set by passing a ruby hash containing keys and values
727
+ representing header names and values:
728
+
729
+ ```ruby
730
+ # GET request with modified headers
731
+ RestMan.get 'http://example.com/resource', {:Authorization => 'Bearer cT0febFoD5lxAlNAXHo6g'}
732
+
733
+ # POST request with modified headers
734
+ RestMan.post 'http://example.com/resource', {:foo => 'bar', :baz => 'qux'}, {:Authorization => 'Bearer cT0febFoD5lxAlNAXHo6g'}
735
+
736
+ # DELETE request with modified headers
737
+ RestMan.delete 'http://example.com/resource', {:Authorization => 'Bearer cT0febFoD5lxAlNAXHo6g'}
738
+ ```
739
+
740
+ ## Timeouts
741
+
742
+ By default the timeout for a request is 60 seconds. Timeouts for your request can
743
+ be adjusted by setting the `timeout:` to the number of seconds that you would like
744
+ the request to wait. Setting `timeout:` will override both `read_timeout:` and `open_timeout:`.
745
+
746
+ ```ruby
747
+ RestMan::Request.execute(method: :get, url: 'http://example.com/resource',
748
+ timeout: 120)
749
+ ```
750
+
751
+ Additionally, you can set `read_timeout:` and `open_timeout:` separately.
752
+
753
+ ```ruby
754
+ RestMan::Request.execute(method: :get, url: 'http://example.com/resource',
755
+ read_timeout: 120, open_timeout: 240)
756
+ ```
757
+
758
+ ## Cookies
759
+
760
+ Request and Response objects know about HTTP cookies, and will automatically
761
+ extract and set headers for them as needed:
762
+
763
+ ```ruby
764
+ response = RestMan.get 'http://example.com/action_which_sets_session_id'
765
+ response.cookies
766
+ # => {"_applicatioN_session_id" => "1234"}
767
+
768
+ response2 = RestMan.post(
769
+ 'http://localhost:3000/',
770
+ {:param1 => "foo"},
771
+ {:cookies => {:session_id => "1234"}}
772
+ )
773
+ # ...response body
774
+ ```
775
+ ### Full cookie jar support (new in 1.8)
776
+
777
+ The original cookie implementation was very naive and ignored most of the
778
+ cookie RFC standards.
779
+ __New in 1.8__: An HTTP::CookieJar of cookies
780
+
781
+ Response objects now carry a cookie_jar method that exposes an HTTP::CookieJar
782
+ of cookies, which supports full standards compliant behavior.
783
+
784
+ ## SSL/TLS support
785
+
786
+ Various options are supported for configuring rest-man's TLS settings. By
787
+ default, rest-man will verify certificates using the system's CA store on
788
+ all platforms. (This is intended to be similar to how browsers behave.) You can
789
+ specify an :ssl_ca_file, :ssl_ca_path, or :ssl_cert_store to customize the
790
+ certificate authorities accepted.
791
+
792
+ ### SSL Client Certificates
793
+
794
+ ```ruby
795
+ RestMan::Resource.new(
796
+ 'https://example.com',
797
+ :ssl_client_cert => OpenSSL::X509::Certificate.new(File.read("cert.pem")),
798
+ :ssl_client_key => OpenSSL::PKey::RSA.new(File.read("key.pem"), "passphrase, if any"),
799
+ :ssl_ca_file => "ca_certificate.pem",
800
+ :verify_ssl => OpenSSL::SSL::VERIFY_PEER
801
+ ).get
802
+ ```
803
+ Self-signed certificates can be generated with the openssl command-line tool.
804
+
805
+ ## Hook
806
+
807
+ RestMan.add_before_execution_proc add a Proc to be called before each execution.
808
+ It's handy if you need direct access to the HTTP request.
809
+
810
+ Example:
811
+
812
+ ```ruby
813
+ # Add oauth support using the oauth gem
814
+ require 'oauth'
815
+ access_token = ...
816
+
817
+ RestMan.add_before_execution_proc do |req, params|
818
+ access_token.sign! req
819
+ end
820
+
821
+ RestMan.get 'http://example.com'
822
+ ```
823
+
824
+ ## Credits
825
+ | | |
826
+ |-------------------------|---------------------------------------------------------|
827
+ | **REST Client Team** | Andy Brody |
828
+ | **Creator** | Adam Wiggins |
829
+ | **Maintainers Emeriti** | Lawrence Leonard Gilbert, Matthew Manning, Julien Kirch |
830
+ | **Major contributions** | Blake Mizerany, Julien Kirch |
831
+
832
+ A great many generous folks have contributed features and patches.
833
+ See AUTHORS for the full list.
834
+
835
+ ## Legal
836
+
837
+ Released under the MIT License: https://opensource.org/licenses/MIT
838
+
839
+ Photo of the International Space Station was produced by NASA and is in the
840
+ public domain.
841
+
842
+ Code for reading Windows root certificate store derived from work by Puppet;
843
+ used under terms of the Apache License, Version 2.0.