glebtv-httpclient 3.0.0

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