rest-client 1.1.0 → 1.6.3

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.

Potentially problematic release.


This version of rest-client might be problematic. Click here for more details.

@@ -0,0 +1,27 @@
1
+ require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
+
3
+ require 'webmock/rspec'
4
+ include WebMock
5
+
6
+ describe RestClient::Request do
7
+
8
+ it "manage params for get requests" do
9
+ stub_request(:get, 'http://some/resource?a=b&c=d').with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Foo'=>'bar'}).to_return(:body => 'foo', :status => 200)
10
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => {:a => :b, 'c' => 'd'}}).body.should == 'foo'
11
+
12
+ stub_request(:get, 'http://some/resource').with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Foo'=>'bar', 'params' => 'a'}).to_return(:body => 'foo', :status => 200)
13
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => :a}).body.should == 'foo'
14
+ end
15
+
16
+ it "can use a block to process response" do
17
+ response_value = nil
18
+ block = Proc.new do |http_response|
19
+ response_value = http_response.body
20
+ end
21
+ stub_request(:get, 'http://some/resource?a=b&c=d').with(:headers => {'Accept'=>'*/*; q=0.5, application/xml', 'Accept-Encoding'=>'gzip, deflate', 'Foo'=>'bar'}).to_return(:body => 'foo', :status => 200)
22
+ RestClient::Request.execute(:url => 'http://some/resource', :method => :get, :headers => {:foo => :bar, :params => {:a => :b, 'c' => 'd'}}, :block_response => block)
23
+ response_value.should == "foo"
24
+ end
25
+
26
+ end
27
+
@@ -1,497 +1,529 @@
1
- require File.dirname(__FILE__) + '/base'
1
+ require File.join( File.dirname(File.expand_path(__FILE__)), 'base')
2
+
3
+ require 'webmock/rspec'
4
+ include WebMock
2
5
 
3
6
  describe RestClient::Request do
4
- before do
5
- @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
6
-
7
- @uri = mock("uri")
8
- @uri.stub!(:request_uri).and_return('/resource')
9
- @uri.stub!(:host).and_return('some')
10
- @uri.stub!(:port).and_return(80)
11
-
12
- @net = mock("net::http base")
13
- @http = mock("net::http connection")
14
- Net::HTTP.stub!(:new).and_return(@net)
15
- @net.stub!(:start).and_yield(@http)
16
- @net.stub!(:use_ssl=)
17
- @net.stub!(:verify_mode=)
18
- end
19
-
20
- it "accept */* mimetype, preferring xml" do
21
- @request.default_headers[:accept].should == '*/*; q=0.5, application/xml'
22
- end
23
-
24
- it "decodes an uncompressed result body by passing it straight through" do
25
- RestClient::Request.decode(nil, 'xyz').should == 'xyz'
26
- end
27
-
28
- it "decodes a gzip body" do
29
- RestClient::Request.decode('gzip', "\037\213\b\b\006'\252H\000\003t\000\313T\317UH\257\312,HM\341\002\000G\242(\r\v\000\000\000").should == "i'm gziped\n"
30
- end
31
-
32
- it "ingores gzip for empty bodies" do
33
- RestClient::Request.decode('gzip', '').should be_empty
34
- end
35
-
36
- it "decodes a deflated body" do
37
- RestClient::Request.decode('deflate', "x\234+\316\317MUHIM\313I,IMQ(I\255(\001\000A\223\006\363").should == "some deflated text"
38
- end
39
-
40
- it "processes a successful result" do
41
- res = mock("result")
42
- res.stub!(:code).and_return("200")
43
- res.stub!(:body).and_return('body')
44
- res.stub!(:[]).with('content-encoding').and_return(nil)
45
- @request.process_result(res).should == 'body'
46
- end
47
-
48
- it "doesn't classify successful requests as failed" do
49
- 203.upto(206) do |code|
50
- res = mock("result")
51
- res.stub!(:code).and_return(code.to_s)
52
- res.stub!(:body).and_return("")
53
- res.stub!(:[]).with('content-encoding').and_return(nil)
54
- @request.process_result(res).should be_empty
55
- end
56
- end
57
-
58
- it "parses a url into a URI object" do
59
- URI.should_receive(:parse).with('http://example.com/resource')
60
- @request.parse_url('http://example.com/resource')
61
- end
62
-
63
- it "adds http:// to the front of resources specified in the syntax example.com/resource" do
64
- URI.should_receive(:parse).with('http://example.com/resource')
65
- @request.parse_url('example.com/resource')
66
- end
67
-
68
- it "extracts the username and password when parsing http://user:password@example.com/" do
69
- URI.stub!(:parse).and_return(mock('uri', :user => 'joe', :password => 'pass1'))
70
- @request.parse_url_with_auth('http://joe:pass1@example.com/resource')
71
- @request.user.should == 'joe'
72
- @request.password.should == 'pass1'
73
- end
74
-
75
- it "doesn't overwrite user and password (which may have already been set by the Resource constructor) if there is no user/password in the url" do
76
- URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
77
- @request = RestClient::Request.new(:method => 'get', :url => 'example.com', :user => 'beth', :password => 'pass2')
78
- @request.parse_url_with_auth('http://example.com/resource')
79
- @request.user.should == 'beth'
80
- @request.password.should == 'pass2'
81
- end
82
-
83
- it "correctly formats cookies provided to the constructor" do
84
- URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
85
- @request = RestClient::Request.new(:method => 'get', :url => 'example.com', :cookies => {:session_id => '1' })
86
- @request.should_receive(:default_headers).and_return({'foo' => 'bar'})
87
- headers = @request.make_headers({}).should == { 'Foo' => 'bar', 'Cookie' => 'session_id=1'}
88
- end
89
-
90
- it "determines the Net::HTTP class to instantiate by the method name" do
91
- @request.net_http_request_class(:put).should == Net::HTTP::Put
92
- end
93
-
94
- it "merges user headers with the default headers" do
95
- @request.should_receive(:default_headers).and_return({ '1' => '2' })
96
- headers = @request.make_headers('3' => '4')
97
- headers.should have_key('1')
98
- headers['1'].should == '2'
99
- headers.should have_key('3')
100
- headers['3'].should == '4'
101
- end
102
-
103
- it "prefers the user header when the same header exists in the defaults" do
104
- @request.should_receive(:default_headers).and_return({ '1' => '2' })
105
- headers = @request.make_headers('1' => '3')
106
- headers.should have_key('1')
107
- headers['1'].should == '3'
108
- end
109
-
110
- it "converts header symbols from :content_type to 'Content-type'" do
111
- @request.should_receive(:default_headers).and_return({})
112
- headers = @request.make_headers(:content_type => 'abc')
113
- headers.should have_key('Content-type')
114
- headers['Content-type'].should == 'abc'
115
- end
116
-
117
- it "converts header values to strings" do
118
- @request.make_headers('A' => 1)['A'].should == '1'
119
- end
120
-
121
- it "executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
122
- @request.should_receive(:parse_url_with_auth).with('http://some/resource').and_return(@uri)
123
- klass = mock("net:http class")
124
- @request.should_receive(:net_http_request_class).with(:put).and_return(klass)
125
- klass.should_receive(:new).and_return('result')
126
- @request.should_receive(:transmit).with(@uri, 'result', kind_of(RestClient::Payload::Base))
127
- @request.execute_inner
128
- end
129
-
130
- it "transmits the request with Net::HTTP" do
131
- @http.should_receive(:request).with('req', 'payload')
132
- @request.should_receive(:process_result)
133
- @request.should_receive(:response_log)
134
- @request.transmit(@uri, 'req', 'payload')
135
- end
136
-
137
- it "uses SSL when the URI refers to a https address" do
138
- @uri.stub!(:is_a?).with(URI::HTTPS).and_return(true)
139
- @net.should_receive(:use_ssl=).with(true)
140
- @http.stub!(:request)
141
- @request.stub!(:process_result)
142
- @request.stub!(:response_log)
143
- @request.transmit(@uri, 'req', 'payload')
144
- end
145
-
146
- it "sends nil payloads" do
147
- @http.should_receive(:request).with('req', nil)
148
- @request.should_receive(:process_result)
149
- @request.stub!(:response_log)
150
- @request.transmit(@uri, 'req', nil)
151
- end
152
-
153
- it "passes non-hash payloads straight through" do
154
- @request.process_payload("x").should == "x"
155
- end
156
-
157
- it "converts a hash payload to urlencoded data" do
158
- @request.process_payload(:a => 'b c+d').should == "a=b%20c%2Bd"
159
- end
160
-
161
- it "accepts nested hashes in payload" do
162
- payload = @request.process_payload(:user => { :name => 'joe', :location => { :country => 'USA', :state => 'CA' }})
163
- payload.should include('user[name]=joe')
164
- payload.should include('user[location][country]=USA')
165
- payload.should include('user[location][state]=CA')
166
- end
167
-
168
- it "set urlencoded content_type header on hash payloads" do
169
- @request.process_payload(:a => 1)
170
- @request.headers[:content_type].should == 'application/x-www-form-urlencoded'
171
- end
172
-
173
- it "sets up the credentials prior to the request" do
174
- @http.stub!(:request)
175
- @request.stub!(:process_result)
176
- @request.stub!(:response_log)
177
-
178
- @request.stub!(:user).and_return('joe')
179
- @request.stub!(:password).and_return('mypass')
180
- @request.should_receive(:setup_credentials).with('req')
181
-
182
- @request.transmit(@uri, 'req', nil)
183
- end
184
-
185
- it "does not attempt to send any credentials if user is nil" do
186
- @request.stub!(:user).and_return(nil)
187
- req = mock("request")
188
- req.should_not_receive(:basic_auth)
189
- @request.setup_credentials(req)
190
- end
191
-
192
- it "setup credentials when there's a user" do
193
- @request.stub!(:user).and_return('joe')
194
- @request.stub!(:password).and_return('mypass')
195
- req = mock("request")
196
- req.should_receive(:basic_auth).with('joe', 'mypass')
197
- @request.setup_credentials(req)
198
- end
199
-
200
- it "catches EOFError and shows the more informative ServerBrokeConnection" do
201
- @http.stub!(:request).and_raise(EOFError)
202
- lambda { @request.transmit(@uri, 'req', nil) }.should raise_error(RestClient::ServerBrokeConnection)
203
- end
204
-
205
- it "execute calls execute_inner" do
206
- @request.should_receive(:execute_inner)
207
- @request.execute
208
- end
209
-
210
- it "class method execute wraps constructor" do
211
- req = mock("rest request")
212
- RestClient::Request.should_receive(:new).with(1 => 2).and_return(req)
213
- req.should_receive(:execute)
214
- RestClient::Request.execute(1 => 2)
215
- end
216
-
217
- it "raises a Redirect with the new location when the response is in the 30x range" do
218
- res = mock('response', :code => '301', :header => { 'Location' => 'http://new/resource' })
219
- lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://new/resource'}
220
- end
221
-
222
- it "handles redirects with relative paths" do
223
- res = mock('response', :code => '301', :header => { 'Location' => 'index' })
224
- lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://some/index' }
225
- end
226
-
227
- it "handles redirects with absolute paths" do
228
- @request.instance_variable_set('@url', 'http://some/place/else')
229
- res = mock('response', :code => '301', :header => { 'Location' => '/index' })
230
- lambda { @request.process_result(res) }.should raise_error(RestClient::Redirect) { |e| e.url.should == 'http://some/index' }
231
- end
232
-
233
- it "uses GET and clears payload when following 30x redirects" do
234
- url = "http://example.com/redirected"
235
-
236
- @request.should_receive(:execute_inner).once.ordered.and_raise(RestClient::Redirect.new(url))
237
-
238
- @request.should_receive(:execute_inner).once.ordered do
239
- @request.url.should == url
240
- @request.method.should == :get
241
- @request.payload.should be_nil
242
- end
243
-
244
- @request.execute
245
- end
246
-
247
- it "raises Unauthorized when the response is 401" do
248
- res = mock('response', :code => '401')
249
- lambda { @request.process_result(res) }.should raise_error(RestClient::Unauthorized)
250
- end
251
-
252
- it "raises ResourceNotFound when the response is 404" do
253
- res = mock('response', :code => '404')
254
- lambda { @request.process_result(res) }.should raise_error(RestClient::ResourceNotFound)
255
- end
256
-
257
- it "raises RequestFailed otherwise" do
258
- res = mock('response', :code => '500')
259
- lambda { @request.process_result(res) }.should raise_error(RestClient::RequestFailed)
260
- end
261
-
262
- it "creates a proxy class if a proxy url is given" do
263
- RestClient.stub!(:proxy).and_return("http://example.com/")
264
- @request.net_http_class.should include(Net::HTTP::ProxyDelta)
265
- end
266
-
267
- it "creates a non-proxy class if a proxy url is not given" do
268
- @request.net_http_class.should_not include(Net::HTTP::ProxyDelta)
269
- end
270
-
271
- it "logs a get request" do
272
- RestClient::Request.new(:method => :get, :url => 'http://url').request_log.should ==
273
- 'RestClient.get "http://url"'
274
- end
275
-
276
- it "logs a post request with a small payload" do
277
- RestClient::Request.new(:method => :post, :url => 'http://url', :payload => 'foo').request_log.should ==
278
- 'RestClient.post "http://url", "foo"'
279
- end
280
-
281
- it "logs a post request with a large payload" do
282
- RestClient::Request.new(:method => :post, :url => 'http://url', :payload => ('x' * 1000)).request_log.should ==
283
- 'RestClient.post "http://url", "(1000 byte payload)"'
284
- end
285
-
286
- it "logs input headers as a hash" do
287
- RestClient::Request.new(:method => :get, :url => 'http://url', :headers => { :accept => 'text/plain' }).request_log.should ==
288
- 'RestClient.get "http://url", :accept=>"text/plain"'
289
- end
290
-
291
- it "logs a response including the status code, content type, and result body size in bytes" do
292
- res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
293
- res.stub!(:[]).with('Content-type').and_return('text/html')
294
- @request.response_log(res).should == "# => 200 OK | text/html 4 bytes"
295
- end
296
-
297
- it "logs a response with a nil Content-type" do
298
- res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
299
- res.stub!(:[]).with('Content-type').and_return(nil)
300
- @request.response_log(res).should == "# => 200 OK | 4 bytes"
301
- end
302
-
303
- it "logs a response with a nil body" do
304
- res = mock('result', :code => '200', :class => Net::HTTPOK, :body => nil)
305
- res.stub!(:[]).with('Content-type').and_return('text/html; charset=utf-8')
306
- @request.response_log(res).should == "# => 200 OK | text/html 0 bytes"
307
- end
308
-
309
- it "strips the charset from the response content type" do
310
- res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
311
- res.stub!(:[]).with('Content-type').and_return('text/html; charset=utf-8')
312
- @request.response_log(res).should == "# => 200 OK | text/html 4 bytes"
313
- end
314
-
315
- it "displays the log to stdout" do
316
- RestClient.stub!(:log).and_return('stdout')
317
- STDOUT.should_receive(:puts).with('xyz')
318
- @request.display_log('xyz')
319
- end
320
-
321
- it "displays the log to stderr" do
322
- RestClient.stub!(:log).and_return('stderr')
323
- STDERR.should_receive(:puts).with('xyz')
324
- @request.display_log('xyz')
325
- end
326
-
327
- it "append the log to the requested filename" do
328
- RestClient.stub!(:log).and_return('/tmp/restclient.log')
329
- f = mock('file handle')
330
- File.should_receive(:open).with('/tmp/restclient.log', 'a').and_yield(f)
331
- f.should_receive(:puts).with('xyz')
332
- @request.display_log('xyz')
333
- end
334
-
335
- it "set read_timeout" do
336
- @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => 123)
337
- @http.stub!(:request)
338
- @request.stub!(:process_result)
339
- @request.stub!(:response_log)
340
-
341
- @net.should_receive(:read_timeout=).with(123)
342
-
343
- @request.transmit(@uri, 'req', nil)
344
- end
345
-
346
- it "set open_timeout" do
347
- @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :open_timeout => 123)
348
- @http.stub!(:request)
349
- @request.stub!(:process_result)
350
- @request.stub!(:response_log)
351
-
352
- @net.should_receive(:open_timeout=).with(123)
353
-
354
- @request.transmit(@uri, 'req', nil)
355
- end
356
-
357
- it "should default to not verifying ssl certificates" do
358
- @request.verify_ssl.should == false
359
- end
360
-
361
- it "should set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is false" do
362
- @net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
363
- @http.stub!(:request)
364
- @request.stub!(:process_result)
365
- @request.stub!(:response_log)
366
- @request.transmit(@uri, 'req', 'payload')
367
- end
368
-
369
- it "should not set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is true" do
370
- @request = RestClient::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true)
371
- @net.should_not_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
372
- @http.stub!(:request)
373
- @request.stub!(:process_result)
374
- @request.stub!(:response_log)
375
- @request.transmit(@uri, 'req', 'payload')
376
- end
377
-
378
- it "should set net.verify_mode to the passed value if verify_ssl is an OpenSSL constant" do
379
- mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
380
- @request = RestClient::Request.new( :method => :put,
381
- :url => 'https://some/resource',
382
- :payload => 'payload',
383
- :verify_ssl => mode )
384
- @net.should_receive(:verify_mode=).with(mode)
385
- @http.stub!(:request)
386
- @request.stub!(:process_result)
387
- @request.stub!(:response_log)
388
- @request.transmit(@uri, 'req', 'payload')
389
- end
390
-
391
- it "should default to not having an ssl_client_cert" do
392
- @request.ssl_client_cert.should be(nil)
393
- end
394
-
395
- it "should set the ssl_client_cert if provided" do
396
- @request = RestClient::Request.new(
397
- :method => :put,
398
- :url => 'https://some/resource',
399
- :payload => 'payload',
400
- :ssl_client_cert => "whatsupdoc!"
401
- )
402
- @net.should_receive(:cert=).with("whatsupdoc!")
403
- @http.stub!(:request)
404
- @request.stub!(:process_result)
405
- @request.stub!(:response_log)
406
- @request.transmit(@uri, 'req', 'payload')
407
- end
408
-
409
- it "should not set the ssl_client_cert if it is not provided" do
410
- @request = RestClient::Request.new(
411
- :method => :put,
412
- :url => 'https://some/resource',
413
- :payload => 'payload'
414
- )
415
- @net.should_not_receive(:cert=).with("whatsupdoc!")
416
- @http.stub!(:request)
417
- @request.stub!(:process_result)
418
- @request.stub!(:response_log)
419
- @request.transmit(@uri, 'req', 'payload')
420
- end
421
-
422
- it "should default to not having an ssl_client_key" do
423
- @request.ssl_client_key.should be(nil)
424
- end
425
-
426
- it "should set the ssl_client_key if provided" do
427
- @request = RestClient::Request.new(
428
- :method => :put,
429
- :url => 'https://some/resource',
430
- :payload => 'payload',
431
- :ssl_client_key => "whatsupdoc!"
432
- )
433
- @net.should_receive(:key=).with("whatsupdoc!")
434
- @http.stub!(:request)
435
- @request.stub!(:process_result)
436
- @request.stub!(:response_log)
437
- @request.transmit(@uri, 'req', 'payload')
438
- end
439
-
440
- it "should not set the ssl_client_key if it is not provided" do
441
- @request = RestClient::Request.new(
442
- :method => :put,
443
- :url => 'https://some/resource',
444
- :payload => 'payload'
445
- )
446
- @net.should_not_receive(:key=).with("whatsupdoc!")
447
- @http.stub!(:request)
448
- @request.stub!(:process_result)
449
- @request.stub!(:response_log)
450
- @request.transmit(@uri, 'req', 'payload')
451
- end
452
-
453
- it "should default to not having an ssl_ca_file" do
454
- @request.ssl_ca_file.should be(nil)
455
- end
456
-
457
- it "should set the ssl_ca_file if provided" do
458
- @request = RestClient::Request.new(
459
- :method => :put,
460
- :url => 'https://some/resource',
461
- :payload => 'payload',
462
- :ssl_ca_file => "Certificate Authority File"
463
- )
464
- @net.should_receive(:ca_file=).with("Certificate Authority File")
465
- @http.stub!(:request)
466
- @request.stub!(:process_result)
467
- @request.stub!(:response_log)
468
- @request.transmit(@uri, 'req', 'payload')
469
- end
470
-
471
- it "should not set the ssl_ca_file if it is not provided" do
472
- @request = RestClient::Request.new(
473
- :method => :put,
474
- :url => 'https://some/resource',
475
- :payload => 'payload'
476
- )
477
- @net.should_not_receive(:ca_file=).with("Certificate Authority File")
478
- @http.stub!(:request)
479
- @request.stub!(:process_result)
480
- @request.stub!(:response_log)
481
- @request.transmit(@uri, 'req', 'payload')
482
- end
483
-
484
- it "should still return a response object for 204 No Content responses" do
485
- @request = RestClient::Request.new(
486
- :method => :put,
487
- :url => 'https://some/resource',
488
- :payload => 'payload'
489
- )
490
- net_http_res = Net::HTTPNoContent.new("", "204", "No Content")
491
- net_http_res.stub(:read_body).and_return(nil)
492
- @http.should_receive(:request).and_return(@request.fetch_body(net_http_res))
493
- response = @request.transmit(@uri, 'req', 'payload')
494
- response.should_not be_nil
495
- response.code.should equal(204)
496
- end
7
+ before do
8
+ @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload')
9
+
10
+ @uri = mock("uri")
11
+ @uri.stub!(:request_uri).and_return('/resource')
12
+ @uri.stub!(:host).and_return('some')
13
+ @uri.stub!(:port).and_return(80)
14
+
15
+ @net = mock("net::http base")
16
+ @http = mock("net::http connection")
17
+ Net::HTTP.stub!(:new).and_return(@net)
18
+ @net.stub!(:start).and_yield(@http)
19
+ @net.stub!(:use_ssl=)
20
+ @net.stub!(:verify_mode=)
21
+ RestClient.log = nil
22
+ end
23
+
24
+ it "accept */* mimetype, preferring xml" do
25
+ @request.default_headers[:accept].should == '*/*; q=0.5, application/xml'
26
+ end
27
+
28
+ describe "compression" do
29
+
30
+ it "decodes an uncompressed result body by passing it straight through" do
31
+ RestClient::Request.decode(nil, 'xyz').should == 'xyz'
32
+ end
33
+
34
+ it "doesn't fail for nil bodies" do
35
+ RestClient::Request.decode('gzip', nil).should be_nil
36
+ end
37
+
38
+
39
+ it "decodes a gzip body" do
40
+ RestClient::Request.decode('gzip', "\037\213\b\b\006'\252H\000\003t\000\313T\317UH\257\312,HM\341\002\000G\242(\r\v\000\000\000").should == "i'm gziped\n"
41
+ end
42
+
43
+ it "ingores gzip for empty bodies" do
44
+ RestClient::Request.decode('gzip', '').should be_empty
45
+ end
46
+
47
+ it "decodes a deflated body" do
48
+ RestClient::Request.decode('deflate', "x\234+\316\317MUHIM\313I,IMQ(I\255(\001\000A\223\006\363").should == "some deflated text"
49
+ end
50
+ end
51
+
52
+ it "processes a successful result" do
53
+ res = mock("result")
54
+ res.stub!(:code).and_return("200")
55
+ res.stub!(:body).and_return('body')
56
+ res.stub!(:[]).with('content-encoding').and_return(nil)
57
+ @request.process_result(res).body.should == 'body'
58
+ @request.process_result(res).to_s.should == 'body'
59
+ end
60
+
61
+ it "doesn't classify successful requests as failed" do
62
+ 203.upto(207) do |code|
63
+ res = mock("result")
64
+ res.stub!(:code).and_return(code.to_s)
65
+ res.stub!(:body).and_return("")
66
+ res.stub!(:[]).with('content-encoding').and_return(nil)
67
+ @request.process_result(res).should be_empty
68
+ end
69
+ end
70
+
71
+ it "parses a url into a URI object" do
72
+ URI.should_receive(:parse).with('http://example.com/resource')
73
+ @request.parse_url('http://example.com/resource')
74
+ end
75
+
76
+ it "adds http:// to the front of resources specified in the syntax example.com/resource" do
77
+ URI.should_receive(:parse).with('http://example.com/resource')
78
+ @request.parse_url('example.com/resource')
79
+ end
80
+
81
+ describe "user - password" do
82
+ it "extracts the username and password when parsing http://user:password@example.com/" do
83
+ URI.stub!(:parse).and_return(mock('uri', :user => 'joe', :password => 'pass1'))
84
+ @request.parse_url_with_auth('http://joe:pass1@example.com/resource')
85
+ @request.user.should == 'joe'
86
+ @request.password.should == 'pass1'
87
+ end
88
+
89
+ it "extracts with escaping the username and password when parsing http://user:password@example.com/" do
90
+ URI.stub!(:parse).and_return(mock('uri', :user => 'joe%20', :password => 'pass1'))
91
+ @request.parse_url_with_auth('http://joe%20:pass1@example.com/resource')
92
+ @request.user.should == 'joe '
93
+ @request.password.should == 'pass1'
94
+ end
95
+
96
+ it "doesn't overwrite user and password (which may have already been set by the Resource constructor) if there is no user/password in the url" do
97
+ URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
98
+ @request = RestClient::Request.new(:method => 'get', :url => 'example.com', :user => 'beth', :password => 'pass2')
99
+ @request.parse_url_with_auth('http://example.com/resource')
100
+ @request.user.should == 'beth'
101
+ @request.password.should == 'pass2'
102
+ end
103
+ end
104
+
105
+ it "correctly formats cookies provided to the constructor" do
106
+ URI.stub!(:parse).and_return(mock('uri', :user => nil, :password => nil))
107
+ @request = RestClient::Request.new(:method => 'get', :url => 'example.com', :cookies => {:session_id => '1', :user_id => "someone" })
108
+ @request.should_receive(:default_headers).and_return({'Foo' => 'bar'})
109
+ @request.make_headers({}).should == { 'Foo' => 'bar', 'Cookie' => 'session_id=1;user_id=someone'}
110
+ end
111
+
112
+ it "determines the Net::HTTP class to instantiate by the method name" do
113
+ @request.net_http_request_class(:put).should == Net::HTTP::Put
114
+ end
115
+
116
+ describe "user headers" do
117
+ it "merges user headers with the default headers" do
118
+ @request.should_receive(:default_headers).and_return({ :accept => '*/*; q=0.5, application/xml', :accept_encoding => 'gzip, deflate' })
119
+ headers = @request.make_headers("Accept" => "application/json", :accept_encoding => 'gzip')
120
+ headers.should have_key "Accept-Encoding"
121
+ headers["Accept-Encoding"].should == "gzip"
122
+ headers.should have_key "Accept"
123
+ headers["Accept"].should == "application/json"
124
+ end
125
+
126
+ it "prefers the user header when the same header exists in the defaults" do
127
+ @request.should_receive(:default_headers).and_return({ '1' => '2' })
128
+ headers = @request.make_headers('1' => '3')
129
+ headers.should have_key('1')
130
+ headers['1'].should == '3'
131
+ end
132
+ end
133
+
134
+ describe "header symbols" do
135
+
136
+ it "converts header symbols from :content_type to 'Content-Type'" do
137
+ @request.should_receive(:default_headers).and_return({})
138
+ headers = @request.make_headers(:content_type => 'abc')
139
+ headers.should have_key('Content-Type')
140
+ headers['Content-Type'].should == 'abc'
141
+ end
142
+
143
+ it "converts content-type from extension to real content-type" do
144
+ @request.should_receive(:default_headers).and_return({})
145
+ headers = @request.make_headers(:content_type => 'json')
146
+ headers.should have_key('Content-Type')
147
+ headers['Content-Type'].should == 'application/json'
148
+ end
149
+
150
+ it "converts accept from extension(s) to real content-type(s)" do
151
+ @request.should_receive(:default_headers).and_return({})
152
+ headers = @request.make_headers(:accept => 'json, mp3')
153
+ headers.should have_key('Accept')
154
+ headers['Accept'].should == 'application/json, audio/mpeg'
155
+
156
+ @request.should_receive(:default_headers).and_return({})
157
+ headers = @request.make_headers(:accept => :json)
158
+ headers.should have_key('Accept')
159
+ headers['Accept'].should == 'application/json'
160
+ end
161
+
162
+ it "only convert symbols in header" do
163
+ @request.should_receive(:default_headers).and_return({})
164
+ headers = @request.make_headers({:foo_bar => 'value', "bar_bar" => 'value'})
165
+ headers['Foo-Bar'].should == 'value'
166
+ headers['bar_bar'].should == 'value'
167
+ end
168
+
169
+ it "converts header values to strings" do
170
+ @request.make_headers('A' => 1)['A'].should == '1'
171
+ end
172
+ end
173
+
174
+ it "executes by constructing the Net::HTTP object, headers, and payload and calling transmit" do
175
+ @request.should_receive(:parse_url_with_auth).with('http://some/resource').and_return(@uri)
176
+ klass = mock("net:http class")
177
+ @request.should_receive(:net_http_request_class).with(:put).and_return(klass)
178
+ klass.should_receive(:new).and_return('result')
179
+ @request.should_receive(:transmit).with(@uri, 'result', kind_of(RestClient::Payload::Base))
180
+ @request.execute
181
+ end
182
+
183
+ it "transmits the request with Net::HTTP" do
184
+ @http.should_receive(:request).with('req', 'payload')
185
+ @request.should_receive(:process_result)
186
+ @request.transmit(@uri, 'req', 'payload')
187
+ end
188
+
189
+ describe "payload" do
190
+ it "sends nil payloads" do
191
+ @http.should_receive(:request).with('req', nil)
192
+ @request.should_receive(:process_result)
193
+ @request.stub!(:response_log)
194
+ @request.transmit(@uri, 'req', nil)
195
+ end
196
+
197
+ it "passes non-hash payloads straight through" do
198
+ @request.process_payload("x").should == "x"
199
+ end
200
+
201
+ it "converts a hash payload to urlencoded data" do
202
+ @request.process_payload(:a => 'b c+d').should == "a=b%20c%2Bd"
203
+ end
204
+
205
+ it "accepts nested hashes in payload" do
206
+ payload = @request.process_payload(:user => { :name => 'joe', :location => { :country => 'USA', :state => 'CA' }})
207
+ payload.should include('user[name]=joe')
208
+ payload.should include('user[location][country]=USA')
209
+ payload.should include('user[location][state]=CA')
210
+ end
211
+ end
212
+
213
+ it "set urlencoded content_type header on hash payloads" do
214
+ @request.process_payload(:a => 1)
215
+ @request.headers[:content_type].should == 'application/x-www-form-urlencoded'
216
+ end
217
+
218
+ describe "credentials" do
219
+ it "sets up the credentials prior to the request" do
220
+ @http.stub!(:request)
221
+ @request.stub!(:process_result)
222
+ @request.stub!(:response_log)
223
+
224
+ @request.stub!(:user).and_return('joe')
225
+ @request.stub!(:password).and_return('mypass')
226
+ @request.should_receive(:setup_credentials).with('req')
227
+
228
+ @request.transmit(@uri, 'req', nil)
229
+ end
230
+
231
+ it "does not attempt to send any credentials if user is nil" do
232
+ @request.stub!(:user).and_return(nil)
233
+ req = mock("request")
234
+ req.should_not_receive(:basic_auth)
235
+ @request.setup_credentials(req)
236
+ end
237
+
238
+ it "setup credentials when there's a user" do
239
+ @request.stub!(:user).and_return('joe')
240
+ @request.stub!(:password).and_return('mypass')
241
+ req = mock("request")
242
+ req.should_receive(:basic_auth).with('joe', 'mypass')
243
+ @request.setup_credentials(req)
244
+ end
245
+ end
246
+
247
+ it "catches EOFError and shows the more informative ServerBrokeConnection" do
248
+ @http.stub!(:request).and_raise(EOFError)
249
+ lambda { @request.transmit(@uri, 'req', nil) }.should raise_error(RestClient::ServerBrokeConnection)
250
+ end
251
+
252
+ it "class method execute wraps constructor" do
253
+ req = mock("rest request")
254
+ RestClient::Request.should_receive(:new).with(1 => 2).and_return(req)
255
+ req.should_receive(:execute)
256
+ RestClient::Request.execute(1 => 2)
257
+ end
258
+
259
+ describe "exception" do
260
+ it "raises Unauthorized when the response is 401" do
261
+ res = mock('response', :code => '401', :[] => ['content-encoding' => ''], :body => '' )
262
+ lambda { @request.process_result(res) }.should raise_error(RestClient::Unauthorized)
263
+ end
264
+
265
+ it "raises ResourceNotFound when the response is 404" do
266
+ res = mock('response', :code => '404', :[] => ['content-encoding' => ''], :body => '' )
267
+ lambda { @request.process_result(res) }.should raise_error(RestClient::ResourceNotFound)
268
+ end
269
+
270
+ it "raises RequestFailed otherwise" do
271
+ res = mock('response', :code => '500', :[] => ['content-encoding' => ''], :body => '' )
272
+ lambda { @request.process_result(res) }.should raise_error(RestClient::InternalServerError)
273
+ end
274
+ end
275
+
276
+ describe "block usage" do
277
+ it "returns what asked to" do
278
+ res = mock('response', :code => '401', :[] => ['content-encoding' => ''], :body => '' )
279
+ @request.process_result(res){|response, request| "foo"}.should == "foo"
280
+ end
281
+ end
282
+
283
+ describe "proxy" do
284
+ it "creates a proxy class if a proxy url is given" do
285
+ RestClient.stub!(:proxy).and_return("http://example.com/")
286
+ @request.net_http_class.should include(Net::HTTP::ProxyDelta)
287
+ end
288
+
289
+ it "creates a non-proxy class if a proxy url is not given" do
290
+ @request.net_http_class.should_not include(Net::HTTP::ProxyDelta)
291
+ end
292
+ end
293
+
294
+
295
+ describe "logging" do
296
+ it "logs a get request" do
297
+ log = RestClient.log = []
298
+ RestClient::Request.new(:method => :get, :url => 'http://url').log_request
299
+ log[0].should == %Q{RestClient.get "http://url", "Accept"=>"*/*; q=0.5, application/xml", "Accept-Encoding"=>"gzip, deflate"\n}
300
+ end
301
+
302
+ it "logs a post request with a small payload" do
303
+ log = RestClient.log = []
304
+ RestClient::Request.new(:method => :post, :url => 'http://url', :payload => 'foo').log_request
305
+ log[0].should == %Q{RestClient.post "http://url", "foo", "Accept"=>"*/*; q=0.5, application/xml", "Accept-Encoding"=>"gzip, deflate", "Content-Length"=>"3"\n}
306
+ end
307
+
308
+ it "logs a post request with a large payload" do
309
+ log = RestClient.log = []
310
+ RestClient::Request.new(:method => :post, :url => 'http://url', :payload => ('x' * 1000)).log_request
311
+ log[0].should == %Q{RestClient.post "http://url", 1000 byte(s) length, "Accept"=>"*/*; q=0.5, application/xml", "Accept-Encoding"=>"gzip, deflate", "Content-Length"=>"1000"\n}
312
+ end
313
+
314
+ it "logs input headers as a hash" do
315
+ log = RestClient.log = []
316
+ RestClient::Request.new(:method => :get, :url => 'http://url', :headers => { :accept => 'text/plain' }).log_request
317
+ log[0].should == %Q{RestClient.get "http://url", "Accept"=>"text/plain", "Accept-Encoding"=>"gzip, deflate"\n}
318
+ end
319
+
320
+ it "logs a response including the status code, content type, and result body size in bytes" do
321
+ log = RestClient.log = []
322
+ res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
323
+ res.stub!(:[]).with('Content-type').and_return('text/html')
324
+ @request.log_response res
325
+ log[0].should == "# => 200 OK | text/html 4 bytes\n"
326
+ end
327
+
328
+ it "logs a response with a nil Content-type" do
329
+ log = RestClient.log = []
330
+ res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
331
+ res.stub!(:[]).with('Content-type').and_return(nil)
332
+ @request.log_response res
333
+ log[0].should == "# => 200 OK | 4 bytes\n"
334
+ end
335
+
336
+ it "logs a response with a nil body" do
337
+ log = RestClient.log = []
338
+ res = mock('result', :code => '200', :class => Net::HTTPOK, :body => nil)
339
+ res.stub!(:[]).with('Content-type').and_return('text/html; charset=utf-8')
340
+ @request.log_response res
341
+ log[0].should == "# => 200 OK | text/html 0 bytes\n"
342
+ end
343
+ end
344
+
345
+ it "strips the charset from the response content type" do
346
+ log = RestClient.log = []
347
+ res = mock('result', :code => '200', :class => Net::HTTPOK, :body => 'abcd')
348
+ res.stub!(:[]).with('Content-type').and_return('text/html; charset=utf-8')
349
+ @request.log_response res
350
+ log[0].should == "# => 200 OK | text/html 4 bytes\n"
351
+ end
352
+
353
+ describe "timeout" do
354
+ it "set read_timeout" do
355
+ @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => 123)
356
+ @http.stub!(:request)
357
+ @request.stub!(:process_result)
358
+ @request.stub!(:response_log)
359
+
360
+ @net.should_receive(:read_timeout=).with(123)
361
+
362
+ @request.transmit(@uri, 'req', nil)
363
+ end
364
+
365
+ it "set open_timeout" do
366
+ @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :open_timeout => 123)
367
+ @http.stub!(:request)
368
+ @request.stub!(:process_result)
369
+ @request.stub!(:response_log)
370
+
371
+ @net.should_receive(:open_timeout=).with(123)
372
+
373
+ @request.transmit(@uri, 'req', nil)
374
+ end
375
+ end
376
+
377
+ describe "ssl" do
378
+ it "uses SSL when the URI refers to a https address" do
379
+ @uri.stub!(:is_a?).with(URI::HTTPS).and_return(true)
380
+ @net.should_receive(:use_ssl=).with(true)
381
+ @http.stub!(:request)
382
+ @request.stub!(:process_result)
383
+ @request.stub!(:response_log)
384
+ @request.transmit(@uri, 'req', 'payload')
385
+ end
386
+
387
+ it "should default to not verifying ssl certificates" do
388
+ @request.verify_ssl.should == false
389
+ end
390
+
391
+ it "should set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is false" do
392
+ @net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
393
+ @http.stub!(:request)
394
+ @request.stub!(:process_result)
395
+ @request.stub!(:response_log)
396
+ @request.transmit(@uri, 'req', 'payload')
397
+ end
398
+
399
+ it "should not set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is true" do
400
+ @request = RestClient::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true)
401
+ @net.should_not_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE)
402
+ @http.stub!(:request)
403
+ @request.stub!(:process_result)
404
+ @request.stub!(:response_log)
405
+ @request.transmit(@uri, 'req', 'payload')
406
+ end
407
+
408
+ it "should set net.verify_mode to the passed value if verify_ssl is an OpenSSL constant" do
409
+ mode = OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
410
+ @request = RestClient::Request.new( :method => :put,
411
+ :url => 'https://some/resource',
412
+ :payload => 'payload',
413
+ :verify_ssl => mode )
414
+ @net.should_receive(:verify_mode=).with(mode)
415
+ @net.should_receive(:verify_callback=)
416
+ @http.stub!(:request)
417
+ @request.stub!(:process_result)
418
+ @request.stub!(:response_log)
419
+ @request.transmit(@uri, 'req', 'payload')
420
+ end
421
+
422
+ it "should default to not having an ssl_client_cert" do
423
+ @request.ssl_client_cert.should be(nil)
424
+ end
425
+
426
+ it "should set the ssl_client_cert if provided" do
427
+ @request = RestClient::Request.new(
428
+ :method => :put,
429
+ :url => 'https://some/resource',
430
+ :payload => 'payload',
431
+ :ssl_client_cert => "whatsupdoc!"
432
+ )
433
+ @net.should_receive(:cert=).with("whatsupdoc!")
434
+ @http.stub!(:request)
435
+ @request.stub!(:process_result)
436
+ @request.stub!(:response_log)
437
+ @request.transmit(@uri, 'req', 'payload')
438
+ end
439
+
440
+ it "should not set the ssl_client_cert if it is not provided" do
441
+ @request = RestClient::Request.new(
442
+ :method => :put,
443
+ :url => 'https://some/resource',
444
+ :payload => 'payload'
445
+ )
446
+ @net.should_not_receive(:cert=).with("whatsupdoc!")
447
+ @http.stub!(:request)
448
+ @request.stub!(:process_result)
449
+ @request.stub!(:response_log)
450
+ @request.transmit(@uri, 'req', 'payload')
451
+ end
452
+
453
+ it "should default to not having an ssl_client_key" do
454
+ @request.ssl_client_key.should be(nil)
455
+ end
456
+
457
+ it "should set the ssl_client_key if provided" do
458
+ @request = RestClient::Request.new(
459
+ :method => :put,
460
+ :url => 'https://some/resource',
461
+ :payload => 'payload',
462
+ :ssl_client_key => "whatsupdoc!"
463
+ )
464
+ @net.should_receive(:key=).with("whatsupdoc!")
465
+ @http.stub!(:request)
466
+ @request.stub!(:process_result)
467
+ @request.stub!(:response_log)
468
+ @request.transmit(@uri, 'req', 'payload')
469
+ end
470
+
471
+ it "should not set the ssl_client_key if it is not provided" do
472
+ @request = RestClient::Request.new(
473
+ :method => :put,
474
+ :url => 'https://some/resource',
475
+ :payload => 'payload'
476
+ )
477
+ @net.should_not_receive(:key=).with("whatsupdoc!")
478
+ @http.stub!(:request)
479
+ @request.stub!(:process_result)
480
+ @request.stub!(:response_log)
481
+ @request.transmit(@uri, 'req', 'payload')
482
+ end
483
+
484
+ it "should default to not having an ssl_ca_file" do
485
+ @request.ssl_ca_file.should be(nil)
486
+ end
487
+
488
+ it "should set the ssl_ca_file if provided" do
489
+ @request = RestClient::Request.new(
490
+ :method => :put,
491
+ :url => 'https://some/resource',
492
+ :payload => 'payload',
493
+ :ssl_ca_file => "Certificate Authority File"
494
+ )
495
+ @net.should_receive(:ca_file=).with("Certificate Authority File")
496
+ @http.stub!(:request)
497
+ @request.stub!(:process_result)
498
+ @request.stub!(:response_log)
499
+ @request.transmit(@uri, 'req', 'payload')
500
+ end
501
+
502
+ it "should not set the ssl_ca_file if it is not provided" do
503
+ @request = RestClient::Request.new(
504
+ :method => :put,
505
+ :url => 'https://some/resource',
506
+ :payload => 'payload'
507
+ )
508
+ @net.should_not_receive(:ca_file=).with("Certificate Authority File")
509
+ @http.stub!(:request)
510
+ @request.stub!(:process_result)
511
+ @request.stub!(:response_log)
512
+ @request.transmit(@uri, 'req', 'payload')
513
+ end
514
+ end
515
+
516
+ it "should still return a response object for 204 No Content responses" do
517
+ @request = RestClient::Request.new(
518
+ :method => :put,
519
+ :url => 'https://some/resource',
520
+ :payload => 'payload'
521
+ )
522
+ net_http_res = Net::HTTPNoContent.new("", "204", "No Content")
523
+ net_http_res.stub!(:read_body).and_return(nil)
524
+ @http.should_receive(:request).and_return(@request.fetch_body(net_http_res))
525
+ response = @request.transmit(@uri, 'req', 'payload')
526
+ response.should_not be_nil
527
+ response.code.should == 204
528
+ end
497
529
  end