rest-man 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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.