glebtv-httpclient 3.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 (59) hide show
  1. checksums.yaml +7 -0
  2. data/README.rdoc +108 -0
  3. data/bin/httpclient +65 -0
  4. data/lib/glebtv-httpclient.rb +1 -0
  5. data/lib/hexdump.rb +50 -0
  6. data/lib/http-access2/cookie.rb +1 -0
  7. data/lib/http-access2/http.rb +1 -0
  8. data/lib/http-access2.rb +55 -0
  9. data/lib/httpclient/auth.rb +899 -0
  10. data/lib/httpclient/cacert.p7s +1912 -0
  11. data/lib/httpclient/connection.rb +88 -0
  12. data/lib/httpclient/cookie.rb +438 -0
  13. data/lib/httpclient/http.rb +1050 -0
  14. data/lib/httpclient/include_client.rb +83 -0
  15. data/lib/httpclient/session.rb +1031 -0
  16. data/lib/httpclient/ssl_config.rb +403 -0
  17. data/lib/httpclient/timeout.rb +140 -0
  18. data/lib/httpclient/util.rb +186 -0
  19. data/lib/httpclient/version.rb +3 -0
  20. data/lib/httpclient.rb +1157 -0
  21. data/lib/oauthclient.rb +110 -0
  22. data/sample/async.rb +8 -0
  23. data/sample/auth.rb +11 -0
  24. data/sample/cookie.rb +18 -0
  25. data/sample/dav.rb +103 -0
  26. data/sample/howto.rb +49 -0
  27. data/sample/oauth_buzz.rb +57 -0
  28. data/sample/oauth_friendfeed.rb +59 -0
  29. data/sample/oauth_twitter.rb +61 -0
  30. data/sample/ssl/0cert.pem +22 -0
  31. data/sample/ssl/0key.pem +30 -0
  32. data/sample/ssl/1000cert.pem +19 -0
  33. data/sample/ssl/1000key.pem +18 -0
  34. data/sample/ssl/htdocs/index.html +10 -0
  35. data/sample/ssl/ssl_client.rb +22 -0
  36. data/sample/ssl/webrick_httpsd.rb +29 -0
  37. data/sample/stream.rb +21 -0
  38. data/sample/thread.rb +27 -0
  39. data/sample/wcat.rb +21 -0
  40. data/test/ca-chain.cert +44 -0
  41. data/test/ca.cert +23 -0
  42. data/test/client.cert +19 -0
  43. data/test/client.key +15 -0
  44. data/test/helper.rb +129 -0
  45. data/test/htdigest +1 -0
  46. data/test/htpasswd +2 -0
  47. data/test/runner.rb +2 -0
  48. data/test/server.cert +19 -0
  49. data/test/server.key +15 -0
  50. data/test/sslsvr.rb +65 -0
  51. data/test/subca.cert +21 -0
  52. data/test/test_auth.rb +321 -0
  53. data/test/test_cookie.rb +412 -0
  54. data/test/test_hexdump.rb +14 -0
  55. data/test/test_http-access2.rb +507 -0
  56. data/test/test_httpclient.rb +1801 -0
  57. data/test/test_include_client.rb +52 -0
  58. data/test/test_ssl.rb +235 -0
  59. metadata +102 -0
data/lib/httpclient.rb ADDED
@@ -0,0 +1,1157 @@
1
+ # HTTPClient - HTTP client library.
2
+ # Copyright (C) 2000-2009 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
3
+ #
4
+ # This program is copyrighted free software by NAKAMURA, Hiroshi. You can
5
+ # redistribute it and/or modify it under the same terms of Ruby's license;
6
+ # either the dual license version in 2003, or any later version.
7
+
8
+
9
+ require 'stringio'
10
+ require 'digest/sha1'
11
+
12
+ # Extra library
13
+ require 'httpclient/version'
14
+ require 'httpclient/util'
15
+ require 'httpclient/ssl_config'
16
+ require 'httpclient/connection'
17
+ require 'httpclient/session'
18
+ require 'httpclient/http'
19
+ require 'httpclient/auth'
20
+ require 'httpclient/cookie'
21
+
22
+ # :main:HTTPClient
23
+ # The HTTPClient class provides several methods for accessing Web resources
24
+ # via HTTP.
25
+ #
26
+ # HTTPClient instance is designed to be MT-safe. You can call a HTTPClient
27
+ # instance from several threads without synchronization after setting up an
28
+ # instance.
29
+ #
30
+ # clnt = HTTPClient.new
31
+ # clnt.set_cookie_store('/home/nahi/cookie.dat')
32
+ # urls.each do |url|
33
+ # Thread.new(url) do |u|
34
+ # p clnt.head(u).status
35
+ # end
36
+ # end
37
+ #
38
+ # == How to use
39
+ #
40
+ # At first, how to create your client. See initialize for more detail.
41
+ #
42
+ # 1. Create simple client.
43
+ #
44
+ # clnt = HTTPClient.new
45
+ #
46
+ # 2. Accessing resources through HTTP proxy. You can use environment
47
+ # variable 'http_proxy' or 'HTTP_PROXY' instead.
48
+ #
49
+ # clnt = HTTPClient.new('http://myproxy:8080')
50
+ #
51
+ # === How to retrieve web resources
52
+ #
53
+ # See get and get_content.
54
+ #
55
+ # 1. Get content of specified URL. It returns HTTP::Message object and
56
+ # calling 'body' method of it returns a content String.
57
+ #
58
+ # puts clnt.get('http://dev.ctor.org/').body
59
+ #
60
+ # 2. For getting content directly, use get_content. It follows redirect
61
+ # response and returns a String of whole result.
62
+ #
63
+ # puts clnt.get_content('http://dev.ctor.org/')
64
+ #
65
+ # 3. You can pass :follow_redirect option to follow redirect response in get.
66
+ #
67
+ # puts clnt.get('http://dev.ctor.org/', :follow_redirect => true)
68
+ #
69
+ # 4. Get content as chunks of String. It yields chunks of String.
70
+ #
71
+ # clnt.get_content('http://dev.ctor.org/') do |chunk|
72
+ # puts chunk
73
+ # end
74
+ #
75
+ # === Invoking other HTTP methods
76
+ #
77
+ # See head, get, post, put, delete, options, propfind, proppatch and trace.
78
+ # It returns a HTTP::Message instance as a response.
79
+ #
80
+ # 1. Do HEAD request.
81
+ #
82
+ # res = clnt.head(uri)
83
+ # p res.header['Last-Modified'][0]
84
+ #
85
+ # 2. Do GET request with query.
86
+ #
87
+ # query = { 'keyword' => 'ruby', 'lang' => 'en' }
88
+ # res = clnt.get(uri, query)
89
+ # p res.status
90
+ # p res.contenttype
91
+ # p res.header['X-Custom']
92
+ # puts res.body
93
+ #
94
+ # You can also use keyword argument style.
95
+ #
96
+ # res = clnt.get(uri, :query => { :keyword => 'ruby', :lang => 'en' })
97
+ #
98
+ # === How to POST
99
+ #
100
+ # See post.
101
+ #
102
+ # 1. Do POST a form data.
103
+ #
104
+ # body = { 'keyword' => 'ruby', 'lang' => 'en' }
105
+ # res = clnt.post(uri, body)
106
+ #
107
+ # Keyword argument style.
108
+ #
109
+ # res = clnt.post(uri, :body => ...)
110
+ #
111
+ # 2. Do multipart file upload with POST. No need to set extra header by
112
+ # yourself from httpclient/2.1.4.
113
+ #
114
+ # File.open('/tmp/post_data') do |file|
115
+ # body = { 'upload' => file, 'user' => 'nahi' }
116
+ # res = clnt.post(uri, body)
117
+ # end
118
+ #
119
+ # 3. Do multipart wth custom body.
120
+ #
121
+ # File.open('/tmp/post_data') do |file|
122
+ # body = [{ 'Content-Type' => 'application/atom+xml; charset=UTF-8',
123
+ # :content => '<entry>...</entry>' },
124
+ # { 'Content-Type' => 'video/mp4',
125
+ # 'Content-Transfer-Encoding' => 'binary',
126
+ # :content => file }]
127
+ # res = clnt.post(uri, body)
128
+ # end
129
+ #
130
+ # === Accessing via SSL
131
+ #
132
+ # Ruby needs to be compiled with OpenSSL.
133
+ #
134
+ # 1. Get content of specified URL via SSL.
135
+ # Just pass an URL which starts with 'https://'.
136
+ #
137
+ # https_url = 'https://www.rsa.com'
138
+ # clnt.get(https_url)
139
+ #
140
+ # 2. Getting peer certificate from response.
141
+ #
142
+ # res = clnt.get(https_url)
143
+ # p res.peer_cert #=> returns OpenSSL::X509::Certificate
144
+ #
145
+ # 3. Configuring OpenSSL options. See HTTPClient::SSLConfig for more details.
146
+ #
147
+ # user_cert_file = 'cert.pem'
148
+ # user_key_file = 'privkey.pem'
149
+ # clnt.ssl_config.set_client_cert_file(user_cert_file, user_key_file)
150
+ # clnt.get(https_url)
151
+ #
152
+ # === Handling Cookies
153
+ #
154
+ # 1. Using volatile Cookies. Nothing to do. HTTPClient handles Cookies.
155
+ #
156
+ # clnt = HTTPClient.new
157
+ # res = clnt.get(url1) # receives Cookies.
158
+ # res = clnt.get(url2) # sends Cookies if needed.
159
+ # p res.cookies
160
+ #
161
+ # 2. Saving non volatile Cookies to a specified file. Need to set a file at
162
+ # first and invoke save method at last.
163
+ #
164
+ # clnt = HTTPClient.new
165
+ # clnt.set_cookie_store('/home/nahi/cookie.dat')
166
+ # clnt.get(url)
167
+ # ...
168
+ # clnt.save_cookie_store
169
+ #
170
+ # 3. Disabling Cookies.
171
+ #
172
+ # clnt = HTTPClient.new
173
+ # clnt.cookie_manager = nil
174
+ #
175
+ # === Configuring authentication credentials
176
+ #
177
+ # 1. Authentication with Web server. Supports BasicAuth, DigestAuth, and
178
+ # Negotiate/NTLM (requires ruby/ntlm module).
179
+ #
180
+ # clnt = HTTPClient.new
181
+ # domain = 'http://dev.ctor.org/http-access2/'
182
+ # user = 'user'
183
+ # password = 'user'
184
+ # clnt.set_auth(domain, user, password)
185
+ # p clnt.get('http://dev.ctor.org/http-access2/login').status
186
+ #
187
+ # 2. Authentication with Proxy server. Supports BasicAuth and NTLM
188
+ # (requires win32/sspi)
189
+ #
190
+ # clnt = HTTPClient.new(proxy)
191
+ # user = 'proxy'
192
+ # password = 'proxy'
193
+ # clnt.set_proxy_auth(user, password)
194
+ # p clnt.get(url)
195
+ #
196
+ # === Invoking HTTP methods with custom header
197
+ #
198
+ # Pass a Hash or an Array for header argument.
199
+ #
200
+ # header = { 'Accept' => 'text/html' }
201
+ # clnt.get(uri, query, header)
202
+ #
203
+ # header = [['Accept', 'image/jpeg'], ['Accept', 'image/png']]
204
+ # clnt.get_content(uri, query, header)
205
+ #
206
+ # === Invoking HTTP methods asynchronously
207
+ #
208
+ # See head_async, get_async, post_async, put_async, delete_async,
209
+ # options_async, propfind_async, proppatch_async, and trace_async.
210
+ # It immediately returns a HTTPClient::Connection instance as a returning value.
211
+ #
212
+ # connection = clnt.post_async(url, body)
213
+ # print 'posting.'
214
+ # while true
215
+ # break if connection.finished?
216
+ # print '.'
217
+ # sleep 1
218
+ # end
219
+ # puts '.'
220
+ # res = connection.pop
221
+ # p res.status
222
+ # p res.body.read # res.body is an IO for the res of async method.
223
+ #
224
+ # === Shortcut methods
225
+ #
226
+ # You can invoke get_content, get, etc. without creating HTTPClient instance.
227
+ #
228
+ # ruby -rhttpclient -e 'puts HTTPClient.get_content(ARGV.shift)' http://dev.ctor.org/
229
+ # ruby -rhttpclient -e 'p HTTPClient.head(ARGV.shift).header["last-modified"]' http://dev.ctor.org/
230
+ #
231
+ class HTTPClient
232
+ RUBY_VERSION_STRING = "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
233
+ LIB_NAME = "(#{VERSION}, #{RUBY_VERSION_STRING})"
234
+
235
+ include Util
236
+
237
+ # Raised for indicating running environment configuration error for example
238
+ # accessing via SSL under the ruby which is not compiled with OpenSSL.
239
+ class ConfigurationError < StandardError
240
+ end
241
+
242
+ # Raised for indicating HTTP response error.
243
+ class BadResponseError < RuntimeError
244
+ # HTTP::Message:: a response
245
+ attr_reader :res
246
+
247
+ def initialize(msg, res = nil) # :nodoc:
248
+ super(msg)
249
+ @res = res
250
+ end
251
+ end
252
+
253
+ # Raised for indicating a timeout error.
254
+ class TimeoutError < RuntimeError
255
+ end
256
+
257
+ # Raised for indicating a connection timeout error.
258
+ # You can configure connection timeout via HTTPClient#connect_timeout=.
259
+ class ConnectTimeoutError < TimeoutError
260
+ end
261
+
262
+ # Raised for indicating a request sending timeout error.
263
+ # You can configure request sending timeout via HTTPClient#send_timeout=.
264
+ class SendTimeoutError < TimeoutError
265
+ end
266
+
267
+ # Raised for indicating a response receiving timeout error.
268
+ # You can configure response receiving timeout via
269
+ # HTTPClient#receive_timeout=.
270
+ class ReceiveTimeoutError < TimeoutError
271
+ end
272
+
273
+ # Deprecated. just for backward compatibility
274
+ class Session
275
+ BadResponse = ::HTTPClient::BadResponseError
276
+ end
277
+
278
+ class << self
279
+ %w(get_content post_content head get post put delete options propfind proppatch trace).each do |name|
280
+ eval <<-EOD
281
+ def #{name}(*arg, &block)
282
+ clnt = new
283
+ begin
284
+ clnt.#{name}(*arg, &block)
285
+ ensure
286
+ clnt.reset_all
287
+ end
288
+ end
289
+ EOD
290
+ end
291
+
292
+ private
293
+
294
+ def attr_proxy(symbol, assignable = false)
295
+ name = symbol.to_s
296
+ define_method(name) {
297
+ @session_manager.__send__(name)
298
+ }
299
+ if assignable
300
+ aname = name + '='
301
+ define_method(aname) { |rhs|
302
+ reset_all
303
+ @session_manager.__send__(aname, rhs)
304
+ }
305
+ end
306
+ end
307
+ end
308
+
309
+ # HTTPClient::SSLConfig:: SSL configurator.
310
+ attr_reader :ssl_config
311
+ # WebAgent::CookieManager:: Cookies configurator.
312
+ attr_accessor :cookie_manager
313
+ # An array of response HTTP message body String which is used for loop-back
314
+ # test. See test/* to see how to use it. If you want to do loop-back test
315
+ # of HTTP header, use test_loopback_http_response instead.
316
+ attr_reader :test_loopback_response
317
+ # An array of request filter which can trap HTTP request/response.
318
+ # See HTTPClient::WWWAuth to see how to use it.
319
+ attr_reader :request_filter
320
+ # HTTPClient::ProxyAuth:: Proxy authentication handler.
321
+ attr_reader :proxy_auth
322
+ # HTTPClient::WWWAuth:: WWW authentication handler.
323
+ attr_reader :www_auth
324
+ # How many times get_content and post_content follows HTTP redirect.
325
+ # 10 by default.
326
+ attr_accessor :follow_redirect_count
327
+
328
+ # Set HTTP version as a String:: 'HTTP/1.0' or 'HTTP/1.1'
329
+ attr_proxy(:protocol_version, true)
330
+ # Connect timeout in sec.
331
+ attr_proxy(:connect_timeout, true)
332
+ # Request sending timeout in sec.
333
+ attr_proxy(:send_timeout, true)
334
+ # Response receiving timeout in sec.
335
+ attr_proxy(:receive_timeout, true)
336
+ # Reuse the same connection within this timeout in sec. from last used.
337
+ attr_proxy(:keep_alive_timeout, true)
338
+ # Size of reading block for non-chunked response.
339
+ attr_proxy(:read_block_size, true)
340
+ # Negotiation retry count for authentication. 5 by default.
341
+ attr_proxy(:protocol_retry_count, true)
342
+ # if your ruby is older than 2005-09-06, do not set socket_sync = false to
343
+ # avoid an SSL socket blocking bug in openssl/buffering.rb.
344
+ attr_proxy(:socket_sync, true)
345
+ # User-Agent header in HTTP request.
346
+ attr_proxy(:agent_name, true)
347
+ # From header in HTTP request.
348
+ attr_proxy(:from, true)
349
+ # An array of response HTTP String (not a HTTP message body) which is used
350
+ # for loopback test. See test/* to see how to use it.
351
+ attr_proxy(:test_loopback_http_response)
352
+ # Decompress a compressed (with gzip or deflate) content body transparently. false by default.
353
+ attr_proxy(:transparent_gzip_decompression, true)
354
+ # Local socket address. Set HTTPClient#socket_local.host and HTTPClient#socket_local.port to specify local binding hostname and port of TCP socket.
355
+ attr_proxy(:socket_local, true)
356
+
357
+ # Default header for PROPFIND request.
358
+ PROPFIND_DEFAULT_EXTHEADER = { 'Depth' => '0' }
359
+
360
+ # Default User-Agent header
361
+ DEFAULT_AGENT_NAME = 'HTTPClient/1.0'
362
+
363
+ # Creates a HTTPClient instance which manages sessions, cookies, etc.
364
+ #
365
+ # HTTPClient.new takes 3 optional arguments for proxy url string,
366
+ # User-Agent String and From header String. User-Agent and From are embedded
367
+ # in HTTP request Header if given. No User-Agent and From header added
368
+ # without setting it explicitly.
369
+ #
370
+ # proxy = 'http://myproxy:8080'
371
+ # agent_name = 'MyAgent/0.1'
372
+ # from = 'from@example.com'
373
+ # HTTPClient.new(proxy, agent_name, from)
374
+ #
375
+ # You can use a keyword argument style Hash. Keys are :proxy, :agent_name
376
+ # and :from.
377
+ #
378
+ # HTTPClient.new(:agent_name => 'MyAgent/0.1')
379
+ def initialize(*args)
380
+ proxy, agent_name, from = keyword_argument(args, :proxy, :agent_name, :from)
381
+ @proxy = nil # assigned later.
382
+ @no_proxy = nil
383
+ @no_proxy_regexps = []
384
+ @www_auth = WWWAuth.new
385
+ @proxy_auth = ProxyAuth.new
386
+ @request_filter = [@proxy_auth, @www_auth]
387
+ @debug_dev = nil
388
+ @redirect_uri_callback = method(:default_redirect_uri_callback)
389
+ @test_loopback_response = []
390
+ @session_manager = SessionManager.new(self)
391
+ @session_manager.agent_name = agent_name || DEFAULT_AGENT_NAME
392
+ @session_manager.from = from
393
+ @session_manager.ssl_config = @ssl_config = SSLConfig.new(self)
394
+ @cookie_manager = WebAgent::CookieManager.new
395
+ @follow_redirect_count = 10
396
+ load_environment
397
+ self.proxy = proxy if proxy
398
+ keep_webmock_compat
399
+ end
400
+
401
+ # webmock 1.6.2 depends on HTTP::Message#body.content to work.
402
+ # let's keep it work iif webmock is loaded for a while.
403
+ def keep_webmock_compat
404
+ if respond_to?(:do_get_block_with_webmock)
405
+ ::HTTP::Message.module_eval do
406
+ def body
407
+ def (o = self.content).content
408
+ self
409
+ end
410
+ o
411
+ end
412
+ end
413
+ end
414
+ end
415
+
416
+ # Returns debug device if exists. See debug_dev=.
417
+ def debug_dev
418
+ @debug_dev
419
+ end
420
+
421
+ # Sets debug device. Once debug device is set, all HTTP requests and
422
+ # responses are dumped to given device. dev must respond to << for dump.
423
+ #
424
+ # Calling this method resets all existing sessions.
425
+ def debug_dev=(dev)
426
+ @debug_dev = dev
427
+ reset_all
428
+ @session_manager.debug_dev = dev
429
+ end
430
+
431
+ # Returns URI object of HTTP proxy if exists.
432
+ def proxy
433
+ @proxy
434
+ end
435
+
436
+ # Sets HTTP proxy used for HTTP connection. Given proxy can be an URI,
437
+ # a String or nil. You can set user/password for proxy authentication like
438
+ # HTTPClient#proxy = 'http://user:passwd@myproxy:8080'
439
+ #
440
+ # You can use environment variable 'http_proxy' or 'HTTP_PROXY' for it.
441
+ # You need to use 'cgi_http_proxy' or 'CGI_HTTP_PROXY' instead if you run
442
+ # HTTPClient from CGI environment from security reason. (HTTPClient checks
443
+ # 'REQUEST_METHOD' environment variable whether it's CGI or not)
444
+ #
445
+ # Calling this method resets all existing sessions.
446
+ def proxy=(proxy)
447
+ if proxy.nil? || proxy.to_s.empty?
448
+ @proxy = nil
449
+ @proxy_auth.reset_challenge
450
+ else
451
+ @proxy = urify(proxy)
452
+ if @proxy.scheme == nil or @proxy.scheme.downcase != 'http' or
453
+ @proxy.host == nil or @proxy.port == nil
454
+ raise ArgumentError.new("unsupported proxy #{proxy}")
455
+ end
456
+ @proxy_auth.reset_challenge
457
+ if @proxy.user || @proxy.password
458
+ @proxy_auth.set_auth(@proxy.user, @proxy.password)
459
+ end
460
+ end
461
+ reset_all
462
+ @session_manager.proxy = @proxy
463
+ @proxy
464
+ end
465
+
466
+ # Returns NO_PROXY setting String if given.
467
+ def no_proxy
468
+ @no_proxy
469
+ end
470
+
471
+ # Sets NO_PROXY setting String. no_proxy must be a comma separated String.
472
+ # Each entry must be 'host' or 'host:port' such as;
473
+ # HTTPClient#no_proxy = 'example.com,example.co.jp:443'
474
+ #
475
+ # 'localhost' is treated as a no_proxy site regardless of explicitly listed.
476
+ # HTTPClient checks given URI objects before accessing it.
477
+ # 'host' is tail string match. No IP-addr conversion.
478
+ #
479
+ # You can use environment variable 'no_proxy' or 'NO_PROXY' for it.
480
+ #
481
+ # Calling this method resets all existing sessions.
482
+ def no_proxy=(no_proxy)
483
+ @no_proxy = no_proxy
484
+ @no_proxy_regexps.clear
485
+ if @no_proxy
486
+ @no_proxy.scan(/([^:,]+)(?::(\d+))?/) do |host, port|
487
+ if host[0] == ?.
488
+ regexp = /#{Regexp.quote(host)}\z/i
489
+ else
490
+ regexp = /(\A|\.)#{Regexp.quote(host)}\z/i
491
+ end
492
+ @no_proxy_regexps << [regexp, port]
493
+ end
494
+ end
495
+ reset_all
496
+ end
497
+
498
+ # Sets credential for Web server authentication.
499
+ # domain:: a String or an URI to specify where HTTPClient should use this
500
+ # credential. If you set uri to nil, HTTPClient uses this credential
501
+ # wherever a server requires it.
502
+ # user:: username String.
503
+ # passwd:: password String.
504
+ #
505
+ # You can set multiple credentials for each uri.
506
+ #
507
+ # clnt.set_auth('http://www.example.com/foo/', 'foo_user', 'passwd')
508
+ # clnt.set_auth('http://www.example.com/bar/', 'bar_user', 'passwd')
509
+ #
510
+ # Calling this method resets all existing sessions.
511
+ def set_auth(domain, user, passwd)
512
+ uri = urify(domain)
513
+ @www_auth.set_auth(uri, user, passwd)
514
+ reset_all
515
+ end
516
+
517
+ # Deprecated. Use set_auth instead.
518
+ def set_basic_auth(domain, user, passwd)
519
+ uri = urify(domain)
520
+ @www_auth.basic_auth.set(uri, user, passwd)
521
+ reset_all
522
+ end
523
+
524
+ # Sets credential for Proxy authentication.
525
+ # user:: username String.
526
+ # passwd:: password String.
527
+ #
528
+ # Calling this method resets all existing sessions.
529
+ def set_proxy_auth(user, passwd)
530
+ @proxy_auth.set_auth(user, passwd)
531
+ reset_all
532
+ end
533
+
534
+ # Sets the filename where non-volatile Cookies be saved by calling
535
+ # save_cookie_store.
536
+ # This method tries to load and managing Cookies from the specified file.
537
+ #
538
+ # Calling this method resets all existing sessions.
539
+ def set_cookie_store(filename)
540
+ @cookie_manager.cookies_file = filename
541
+ @cookie_manager.load_cookies if filename
542
+ reset_all
543
+ end
544
+
545
+ # Try to save Cookies to the file specified in set_cookie_store. Unexpected
546
+ # error will be raised if you don't call set_cookie_store first.
547
+ # (interface mismatch between WebAgent::CookieManager implementation)
548
+ def save_cookie_store
549
+ @cookie_manager.save_cookies
550
+ end
551
+
552
+ # Returns stored cookies.
553
+ def cookies
554
+ if @cookie_manager
555
+ @cookie_manager.cookies
556
+ end
557
+ end
558
+
559
+ # Sets callback proc when HTTP redirect status is returned for get_content
560
+ # and post_content. default_redirect_uri_callback is used by default.
561
+ #
562
+ # If you need strict implementation which does not allow relative URI
563
+ # redirection, set strict_redirect_uri_callback instead.
564
+ #
565
+ # clnt.redirect_uri_callback = clnt.method(:strict_redirect_uri_callback)
566
+ #
567
+ def redirect_uri_callback=(redirect_uri_callback)
568
+ @redirect_uri_callback = redirect_uri_callback
569
+ end
570
+
571
+ # Retrieves a web resource.
572
+ #
573
+ # uri:: a String or an URI object which represents an URL of web resource.
574
+ # query:: a Hash or an Array of query part of URL.
575
+ # e.g. { "a" => "b" } => 'http://host/part?a=b'.
576
+ # Give an array to pass multiple value like
577
+ # [["a", "b"], ["a", "c"]] => 'http://host/part?a=b&a=c'.
578
+ # header:: a Hash or an Array of extra headers. e.g.
579
+ # { 'Accept' => 'text/html' } or
580
+ # [['Accept', 'image/jpeg'], ['Accept', 'image/png']].
581
+ # &block:: Give a block to get chunked message-body of response like
582
+ # get_content(uri) { |chunked_body| ... }.
583
+ # Size of each chunk may not be the same.
584
+ #
585
+ # get_content follows HTTP redirect status (see HTTP::Status.redirect?)
586
+ # internally and try to retrieve content from redirected URL. See
587
+ # redirect_uri_callback= how HTTP redirection is handled.
588
+ #
589
+ # If you need to get full HTTP response including HTTP status and headers,
590
+ # use get method. get returns HTTP::Message as a response and you need to
591
+ # follow HTTP redirect by yourself if you need.
592
+ def get_content(uri, *args, &block)
593
+ query, header = keyword_argument(args, :query, :header)
594
+ success_content(follow_redirect(:get, uri, query, nil, header || {}, &block))
595
+ end
596
+
597
+ # Posts a content.
598
+ #
599
+ # uri:: a String or an URI object which represents an URL of web resource.
600
+ # body:: a Hash or an Array of body part. e.g.
601
+ # { "a" => "b" } => 'a=b'
602
+ # Give an array to pass multiple value like
603
+ # [["a", "b"], ["a", "c"]] => 'a=b&a=c'
604
+ # When you pass a File as a value, it will be posted as a
605
+ # multipart/form-data. e.g.
606
+ # { 'upload' => file }
607
+ # You can also send custom multipart by passing an array of hashes.
608
+ # Each part must have a :content attribute which can be a file, all
609
+ # other keys will become headers.
610
+ # [{ 'Content-Type' => 'text/plain', :content => "some text" },
611
+ # { 'Content-Type' => 'video/mp4', :content => File.new('video.mp4') }]
612
+ # => <Two parts with custom Content-Type header>
613
+ # header:: a Hash or an Array of extra headers. e.g.
614
+ # { 'Accept' => 'text/html' }
615
+ # or
616
+ # [['Accept', 'image/jpeg'], ['Accept', 'image/png']].
617
+ # &block:: Give a block to get chunked message-body of response like
618
+ # post_content(uri) { |chunked_body| ... }.
619
+ # Size of each chunk may not be the same.
620
+ #
621
+ # post_content follows HTTP redirect status (see HTTP::Status.redirect?)
622
+ # internally and try to post the content to redirected URL. See
623
+ # redirect_uri_callback= how HTTP redirection is handled.
624
+ # Bear in mind that you should not depend on post_content because it sends
625
+ # the same POST method to the new location which is prohibited in HTTP spec.
626
+ #
627
+ # If you need to get full HTTP response including HTTP status and headers,
628
+ # use post method.
629
+ def post_content(uri, *args, &block)
630
+ body, header = keyword_argument(args, :body, :header)
631
+ success_content(follow_redirect(:post, uri, nil, body, header || {}, &block))
632
+ end
633
+
634
+ # A method for redirect uri callback. How to use:
635
+ # clnt.redirect_uri_callback = clnt.method(:strict_redirect_uri_callback)
636
+ # This callback does not allow relative redirect such as
637
+ # Location: ../foo/
638
+ # in HTTP header. (raises BadResponseError instead)
639
+ def strict_redirect_uri_callback(uri, res)
640
+ newuri = urify(res.header['location'][0])
641
+ if https?(uri) && !https?(newuri)
642
+ raise BadResponseError.new("redirecting to non-https resource")
643
+ end
644
+ if !http?(newuri) && !https?(newuri)
645
+ raise BadResponseError.new("unexpected location: #{newuri}", res)
646
+ end
647
+ puts "redirect to: #{newuri}" if $DEBUG
648
+ newuri
649
+ end
650
+
651
+ # A default method for redirect uri callback. This method is used by
652
+ # HTTPClient instance by default.
653
+ # This callback allows relative redirect such as
654
+ # Location: ../foo/
655
+ # in HTTP header.
656
+ def default_redirect_uri_callback(uri, res)
657
+ newuri = urify(res.header['location'][0])
658
+ if !http?(newuri) && !https?(newuri)
659
+ newuri = uri + newuri
660
+ warn("could be a relative URI in location header which is not recommended")
661
+ warn("'The field value consists of a single absolute URI' in HTTP spec")
662
+ end
663
+ if https?(uri) && !https?(newuri)
664
+ raise BadResponseError.new("redirecting to non-https resource")
665
+ end
666
+ puts "redirect to: #{newuri}" if $DEBUG
667
+ newuri
668
+ end
669
+
670
+ # Sends HEAD request to the specified URL. See request for arguments.
671
+ def head(uri, *args)
672
+ request(:head, uri, argument_to_hash(args, :query, :header, :follow_redirect))
673
+ end
674
+
675
+ # Sends GET request to the specified URL. See request for arguments.
676
+ def get(uri, *args, &block)
677
+ request(:get, uri, argument_to_hash(args, :query, :header, :follow_redirect), &block)
678
+ end
679
+
680
+ # Sends POST request to the specified URL. See request for arguments.
681
+ # You should not depend on :follow_redirect => true for POST method. It
682
+ # sends the same POST method to the new location which is prohibited in HTTP spec.
683
+ def post(uri, *args, &block)
684
+ request(:post, uri, argument_to_hash(args, :body, :header, :follow_redirect), &block)
685
+ end
686
+
687
+ # Sends PUT request to the specified URL. See request for arguments.
688
+ def put(uri, *args, &block)
689
+ request(:put, uri, argument_to_hash(args, :body, :header), &block)
690
+ end
691
+
692
+ # Sends DELETE request to the specified URL. See request for arguments.
693
+ def delete(uri, *args, &block)
694
+ request(:delete, uri, argument_to_hash(args, :body, :header), &block)
695
+ end
696
+
697
+ # Sends OPTIONS request to the specified URL. See request for arguments.
698
+ def options(uri, *args, &block)
699
+ request(:options, uri, argument_to_hash(args, :header), &block)
700
+ end
701
+
702
+ # Sends PROPFIND request to the specified URL. See request for arguments.
703
+ def propfind(uri, *args, &block)
704
+ request(:propfind, uri, argument_to_hash(args, :header), &block)
705
+ end
706
+
707
+ # Sends PROPPATCH request to the specified URL. See request for arguments.
708
+ def proppatch(uri, *args, &block)
709
+ request(:proppatch, uri, argument_to_hash(args, :body, :header), &block)
710
+ end
711
+
712
+ # Sends TRACE request to the specified URL. See request for arguments.
713
+ def trace(uri, *args, &block)
714
+ request('TRACE', uri, argument_to_hash(args, :query, :header), &block)
715
+ end
716
+
717
+ # Sends a request to the specified URL.
718
+ #
719
+ # method:: HTTP method to be sent. method.to_s.upcase is used.
720
+ # uri:: a String or an URI object which represents an URL of web resource.
721
+ # query:: a Hash or an Array of query part of URL.
722
+ # e.g. { "a" => "b" } => 'http://host/part?a=b'
723
+ # Give an array to pass multiple value like
724
+ # [["a", "b"], ["a", "c"]] => 'http://host/part?a=b&a=c'
725
+ # body:: a Hash or an Array of body part. e.g.
726
+ # { "a" => "b" }
727
+ # => 'a=b'
728
+ # Give an array to pass multiple value like
729
+ # [["a", "b"], ["a", "c"]]
730
+ # => 'a=b&a=c'.
731
+ # When the given method is 'POST' and the given body contains a file
732
+ # as a value, it will be posted as a multipart/form-data. e.g.
733
+ # { 'upload' => file }
734
+ # You can also send custom multipart by passing an array of hashes.
735
+ # Each part must have a :content attribute which can be a file, all
736
+ # other keys will become headers.
737
+ # [{ 'Content-Type' => 'text/plain', :content => "some text" },
738
+ # { 'Content-Type' => 'video/mp4', :content => File.new('video.mp4') }]
739
+ # => <Two parts with custom Content-Type header>
740
+ # See HTTP::Message.file? for actual condition of 'a file'.
741
+ # header:: a Hash or an Array of extra headers. e.g.
742
+ # { 'Accept' => 'text/html' } or
743
+ # [['Accept', 'image/jpeg'], ['Accept', 'image/png']].
744
+ # &block:: Give a block to get chunked message-body of response like
745
+ # get(uri) { |chunked_body| ... }.
746
+ # Size of each chunk may not be the same.
747
+ #
748
+ # You can also pass a String as a body. HTTPClient just sends a String as
749
+ # a HTTP request message body.
750
+ #
751
+ # When you pass an IO as a body, HTTPClient sends it as a HTTP request with
752
+ # chunked encoding (Transfer-Encoding: chunked in HTTP header) if IO does not
753
+ # respond to :read. Bear in mind that some server application does not support
754
+ # chunked request. At least cgi.rb does not support it.
755
+ def request(method, uri, *args, &block)
756
+ query, body, header, follow_redirect = keyword_argument(args, :query, :body, :header, :follow_redirect)
757
+ if [:post, :put].include?(method)
758
+ body ||= ''
759
+ end
760
+ if method == :propfind
761
+ header ||= PROPFIND_DEFAULT_EXTHEADER
762
+ else
763
+ header ||= {}
764
+ end
765
+ uri = urify(uri)
766
+ if block
767
+ filtered_block = proc { |res, str|
768
+ block.call(str)
769
+ }
770
+ end
771
+ if follow_redirect
772
+ follow_redirect(method, uri, query, body, header, &block)
773
+ else
774
+ do_request(method, uri, query, body, header, &filtered_block)
775
+ end
776
+ end
777
+
778
+ # Sends HEAD request in async style. See request_async for arguments.
779
+ # It immediately returns a HTTPClient::Connection instance as a result.
780
+ def head_async(uri, *args)
781
+ query, header = keyword_argument(args, :query, :header)
782
+ request_async(:head, uri, query, nil, header || {})
783
+ end
784
+
785
+ # Sends GET request in async style. See request_async for arguments.
786
+ # It immediately returns a HTTPClient::Connection instance as a result.
787
+ def get_async(uri, *args)
788
+ query, header = keyword_argument(args, :query, :header)
789
+ request_async(:get, uri, query, nil, header || {})
790
+ end
791
+
792
+ # Sends POST request in async style. See request_async for arguments.
793
+ # It immediately returns a HTTPClient::Connection instance as a result.
794
+ def post_async(uri, *args)
795
+ body, header = keyword_argument(args, :body, :header)
796
+ request_async(:post, uri, nil, body || '', header || {})
797
+ end
798
+
799
+ # Sends PUT request in async style. See request_async for arguments.
800
+ # It immediately returns a HTTPClient::Connection instance as a result.
801
+ def put_async(uri, *args)
802
+ body, header = keyword_argument(args, :body, :header)
803
+ request_async(:put, uri, nil, body || '', header || {})
804
+ end
805
+
806
+ # Sends DELETE request in async style. See request_async for arguments.
807
+ # It immediately returns a HTTPClient::Connection instance as a result.
808
+ def delete_async(uri, *args)
809
+ header = keyword_argument(args, :header)
810
+ request_async(:delete, uri, nil, nil, header || {})
811
+ end
812
+
813
+ # Sends OPTIONS request in async style. See request_async for arguments.
814
+ # It immediately returns a HTTPClient::Connection instance as a result.
815
+ def options_async(uri, *args)
816
+ header = keyword_argument(args, :header)
817
+ request_async(:options, uri, nil, nil, header || {})
818
+ end
819
+
820
+ # Sends PROPFIND request in async style. See request_async for arguments.
821
+ # It immediately returns a HTTPClient::Connection instance as a result.
822
+ def propfind_async(uri, *args)
823
+ header = keyword_argument(args, :header)
824
+ request_async(:propfind, uri, nil, nil, header || PROPFIND_DEFAULT_EXTHEADER)
825
+ end
826
+
827
+ # Sends PROPPATCH request in async style. See request_async for arguments.
828
+ # It immediately returns a HTTPClient::Connection instance as a result.
829
+ def proppatch_async(uri, *args)
830
+ body, header = keyword_argument(args, :body, :header)
831
+ request_async(:proppatch, uri, nil, body, header || {})
832
+ end
833
+
834
+ # Sends TRACE request in async style. See request_async for arguments.
835
+ # It immediately returns a HTTPClient::Connection instance as a result.
836
+ def trace_async(uri, *args)
837
+ query, body, header = keyword_argument(args, :query, :body, :header)
838
+ request_async(:trace, uri, query, body, header || {})
839
+ end
840
+
841
+ # Sends a request in async style. request method creates new Thread for
842
+ # HTTP connection and returns a HTTPClient::Connection instance immediately.
843
+ #
844
+ # Arguments definition is the same as request.
845
+ def request_async(method, uri, query = nil, body = nil, header = {})
846
+ uri = urify(uri)
847
+ do_request_async(method, uri, query, body, header)
848
+ end
849
+
850
+ # Resets internal session for the given URL. Keep-alive connection for the
851
+ # site (host-port pair) is disconnected if exists.
852
+ def reset(uri)
853
+ uri = urify(uri)
854
+ @session_manager.reset(uri)
855
+ end
856
+
857
+ # Resets all of internal sessions. Keep-alive connections are disconnected.
858
+ def reset_all
859
+ @session_manager.reset_all
860
+ end
861
+
862
+ private
863
+
864
+ class RetryableResponse < StandardError # :nodoc:
865
+ end
866
+
867
+ class KeepAliveDisconnected < StandardError # :nodoc:
868
+ attr_reader :sess
869
+ def initialize(sess = nil)
870
+ @sess = sess
871
+ end
872
+ end
873
+
874
+ def do_request(method, uri, query, body, header, &block)
875
+ conn = Connection.new
876
+ res = nil
877
+ if HTTP::Message.file?(body)
878
+ pos = body.pos rescue nil
879
+ end
880
+ retry_count = @session_manager.protocol_retry_count
881
+ proxy = no_proxy?(uri) ? nil : @proxy
882
+ while retry_count > 0
883
+ body.pos = pos if pos
884
+ req = create_request(method, uri, query, body, header)
885
+ begin
886
+ protect_keep_alive_disconnected do
887
+ do_get_block(req, proxy, conn, &block)
888
+ end
889
+ res = conn.pop
890
+ break
891
+ rescue RetryableResponse
892
+ res = conn.pop
893
+ retry_count -= 1
894
+ end
895
+ end
896
+ res
897
+ end
898
+
899
+ def do_request_async(method, uri, query, body, header)
900
+ conn = Connection.new
901
+ t = Thread.new(conn) { |tconn|
902
+ begin
903
+ if HTTP::Message.file?(body)
904
+ pos = body.pos rescue nil
905
+ end
906
+ retry_count = @session_manager.protocol_retry_count
907
+ proxy = no_proxy?(uri) ? nil : @proxy
908
+ while retry_count > 0
909
+ body.pos = pos if pos
910
+ req = create_request(method, uri, query, body, header)
911
+ begin
912
+ protect_keep_alive_disconnected do
913
+ do_get_stream(req, proxy, tconn)
914
+ end
915
+ break
916
+ rescue RetryableResponse
917
+ retry_count -= 1
918
+ end
919
+ end
920
+ rescue Exception
921
+ conn.push $!
922
+ end
923
+ }
924
+ conn.async_thread = t
925
+ conn
926
+ end
927
+
928
+ def load_environment
929
+ # http_proxy
930
+ if getenv('REQUEST_METHOD')
931
+ # HTTP_PROXY conflicts with the environment variable usage in CGI where
932
+ # HTTP_* is used for HTTP header information. Unlike open-uri, we
933
+ # simply ignore http_proxy in CGI env and use cgi_http_proxy instead.
934
+ self.proxy = getenv('cgi_http_proxy')
935
+ else
936
+ self.proxy = getenv('http_proxy')
937
+ end
938
+ # no_proxy
939
+ self.no_proxy = getenv('no_proxy')
940
+ end
941
+
942
+ def getenv(name)
943
+ ENV[name.downcase] || ENV[name.upcase]
944
+ end
945
+
946
+ def follow_redirect(method, uri, query, body, header, &block)
947
+ uri = urify(uri)
948
+ if block
949
+ filtered_block = proc { |r, str|
950
+ block.call(str) if r.ok?
951
+ }
952
+ end
953
+ if HTTP::Message.file?(body)
954
+ pos = body.pos rescue nil
955
+ end
956
+ retry_number = 0
957
+ while retry_number < @follow_redirect_count
958
+ body.pos = pos if pos
959
+ res = do_request(method, uri, query, body, header, &filtered_block)
960
+ if res.redirect?
961
+ method = :get if res.see_other? # See RFC2616 10.3.4
962
+ method = :get if res.found? # All browsers convert POST to GET on 302 redirects for historical reasons
963
+ uri = urify(@redirect_uri_callback.call(uri, res))
964
+ retry_number += 1
965
+ else
966
+ return res
967
+ end
968
+ end
969
+ raise BadResponseError.new("retry count exceeded", res)
970
+ end
971
+
972
+ def success_content(res)
973
+ if res.ok?
974
+ return res.content
975
+ else
976
+ raise BadResponseError.new("unexpected response: #{res.header.inspect}", res)
977
+ end
978
+ end
979
+
980
+ def protect_keep_alive_disconnected
981
+ begin
982
+ yield
983
+ rescue KeepAliveDisconnected => e
984
+ if e.sess
985
+ @session_manager.invalidate(e.sess.dest)
986
+ end
987
+ yield
988
+ end
989
+ end
990
+
991
+ def create_request(method, uri, query, body, header)
992
+ method = method.to_s.upcase
993
+ if header.is_a?(Hash)
994
+ header = header.to_a
995
+ else
996
+ header = header.dup
997
+ end
998
+ boundary = nil
999
+ if body
1000
+ _, content_type = header.find { |key, value|
1001
+ key.downcase == 'content-type'
1002
+ }
1003
+ if content_type
1004
+ if /\Amultipart/ =~ content_type
1005
+ if content_type =~ /boundary=(.+)\z/
1006
+ boundary = $1
1007
+ else
1008
+ boundary = create_boundary
1009
+ content_type = "#{content_type}; boundary=#{boundary}"
1010
+ header = override_header(header, 'Content-Type', content_type)
1011
+ end
1012
+ end
1013
+ else
1014
+ if file_in_form_data?(body)
1015
+ boundary = create_boundary
1016
+ content_type = "multipart/form-data; boundary=#{boundary}"
1017
+ else
1018
+ content_type = 'application/x-www-form-urlencoded'
1019
+ end
1020
+ header << ['Content-Type', content_type]
1021
+ end
1022
+ end
1023
+ req = HTTP::Message.new_request(method, uri, query, body, boundary)
1024
+ header.each do |key, value|
1025
+ req.header.add(key.to_s, value)
1026
+ end
1027
+ if @cookie_manager && cookie = @cookie_manager.find(uri)
1028
+ req.header.add('Cookie', cookie)
1029
+ end
1030
+ req
1031
+ end
1032
+
1033
+ def create_boundary
1034
+ Digest::SHA1.hexdigest(Time.now.to_s)
1035
+ end
1036
+
1037
+ def file_in_form_data?(body)
1038
+ HTTP::Message.multiparam_query?(body) &&
1039
+ body.any? { |k, v| HTTP::Message.file?(v) }
1040
+ end
1041
+
1042
+ def override_header(header, key, value)
1043
+ result = []
1044
+ header.each do |k, v|
1045
+ if k.downcase == key.downcase
1046
+ result << [key, value]
1047
+ else
1048
+ result << [k, v]
1049
+ end
1050
+ end
1051
+ result
1052
+ end
1053
+
1054
+ NO_PROXY_HOSTS = ['localhost']
1055
+
1056
+ def no_proxy?(uri)
1057
+ if !@proxy or NO_PROXY_HOSTS.include?(uri.host)
1058
+ return true
1059
+ end
1060
+ @no_proxy_regexps.each do |regexp, port|
1061
+ if !port || uri.port == port.to_i
1062
+ if regexp =~ uri.host
1063
+ return true
1064
+ end
1065
+ end
1066
+ end
1067
+ false
1068
+ end
1069
+
1070
+ # !! CAUTION !!
1071
+ # Method 'do_get*' runs under MT conditon. Be careful to change.
1072
+ def do_get_block(req, proxy, conn, &block)
1073
+ @request_filter.each do |filter|
1074
+ filter.filter_request(req)
1075
+ end
1076
+ if str = @test_loopback_response.shift
1077
+ dump_dummy_request_response(req.http_body.dump, str) if @debug_dev
1078
+ conn.push(HTTP::Message.new_response(str, req.header))
1079
+ return
1080
+ end
1081
+ content = block ? nil : ''
1082
+ res = HTTP::Message.new_response(content, req.header)
1083
+ @debug_dev << "= Request\n\n" if @debug_dev
1084
+ sess = @session_manager.query(req, proxy)
1085
+ res.peer_cert = sess.ssl_peer_cert
1086
+ @debug_dev << "\n\n= Response\n\n" if @debug_dev
1087
+ do_get_header(req, res, sess)
1088
+ conn.push(res)
1089
+ sess.get_body do |part|
1090
+ set_encoding(part, res.body_encoding)
1091
+ if block
1092
+ block.call(res, part)
1093
+ else
1094
+ content << part
1095
+ end
1096
+ end
1097
+ # there could be a race condition but it's OK to cache unreusable
1098
+ # connection because we do retry for that case.
1099
+ @session_manager.keep(sess) unless sess.closed?
1100
+ commands = @request_filter.collect { |filter|
1101
+ filter.filter_response(req, res)
1102
+ }
1103
+ if commands.find { |command| command == :retry }
1104
+ raise RetryableResponse.new
1105
+ end
1106
+ end
1107
+
1108
+ def do_get_stream(req, proxy, conn)
1109
+ @request_filter.each do |filter|
1110
+ filter.filter_request(req)
1111
+ end
1112
+ if str = @test_loopback_response.shift
1113
+ dump_dummy_request_response(req.http_body.dump, str) if @debug_dev
1114
+ conn.push(HTTP::Message.new_response(StringIO.new(str), req.header))
1115
+ return
1116
+ end
1117
+ piper, pipew = IO.pipe
1118
+ res = HTTP::Message.new_response(piper, req.header)
1119
+ @debug_dev << "= Request\n\n" if @debug_dev
1120
+ sess = @session_manager.query(req, proxy)
1121
+ res.peer_cert = sess.ssl_peer_cert
1122
+ @debug_dev << "\n\n= Response\n\n" if @debug_dev
1123
+ do_get_header(req, res, sess)
1124
+ conn.push(res)
1125
+ sess.get_body do |part|
1126
+ set_encoding(part, res.body_encoding)
1127
+ pipew.write(part)
1128
+ end
1129
+ pipew.close
1130
+ @session_manager.keep(sess) unless sess.closed?
1131
+ _ = @request_filter.collect { |filter|
1132
+ filter.filter_response(req, res)
1133
+ }
1134
+ # ignore commands (not retryable in async mode)
1135
+ end
1136
+
1137
+ def do_get_header(req, res, sess)
1138
+ res.http_version, res.status, res.reason, headers = sess.get_header
1139
+ res.header.set_headers(headers)
1140
+ if @cookie_manager
1141
+ res.header['set-cookie'].each do |cookie|
1142
+ @cookie_manager.parse(cookie, req.header.request_uri)
1143
+ end
1144
+ end
1145
+ end
1146
+
1147
+ def dump_dummy_request_response(req, res)
1148
+ @debug_dev << "= Dummy Request\n\n"
1149
+ @debug_dev << req
1150
+ @debug_dev << "\n\n= Dummy Response\n\n"
1151
+ @debug_dev << res
1152
+ end
1153
+
1154
+ def set_encoding(str, encoding)
1155
+ str.force_encoding(encoding) if encoding
1156
+ end
1157
+ end