patron 0.13.4 → 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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -0
  3. data/README.md +113 -50
  4. data/ext/patron/extconf.rb +55 -20
  5. data/ext/patron/session_ext.c +1092 -761
  6. data/lib/patron/compat.rb +57 -0
  7. data/lib/patron/error.rb +25 -13
  8. data/lib/patron/header_parser.rb +27 -25
  9. data/lib/patron/request.rb +98 -142
  10. data/lib/patron/response.rb +33 -24
  11. data/lib/patron/response_decoding.rb +60 -67
  12. data/lib/patron/session.rb +127 -104
  13. data/lib/patron/util.rb +14 -12
  14. data/lib/patron/version.rb +3 -1
  15. data/lib/patron.rb +9 -15
  16. metadata +12 -49
  17. data/.autotest +0 -15
  18. data/.github/workflows/ci.yml +0 -26
  19. data/.gitignore +0 -10
  20. data/.rspec +0 -0
  21. data/.travis.yml +0 -13
  22. data/.yardopts +0 -1
  23. data/Gemfile +0 -17
  24. data/Rakefile +0 -31
  25. data/ext/patron/.gitignore +0 -4
  26. data/ext/patron/membuffer.c +0 -85
  27. data/ext/patron/membuffer.h +0 -78
  28. data/ext/patron/sglib.h +0 -1952
  29. data/lib/patron/proxy_type.rb +0 -11
  30. data/patron.gemspec +0 -38
  31. data/pic.png +0 -0
  32. data/script/console +0 -11
  33. data/script/test_server +0 -7
  34. data/spec/header_parser_spec.rb +0 -101
  35. data/spec/patron_spec.rb +0 -28
  36. data/spec/request_spec.rb +0 -77
  37. data/spec/response_spec.rb +0 -145
  38. data/spec/sample_response_headers/headers_wetransfer.txt +0 -18
  39. data/spec/sample_response_headers/headers_wetransfer_with_proxy_status.txt +0 -20
  40. data/spec/sample_response_headers/headers_wetransfer_with_redirect.txt +0 -24
  41. data/spec/sample_response_headers/headers_with_set_cookie.txt +0 -9
  42. data/spec/sample_response_headers/sample_http2_header.txt +0 -16
  43. data/spec/sample_response_headers/webmock_headers_without_trailing_crlf.txt +0 -15
  44. data/spec/session_spec.rb +0 -698
  45. data/spec/session_ssl_spec.rb +0 -313
  46. data/spec/spec_helper.rb +0 -34
  47. data/spec/support/certs/cacert.pem +0 -36
  48. data/spec/support/certs/privkey.pem +0 -51
  49. data/spec/support/config.ru +0 -146
  50. data/spec/support/fork.rb +0 -70
  51. data/spec/support/test_server.rb +0 -26
  52. data/spec/util_spec.rb +0 -71
data/spec/session_spec.rb DELETED
@@ -1,698 +0,0 @@
1
- require File.expand_path("./spec") + '/spec_helper.rb'
2
- require 'webrick'
3
- require 'yaml'
4
- require 'base64'
5
- require 'fileutils'
6
- require 'securerandom'
7
-
8
- describe Patron::Session do
9
-
10
- def yaml_load(str)
11
- if RUBY_VERSION >= '3.1.0'
12
- YAML::safe_load(str, permitted_classes: [OpenStruct])
13
- else
14
- YAML::load(str)
15
- end
16
- end
17
-
18
- before(:each) do
19
- @session = Patron::Session.new
20
- @session.base_url = "http://localhost:9001"
21
- end
22
-
23
- context 'when trying a non-HTTP(s) URL' do
24
- forbidden_protos = %w( smb tftp imap smtp telnet dict ftp sftp scp file gopher )
25
- forbidden_protos.each do |forbidden_proto|
26
- it "should deny a #{forbidden_proto.upcase} request" do
27
- expect {
28
- @session.base_url = '%s://localhost' % forbidden_proto
29
- @session.get('')
30
- }.to raise_error(Patron::UnsupportedProtocol)
31
- end
32
- end
33
- end
34
-
35
- it 'does not follow a redirect to a non-HTTP/HTTPS URL' do
36
- # The "/evil-redirect" servlet tries to do a redirect to SMTP,
37
- # which can lead to exploits. By default, libCURL will just follow
38
- # that redirect.
39
- expect {
40
- @session.get('/evil-redirect')
41
- }.to raise_error(Patron::UnsupportedProtocol)
42
- end
43
-
44
- it "should work when forcing ipv4" do
45
- @session.force_ipv4 = true
46
- expect { @session.get("/test") }.to_not raise_error
47
- end
48
-
49
- describe '.escape and #escape' do
50
- it 'makes escape() and unescape() available on the class' do
51
- string = "foo~bar baz/"
52
- escaped = described_class.escape(string)
53
- unescaped = described_class.unescape(escaped)
54
- expect(unescaped).to be == string
55
- end
56
-
57
- it "should escape and unescape strings symetrically" do
58
- string = "foo~bar baz/"
59
- escaped = @session.escape(string)
60
- unescaped = @session.unescape(escaped)
61
- expect(unescaped).to be == string
62
- end
63
-
64
- it "should make e and unescape strings symetrically" do
65
- string = "foo~bar baz/"
66
- escaped = @session.escape(string)
67
- unescaped = @session.unescape(escaped)
68
- expect(unescaped).to be == string
69
- end
70
- end
71
-
72
- it "should raise an error when passed an invalid action" do
73
- expect { @session.request(:bogus, "/test", {}) }.to raise_error(ArgumentError)
74
- end
75
-
76
- it "should raise an error when no URL is provided" do
77
- @session.base_url = nil
78
- expect {@session.get(nil)}.to raise_error(ArgumentError)
79
- end
80
-
81
- it "should retrieve a url with :get" do
82
- response = @session.get("/test")
83
- body = yaml_load(response.body)
84
- expect(body.request_method).to be == "GET"
85
- end
86
-
87
- it "should use full base url" do
88
- @session.base_url = "http://localhost:9001/api/v1"
89
- response = @session.get("/test")
90
- expect(response.url).to be == "http://localhost:9001/api/v1/test"
91
- end
92
-
93
- it 'should ignore #base_url when a full URL is provided' do
94
- @session.base_url = "http://example.com:123"
95
- expect { @session.get("http://localhost:9001/test") }.to_not raise_error(URI::InvalidURIError)
96
- end
97
-
98
- it "should download content with :get and a file path" do
99
- tf = Tempfile.new
100
- tf.close
101
- tmpfile = tf.path
102
-
103
- response = @session.get_file "/test", tmpfile
104
- expect(response.body).to be_nil
105
- body = yaml_load(File.open(tmpfile).read)
106
- expect(body.request_method).to be == "GET"
107
- end
108
-
109
- it "should download correctly(md5 ok) with get_file" do
110
- tf = Tempfile.new
111
- tf.close
112
- tmpfile = tf.path
113
-
114
- response = @session.get_file "/picture", tmpfile
115
- expect(response.body).to be_nil
116
- expect(File.size(File.join(File.dirname(__FILE__),"../pic.png"))).to be == File.size(tmpfile)
117
- end
118
-
119
- it "should follow a 307 redirect with Range headers" do
120
- tf = Tempfile.new
121
- tf.close
122
- tmpfile = tf.path
123
-
124
- response = @session.get_file "/redirect-to-picture", tmpfile, {'Range' => 'bytes=0-3'}
125
- expect(response.body).to be_nil
126
- expect(File.size(tmpfile)).to eq(4)
127
- end
128
-
129
- it "downloads a very large file" do
130
- tf = Tempfile.new
131
- tf.close
132
- tmpfile = tf.path
133
-
134
- @session.get_file "/very-large", tmpfile
135
- expect(File.size(tmpfile)).to eq(15 * 1024 * 1024)
136
- end
137
-
138
- it "raises an exception if the download body limit is exceeded when using direct-to-file download" do
139
- tf = Tempfile.new
140
- tf.close
141
- tmpfile = tf.path
142
-
143
- @session.download_byte_limit = 1024
144
- @session.buffer_size = 1024
145
- expect {
146
- @session.get_file "/very-large", tmpfile
147
- }.to raise_error(Patron::Error)
148
- # On Ruby 2.x Patron::Aborted takes precedence, but
149
- # on 1.9 it will be Patron::PartialFileError, so we cannot match on exception.
150
- #
151
- # Not clear why, but Patron (or libCURL, or the two in combination)
152
- # may fetch more bytes than we permit it to - for example,
153
- # on Darwin the limit is respected exactly, while on Linux in GH actions
154
- # it does retrieve substantially more.
155
- # The URL we are hitting is serving a file that is 15 megabytes. On Linux
156
- # the tempfile fills to 103344 in our tests, so this is probably either
157
- # OS-dependent or related to timings with the RubyVM context switching.
158
- # To ensure our limiting works, it is enough to check that the downloaded file
159
- # is perceptibly smaller than the complete size of 15MB. Let's say < 1MB.
160
- expect(File.size(tmpfile)).to be < (1024 * 1024)
161
- end
162
-
163
- it "raises an exception if the download body limit is exceeded when using download-to-memory" do
164
- @session.download_byte_limit = 1024
165
- @session.buffer_size = 1024
166
- expect {
167
- @session.get "/very-large"
168
- }.to raise_error(Patron::Error)
169
- # On Ruby 2.x Patron::Aborted takes precedence, but
170
- # on 1.9 it will be Patron::PartialFileError
171
- end
172
-
173
- it "should not send the user-agent if it has been deleted from headers" do
174
- @session.headers.delete 'User-Agent'
175
- response = @session.get("/test")
176
- body = yaml_load(response.body)
177
- expect(body.header["user-agent"]).to be_nil
178
- end
179
-
180
- it "should set the default User-agent" do
181
- response = @session.get("/test")
182
- body = yaml_load(response.body)
183
- expect(body.header["user-agent"]).to be == [Patron.user_agent_string]
184
- end
185
-
186
- it "should include custom headers in a request" do
187
- response = @session.get("/test", {"User-Agent" => "PatronTest"})
188
- body = yaml_load(response.body)
189
- expect(body.header["user-agent"]).to be == ["PatronTest"]
190
- end
191
-
192
- it "should include default headers in a request, if they were defined" do
193
- @session.headers = {"User-Agent" => "PatronTest"}
194
- response = @session.get("/test")
195
- body = yaml_load(response.body)
196
- expect(body.header["user-agent"]).to be == ["PatronTest"]
197
- end
198
-
199
- it "should merge custom headers with session headers" do
200
- @session.headers["X-Test"] = "Testing"
201
- response = @session.get("/test", {"User-Agent" => "PatronTest"})
202
- body = yaml_load(response.body)
203
- expect(body.header["user-agent"]).to be == ["PatronTest"]
204
- expect(body.header["x-test"]).to be == ["Testing"]
205
- end
206
-
207
- it "accepts the DNS cache timeout option" do
208
- @session.dns_cache_timeout = 60
209
- @session.get("/")
210
- end
211
-
212
- it "should raise an exception on timeout" do
213
- @session.timeout = 1
214
- expect {@session.get("/timeout?millis=1100")}.to raise_error(Patron::TimeoutError)
215
- end
216
-
217
- it "should raise an exception on a sub-second timeout" do
218
- @session.timeout = 0.3
219
- expect {@session.get("/timeout?millis=400")}.to raise_error(Patron::TimeoutError)
220
- end
221
-
222
- it "should raise an exception on timeout when reading from a slow resource and low speed limit is set" do
223
- @session.timeout = 10 # Greater than the low_speed_time
224
- @session.low_speed_time = 2
225
- @session.low_speed_limit = 10
226
-
227
- # Our test server starts sending the body only after 5 seconds. We should be able to abort
228
- # by exceeding the low_speed_time.
229
- started = Time.now.to_f
230
- expect {@session.get("/slow")}.to raise_error(Patron::TimeoutError)
231
- delta_s = Time.now.to_f - started
232
- # For some reason libCURL times out after 3 seconds instead of 2 sometimes.
233
- expect(delta_s).to be_within(0.1).of(2).or be_within(0.1).of(3)
234
- end
235
-
236
- it "is able to terminate the thread that is running a slow request using Thread#kill (uses the custom unblock)" do
237
- t = Thread.new do
238
- session = Patron::Session.new
239
- session.timeout = 10 # Greater than the sleep duration
240
- session.base_url = "http://localhost:9001"
241
- session.get("/slow")
242
- end
243
-
244
- # Our test server starts sending the body only after 5 seconds. We should be able to abort
245
- # using a signal during that time.
246
- started = Time.now.to_f
247
- sleep 2 # Less than what it takes for the server to respond
248
- t.kill # Kill the thread forcibly
249
- t.join # wrap up the thread. If Patron is still busy there, this join call will still take another 1s.
250
-
251
- delta_s = Time.now.to_f - started
252
- # For some reason libCURL times out after 3 seconds instead of 2 sometimes.
253
- expect(delta_s).to be_within(0.1).of(2).or be_within(0.1).of(3)
254
- end
255
-
256
- it "is able to terminate the process that is running a slow request with SIGINT" do
257
- pid = Process.fork do
258
- trap('SIGINT') do
259
- exit # exit the process
260
- end
261
- session = Patron::Session.new
262
- session.timeout = 10 # Greater than the sleep duration
263
- session.base_url = "http://localhost:9001"
264
- session.get("/slow")
265
- end
266
-
267
- # Our test server starts sending the body only after 5 seconds. We should be able to abort
268
- # using a signal during that time.
269
- started = Time.now.to_f
270
- sleep 2 # Less than what it takes for the server to respond
271
- Process.kill("INT", pid) # Signal the process...
272
- Process.wait(pid) # wrap up the process. If Patron is still busy there, this call will still take another 1s.
273
- delta_s = Time.now.to_f - started
274
- # For some reason libCURL times out after 3 seconds instead of 2 sometimes.
275
- expect(delta_s).to be_within(0.1).of(2).or be_within(0.1).of(3)
276
- end
277
-
278
- it "receives progress callbacks" do
279
- session = Patron::Session.new
280
- session.timeout = 10 # # Greater than the /slow respond time (5 seconds)
281
- session.base_url = "http://localhost:9001"
282
- callback_args = []
283
- session.progress_callback = Proc.new {|dltotal, dlnow, ultotal, ulnow|
284
- callback_args << [dltotal, dlnow, ultotal, ulnow]
285
- }
286
- session.get("/slow")
287
- expect(callback_args).not_to be_empty
288
-
289
- # Make sure that the callback setting was not reset.
290
- callback_args = []
291
- session.get("/slow")
292
- expect(callback_args).not_to be_empty
293
- end
294
-
295
- it "should follow redirects by default" do
296
- @session.max_redirects = 1
297
- response = @session.get("/redirect")
298
- body = yaml_load(response.body)
299
- expect(response.status).to be == 200
300
- expect(body.path).to be == "/test"
301
- end
302
-
303
- it "should not keep the Location header from the redirecting response" do
304
- @session.max_redirects = 1
305
- response = @session.get("/redirect")
306
- expect(response.headers['Location']).to be_nil
307
- end
308
-
309
- it "should include redirect count in response" do
310
- @session.max_redirects = 1
311
- response = @session.get("/redirect")
312
- expect(response.redirect_count).to be == 1
313
- end
314
-
315
- it "should not follow redirects when configured to do so" do
316
- @session.max_redirects = 0
317
- response = @session.get("/redirect")
318
- expect(response.status).to be == 301
319
- expect(response.body).to be_empty
320
- end
321
-
322
- it "should retrieve URL metadata with :head" do
323
- response = @session.head("/test")
324
- expect(response.status).to be == 200
325
- expect(response.body).to be_empty
326
- expect(response.headers).to_not be_empty
327
- end
328
-
329
- it "should send a delete request with :delete" do
330
- response = @session.delete("/test")
331
- body = yaml_load(response.body)
332
- expect(body.request_method).to be == "DELETE"
333
- end
334
-
335
- it "should send a COPY request with :copy" do
336
- response = @session.copy("/test", "/test2")
337
- body = yaml_load(response.body)
338
- expect(body.request_method).to be == "COPY"
339
- end
340
-
341
- it "should include a Destination header in COPY requests" do
342
- response = @session.copy("/test", "/test2")
343
- body = yaml_load(response.body)
344
- expect(body.header['destination'].first).to be == "/test2"
345
- end
346
-
347
- it "should upload data with :get" do
348
- data = "upload data"
349
- response = @session.request(:get, "/test", {}, :data => data)
350
- body = yaml_load(response.body)
351
- expect(body.request_method).to be == "GET"
352
- expect(body.header['content-length']).to be == [data.size.to_s]
353
- end
354
-
355
- it "should call to_s on the data being uploaded via GET if it is not already a String" do
356
- data = 12345
357
- response = @session.request(:get, "/test", {}, :data => data)
358
- body = yaml_load(response.body)
359
- expect(body.request_method).to be == "GET"
360
- end
361
-
362
- it "should upload data with :get" do
363
- # Sending a request body with a GET request is a technique seldom used,
364
- # but it does get used nevertheless - for instance, it is a usual
365
- # practice when interacting with an ElasticSearch cluster where
366
- # you can have very deeply going queries, which are still technically GETs
367
- data = Random.new.bytes(1024 * 24)
368
- response = @session.request(:get, "/test", {}, :data => data)
369
- body = yaml_load(response.body)
370
- expect(body.request_method).to be == "GET"
371
- expect(body.header['content-length']).to be == [data.size.to_s]
372
- end
373
-
374
- it "should upload data with :put" do
375
- data = Random.new.bytes(1024 * 24)
376
- response = @session.put("/test", data)
377
- body = yaml_load(response.body)
378
- expect(body.request_method).to be == "PUT"
379
- expect(body.header['content-length']).to be == [data.size.to_s]
380
- end
381
-
382
- it "should upload a Tempfile with :put" do
383
- data = Tempfile.new 'data-buffer'
384
- data << Random.new.bytes(1024 * 64)
385
- data.flush; data.rewind
386
-
387
- response = @session.put("/test", data, {'Expect' => ''})
388
- body = yaml_load(response.body)
389
- expect(body.request_method).to be == "PUT"
390
- expect(body.header['content-length']).to be == [data.size.to_s]
391
- end
392
-
393
- it "should upload data with :patch" do
394
- data = "upload data"
395
- response = @session.patch("/testpatch", data)
396
- body = yaml_load(response.body)
397
- expect(body.body).to eq("upload data")
398
- end
399
-
400
- it "should upload data with :delete" do
401
- data = "upload data"
402
- response = @session.request(:delete, "/test", {}, :data => data)
403
- body = yaml_load(response.body)
404
- expect(body.request_method).to be == "DELETE"
405
- expect(body.header['content-length']).to be == [data.size.to_s]
406
- end
407
-
408
- it "should raise when no data is provided to :put" do
409
- expect { @session.put("/test", nil) }.to raise_error(ArgumentError)
410
- end
411
-
412
- it "should upload a file with :put" do
413
- response = @session.put_file("/test", "LICENSE")
414
- body = yaml_load(response.body)
415
- expect(body.request_method).to be == "PUT"
416
- end
417
-
418
- it "should raise when no file is provided to :put" do
419
- expect { @session.put_file("/test", nil) }.to raise_error(ArgumentError)
420
- end
421
-
422
- it "should use chunked encoding when uploading a file with :put" do
423
- response = @session.put_file("/test", "LICENSE")
424
- body = yaml_load(response.body)
425
- expect(body.header['transfer-encoding'].first).to be == "chunked"
426
- end
427
-
428
- it "should call to_s on the data being uploaded via POST if it is not already a String" do
429
- data = 12345
430
- response = @session.post("/testpost", data)
431
- body = yaml_load(response.body)
432
- expect(body['body']).to eq("12345")
433
- end
434
-
435
- it "should upload data with :post" do
436
- data = "upload data"
437
- response = @session.post("/test", data)
438
- body = yaml_load(response.body)
439
- expect(body.request_method).to be == "POST"
440
- expect(body.header['content-length']).to be == [data.size.to_s]
441
- end
442
-
443
- it "should POST a hash of arguments as a urlencoded form" do
444
- data = {:foo => 123, 'baz' => '++hello world++'}
445
- response = @session.post("/testpost", data)
446
- body = yaml_load(response.body)
447
- expect(body['content_type']).to be == "application/x-www-form-urlencoded"
448
- expect(body['body']).to match(/baz=%2B%2Bhello%20world%2B%2B/)
449
- expect(body['body']).to match(/foo=123/)
450
- end
451
-
452
- it "should raise when no data is provided to :post" do
453
- expect { @session.post("/test", nil) }.to raise_error(ArgumentError)
454
- end
455
-
456
- it "should upload a file with :post" do
457
- response = @session.post_file("/test", "LICENSE")
458
- body = yaml_load(response.body)
459
- expect(body.request_method).to be == "POST"
460
- end
461
-
462
- it "should upload a multipart with :post" do
463
- response = @session.post_multipart("/test", { :test_data => "123" }, { :test_file => "LICENSE" } )
464
- body = yaml_load(response.body)
465
- expect(body.request_method).to be == "POST"
466
- end
467
-
468
- it "should raise when no file is provided to :post" do
469
- expect { @session.post_file("/test", nil) }.to raise_error(ArgumentError)
470
- end
471
-
472
- it "should use chunked encoding when uploading a file with :post" do
473
- response = @session.post_file("/test", "LICENSE")
474
- body = yaml_load(response.body)
475
- expect(body.header['transfer-encoding'].first).to be == "chunked"
476
- end
477
-
478
- it "should pass credentials as http basic auth" do
479
- @session.username = "foo"
480
- @session.password = "bar"
481
- response = @session.get("/test")
482
- body = yaml_load(response.body)
483
- expect(body.header['authorization']).to be == [encode_authz("foo", "bar")]
484
- end
485
-
486
- it "should store cookies across multiple requests" do
487
- tf = Tempfile.new('cookiejar')
488
- tf.close
489
- cookie_jar_path = tf.path
490
-
491
- @session.handle_cookies(cookie_jar_path)
492
- response = @session.get("/setcookie").body
493
-
494
- cookie_jar_contents = tf.open.read
495
- tf.unlink
496
- expect(cookie_jar_contents).not_to be_empty
497
- expect(cookie_jar_contents).to include('Netscape HTTP Cookie File')
498
- end
499
-
500
- it "should handle cookies if set" do
501
- @session.handle_cookies
502
- response = @session.get("/setcookie").body
503
- expect(yaml_load(response).header['cookie'].first).to be == "session_id=foo123"
504
- end
505
-
506
- it "should not handle cookies by default" do
507
- response = @session.get("/setcookie").body
508
- expect(yaml_load(response).header).to_not include('cookie')
509
- end
510
-
511
- it "should ignore a wrong Content-Length when asked to" do
512
- expect {
513
- @session.ignore_content_length = true
514
- @session.get("/wrongcontentlength", { 'Connection' => 'close' })
515
- }.to_not raise_error
516
- end
517
-
518
- it "should fail by default with a Content-Length too high" do
519
- expect {
520
- @session.ignore_content_length = nil
521
- @session.get("/wrongcontentlength", { 'Connection' => 'close' })
522
- }.to raise_error(Patron::PartialFileError)
523
- end
524
-
525
- it "should raise exception if cookie store is not writable or readable" do
526
- expect { @session.handle_cookies("/trash/clash/foo") }.to raise_error(ArgumentError)
527
- end
528
-
529
- it "should work with multiple threads" do
530
- threads = []
531
- 3.times do
532
- threads << Thread.new do
533
- session = Patron::Session.new
534
- session.base_url = "http://localhost:9001"
535
- session.post_file("/test", "LICENSE")
536
- end
537
- end
538
- threads.each {|t| t.join }
539
- end
540
-
541
- it "should limit the buffer_size" do
542
- # Buffer size is tricky to test, as it only affects the buffer size for each
543
- # read and it's not really visible at this, higher level. It's also only a
544
- # suggestion rather than a command so it may not even take affect. Currently
545
- # we just test that the response completes without any issues, it would be nice
546
- # to have a more robust test here.
547
- @session.buffer_size = 1
548
-
549
- body = nil
550
-
551
- expect {
552
- response = @session.get("/test")
553
- body = yaml_load(response.body)
554
- }.to_not raise_error
555
-
556
- expect(body.request_method).to be == "GET"
557
- end
558
-
559
- it "should automatically decompress using Content-Encoding if requested" do
560
- @session.automatic_content_encoding = true
561
- response = @session.get('/gzip-compressed')
562
-
563
- expect(response.headers['Content-Length']).to eq('125')
564
-
565
- body = response.body
566
- expect(body).to match(/Some highly compressible data/)
567
- expect(body.bytesize).to eq(29696)
568
- end
569
-
570
- it "should serialize query params and append them to the url" do
571
- response = @session.request(:get, "/test", {}, :query => {:foo => "bar"})
572
- request = yaml_load(response.body)
573
- expect(request.path + '?' + request.query_string).to be == "/test?foo=bar"
574
- end
575
-
576
- it "should merge parameters in the :query option with pre-existing query parameters" do
577
- response = @session.request(:get, "/test?foo=bar", {}, :query => {:baz => "quux"})
578
- request = yaml_load(response.body)
579
- expect(request.path + '?' + request.query_string).to be == "/test?foo=bar&baz=quux"
580
- end
581
-
582
- def encode_authz(user, passwd)
583
- "Basic " + Base64.encode64("#{user}:#{passwd}").strip
584
- end
585
-
586
- describe 'when using a subclass with a custom Response' do
587
-
588
- class CustomResponse
589
- attr_reader :constructor_args
590
- def initialize(*constructor_args)
591
- @constructor_args = constructor_args
592
- end
593
- end
594
-
595
- class CustomizedSession < Patron::Session
596
- def response_class
597
- CustomResponse
598
- end
599
- end
600
-
601
- it 'instantiates the customized response object' do
602
- @session = CustomizedSession.new
603
- @session.base_url = "http://localhost:9001"
604
- response = @session.request(:get, "/test", {}, :query => {:foo => "bar"})
605
-
606
- expect(response).to be_kind_of(CustomResponse)
607
- expect(response.constructor_args.length).to eq(6)
608
- end
609
- end
610
-
611
- describe 'when instantiating with hash arguments' do
612
-
613
- let(:args) { {
614
- :timeout => 10,
615
- :dns_cache_timeout => 10,
616
- :base_url => 'http://localhost:9001',
617
- :headers => {'User-Agent' => 'myapp/1.0'}
618
- } }
619
-
620
- let(:session) { Patron::Session.new(args) }
621
-
622
- it 'sets the base_url' do
623
- expect(session.base_url).to be == args[:base_url]
624
- end
625
-
626
- it 'sets timeout' do
627
- expect(session.timeout).to be == args[:timeout]
628
- end
629
-
630
- it 'sets DNS cache timeout' do
631
- expect(session.dns_cache_timeout).to be == args[:dns_cache_timeout]
632
- end
633
-
634
- it 'sets headers' do
635
- expect(session.headers).to be == args[:headers]
636
- end
637
-
638
- context 'when given an incorrect accessor name' do
639
- let(:args) { { :not_a_real_accessor => 'http://localhost:9001' }}
640
- it 'raises no method error' do
641
- expect { session }.to raise_error NoMethodError
642
- end
643
- end
644
-
645
- end
646
-
647
- describe 'when instantiating with a block' do
648
- args = {
649
- :timeout => 10,
650
- :base_url => 'http://localhost:9001',
651
- :headers => {'User-Agent' => 'myapp/1.0'}
652
- }
653
-
654
- session = Patron::Session.new do |patron|
655
- patron.timeout = args[:timeout]
656
- patron.base_url = args[:base_url]
657
- patron.headers = args[:headers]
658
- end
659
-
660
- it 'sets the base_url' do
661
- expect(session.base_url).to be == args[:base_url]
662
- end
663
-
664
- it 'sets timeout' do
665
- expect(session.timeout).to be == args[:timeout]
666
- end
667
-
668
- it 'sets headers' do
669
- expect(session.headers).to be == args[:headers]
670
- end
671
-
672
- context 'when given an incorrect accessor name' do
673
- it 'raises no method error' do
674
- expect {
675
- Patron::Session.new do |patron|
676
- patron.timeoutttt = args[:timeout]
677
- end
678
- }.to raise_error NoMethodError
679
- end
680
- end
681
- end
682
-
683
- # ------------------------------------------------------------------------
684
- describe 'when debug is enabled' do
685
- it 'it should not clobber stderr' do
686
- rdev = STDERR.stat.rdev
687
-
688
- retval = @session.enable_debug
689
- expect(retval).to eq(@session)
690
- expect(STDERR.stat.rdev).to be == rdev
691
-
692
- retval = @session.enable_debug
693
- expect(retval).to eq(@session)
694
- expect(STDERR.stat.rdev).to be == rdev
695
- end
696
- end
697
-
698
- end