httpclient 2.3.0.1 → 2.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +85 -0
- data/bin/httpclient +18 -6
- data/bin/jsonclient +85 -0
- data/lib/http-access2.rb +1 -1
- data/lib/httpclient.rb +262 -88
- data/lib/httpclient/auth.rb +269 -244
- data/lib/httpclient/cacert.pem +3952 -0
- data/lib/httpclient/cacert1024.pem +3866 -0
- data/lib/httpclient/connection.rb +1 -1
- data/lib/httpclient/cookie.rb +161 -514
- data/lib/httpclient/http.rb +57 -21
- data/lib/httpclient/include_client.rb +2 -0
- data/lib/httpclient/jruby_ssl_socket.rb +588 -0
- data/lib/httpclient/session.rb +259 -317
- data/lib/httpclient/ssl_config.rb +141 -188
- data/lib/httpclient/ssl_socket.rb +150 -0
- data/lib/httpclient/timeout.rb +1 -1
- data/lib/httpclient/util.rb +62 -1
- data/lib/httpclient/version.rb +1 -1
- data/lib/httpclient/webagent-cookie.rb +459 -0
- data/lib/jsonclient.rb +63 -0
- data/lib/oauthclient.rb +2 -1
- data/sample/jsonclient.rb +67 -0
- data/sample/oauth_twitter.rb +4 -4
- data/test/{ca-chain.cert → ca-chain.pem} +0 -0
- data/test/client-pass.key +18 -0
- data/test/helper.rb +10 -8
- data/test/jruby_ssl_socket/test_pemutils.rb +32 -0
- data/test/test_auth.rb +175 -4
- data/test/test_cookie.rb +147 -243
- data/test/test_http-access2.rb +17 -16
- data/test/test_httpclient.rb +458 -77
- data/test/test_jsonclient.rb +80 -0
- data/test/test_ssl.rb +341 -17
- data/test/test_webagent-cookie.rb +465 -0
- metadata +57 -55
- data/README.txt +0 -721
- data/lib/httpclient/cacert.p7s +0 -1858
- data/lib/httpclient/cacert_sha1.p7s +0 -1858
- data/sample/oauth_salesforce_10.rb +0 -63
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 45217dcc777d36d71246dd468e40b79caad351d6
|
4
|
+
data.tar.gz: afcf1a175414e0a1dde95eb5823b0fa339655d9c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f5ae105eb3b269d67521a35446e3518b359e7a359c0c8a14500ef9e9ff8c4681c20b103bb4d5a2f96cdd9caa337fc149f39df3754bb9f3185b6914f284adfdc0
|
7
|
+
data.tar.gz: 005d1769b6906e0c107ba63e7a178c4d73aae59198ed3efe866e8722fdadcf4c4142c3724c167b6db5f8a45cb03f517083164c18fdd1aa65074dc4ab362bc9de
|
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
httpclient - HTTP accessing library. [](http://badge.fury.io/rb/httpclient)
|
2
|
+
|
3
|
+
Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
|
4
|
+
|
5
|
+
'httpclient' gives something like the functionality of libwww-perl (LWP) in
|
6
|
+
Ruby. 'httpclient' formerly known as 'http-access2'.
|
7
|
+
|
8
|
+
See [HTTPClient](http://www.rubydoc.info/gems/httpclient/frames) for documentation.
|
9
|
+
|
10
|
+
|
11
|
+
## Features
|
12
|
+
|
13
|
+
* methods like GET/HEAD/POST/* via HTTP/1.1.
|
14
|
+
* HTTPS(SSL), Cookies, proxy, authentication(Digest, NTLM, Basic), etc.
|
15
|
+
* asynchronous HTTP request, streaming HTTP request.
|
16
|
+
* debug mode CLI.
|
17
|
+
* by contrast with net/http in standard distribution;
|
18
|
+
* Cookies support
|
19
|
+
* MT-safe
|
20
|
+
* streaming POST (POST with File/IO)
|
21
|
+
* Digest auth
|
22
|
+
* Negotiate/NTLM auth for WWW-Authenticate (requires net/ntlm module; rubyntlm gem)
|
23
|
+
* NTLM auth for Proxy-Authenticate (requires 'win32/sspi' module; rubysspi gem)
|
24
|
+
* extensible with filter interface
|
25
|
+
* you don't have to care HTTP/1.1 persistent connection
|
26
|
+
(httpclient cares instead of you)
|
27
|
+
* Not supported now
|
28
|
+
* Cache
|
29
|
+
* Rather advanced HTTP/1.1 usage such as Range, deflate, etc.
|
30
|
+
(of course you can set it in header by yourself)
|
31
|
+
|
32
|
+
## httpclient command
|
33
|
+
|
34
|
+
Usage: 1) `httpclient get https://www.google.co.jp/?q=ruby`
|
35
|
+
Usage: 2) `httpclient`
|
36
|
+
|
37
|
+
For 1) it issues a GET request to the given URI and shows the wiredump and
|
38
|
+
the parsed result. For 2) it invokes irb shell with the binding that has a
|
39
|
+
HTTPClient as 'self'. You can call HTTPClient instance methods like;
|
40
|
+
|
41
|
+
```ruby
|
42
|
+
get "https://www.google.co.jp/", :q => :ruby
|
43
|
+
```
|
44
|
+
|
45
|
+
## Author
|
46
|
+
|
47
|
+
* Name:: Hiroshi Nakamura
|
48
|
+
* E-mail:: nahi@ruby-lang.org
|
49
|
+
* Project web site:: http://github.com/nahi/httpclient
|
50
|
+
|
51
|
+
|
52
|
+
## License
|
53
|
+
|
54
|
+
This program is copyrighted free software by NAKAMURA, Hiroshi. You can
|
55
|
+
redistribute it and/or modify it under the same terms of Ruby's license;
|
56
|
+
either the dual license version in 2003, or any later version.
|
57
|
+
|
58
|
+
httpclient/session.rb is based on http-access.rb in http-access/0.0.4. Some
|
59
|
+
part of it is copyrighted by Maebashi-san who made and published
|
60
|
+
http-access/0.0.4. http-access/0.0.4 did not include license notice but when
|
61
|
+
I asked Maebashi-san he agreed that I can redistribute it under the same terms
|
62
|
+
of Ruby. Many thanks to Maebashi-san.
|
63
|
+
|
64
|
+
|
65
|
+
## Install
|
66
|
+
|
67
|
+
You can install httpclient via rubygems: `gem install httpclient`
|
68
|
+
|
69
|
+
|
70
|
+
## Usage
|
71
|
+
|
72
|
+
See [HTTPClient](http://www.rubydoc.info/gems/httpclient/frames) for documentation.
|
73
|
+
You can also check sample/howto.rb how to use APIs.
|
74
|
+
|
75
|
+
## Bug report or Feature request
|
76
|
+
|
77
|
+
Please file a ticket at the project web site.
|
78
|
+
|
79
|
+
1. find a similar ticket from https://github.com/nahi/httpclient/issues
|
80
|
+
2. create a new ticket by clicking 'Create Issue' button.
|
81
|
+
3. you can use github features such as pull-request if you like.
|
82
|
+
|
83
|
+
## Changes
|
84
|
+
|
85
|
+
See [ChangeLog](https://github.com/nahi/httpclient/blob/master/CHANGELOG.md)
|
data/bin/httpclient
CHANGED
@@ -11,13 +11,24 @@
|
|
11
11
|
# > get "https://www.google.co.jp/", :q => :ruby
|
12
12
|
require 'httpclient'
|
13
13
|
|
14
|
-
|
15
|
-
if
|
14
|
+
method = ARGV.shift
|
15
|
+
if method == 'version'
|
16
|
+
puts HTTPClient::VERSION
|
17
|
+
exit
|
18
|
+
end
|
19
|
+
|
20
|
+
url = ARGV.shift
|
21
|
+
if method && url
|
16
22
|
client = HTTPClient.new
|
17
|
-
client.
|
18
|
-
|
19
|
-
|
20
|
-
|
23
|
+
client.strict_response_size_check = true
|
24
|
+
if method == 'download'
|
25
|
+
print client.get_content(url)
|
26
|
+
else
|
27
|
+
client.debug_dev = STDERR
|
28
|
+
$DEBUG = true
|
29
|
+
require 'pp'
|
30
|
+
pp client.send(method, url, *ARGV)
|
31
|
+
end
|
21
32
|
exit
|
22
33
|
end
|
23
34
|
|
@@ -27,6 +38,7 @@ require 'irb/completion'
|
|
27
38
|
class Runner
|
28
39
|
def initialize
|
29
40
|
@httpclient = HTTPClient.new
|
41
|
+
@httpclient.strict_response_size_check = true
|
30
42
|
end
|
31
43
|
|
32
44
|
def method_missing(msg, *a, &b)
|
data/bin/jsonclient
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# jsonclient shell command.
|
4
|
+
#
|
5
|
+
# Usage: 1) % jsonclient post https://www.example.com/ content.json
|
6
|
+
# Usage: 2) % jsonclient
|
7
|
+
#
|
8
|
+
# For 1) it issues a GET request to the given URI and shows the wiredump and
|
9
|
+
# the parsed result. For 2) it invokes irb shell with the binding that has a
|
10
|
+
# JSONClient as 'self'. You can call JSONClient instance methods like;
|
11
|
+
# > post "https://www.example.com/resource", {'hello' => 'world'}
|
12
|
+
require 'jsonclient'
|
13
|
+
|
14
|
+
method = ARGV.shift
|
15
|
+
url = ARGV.shift
|
16
|
+
body = []
|
17
|
+
if ['post', 'put'].include?(method)
|
18
|
+
if ARGV.size == 1 && File.exist?(ARGV[0])
|
19
|
+
body << File.read(ARGV[0])
|
20
|
+
else
|
21
|
+
body << ARGF.read
|
22
|
+
end
|
23
|
+
end
|
24
|
+
if method && url
|
25
|
+
require 'pp'
|
26
|
+
client = JSONClient.new
|
27
|
+
client.debug_dev = STDERR if $DEBUG
|
28
|
+
res = client.send(method, url, *body)
|
29
|
+
STDERR.puts('RESPONSE HEADER: ')
|
30
|
+
PP.pp(res.headers, STDERR)
|
31
|
+
if res.ok?
|
32
|
+
begin
|
33
|
+
puts JSON.pretty_generate(res.content)
|
34
|
+
rescue JSON::GeneratorError
|
35
|
+
puts res.content
|
36
|
+
end
|
37
|
+
exit 0
|
38
|
+
else
|
39
|
+
STDERR.puts res.content
|
40
|
+
exit 1
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
require 'irb'
|
45
|
+
require 'irb/completion'
|
46
|
+
|
47
|
+
class Runner
|
48
|
+
def initialize
|
49
|
+
@httpclient = JSONClient.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(msg, *a, &b)
|
53
|
+
debug, $DEBUG = $DEBUG, true
|
54
|
+
begin
|
55
|
+
@httpclient.send(msg, *a, &b)
|
56
|
+
ensure
|
57
|
+
$DEBUG = debug
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def run
|
62
|
+
IRB.setup(nil)
|
63
|
+
ws = IRB::WorkSpace.new(binding)
|
64
|
+
irb = IRB::Irb.new(ws)
|
65
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
66
|
+
|
67
|
+
trap("SIGINT") do
|
68
|
+
irb.signal_handle
|
69
|
+
end
|
70
|
+
|
71
|
+
begin
|
72
|
+
catch(:IRB_EXIT) do
|
73
|
+
irb.eval_input
|
74
|
+
end
|
75
|
+
ensure
|
76
|
+
IRB.irb_at_exit
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_s
|
81
|
+
'JSONClient'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
Runner.new.run
|
data/lib/http-access2.rb
CHANGED
@@ -39,7 +39,7 @@ module HTTPAccess2
|
|
39
39
|
Site = ::HTTPClient::Site
|
40
40
|
Connection = ::HTTPClient::Connection
|
41
41
|
SessionManager = ::HTTPClient::SessionManager
|
42
|
-
SSLSocketWrap = ::HTTPClient::
|
42
|
+
SSLSocketWrap = ::HTTPClient::SSLSocket
|
43
43
|
DebugSocket = ::HTTPClient::DebugSocket
|
44
44
|
|
45
45
|
class Session < ::HTTPClient::Session
|
data/lib/httpclient.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# HTTPClient - HTTP client library.
|
2
|
-
# Copyright (C) 2000-
|
2
|
+
# Copyright (C) 2000-2015 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
|
3
3
|
#
|
4
4
|
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
|
5
5
|
# redistribute it and/or modify it under the same terms of Ruby's license;
|
@@ -116,7 +116,7 @@ require 'httpclient/cookie'
|
|
116
116
|
# res = clnt.post(uri, body)
|
117
117
|
# end
|
118
118
|
#
|
119
|
-
# 3. Do multipart
|
119
|
+
# 3. Do multipart with custom body.
|
120
120
|
#
|
121
121
|
# File.open('/tmp/post_data') do |file|
|
122
122
|
# body = [{ 'Content-Type' => 'application/atom+xml; charset=UTF-8',
|
@@ -149,6 +149,16 @@ require 'httpclient/cookie'
|
|
149
149
|
# clnt.ssl_config.set_client_cert_file(user_cert_file, user_key_file)
|
150
150
|
# clnt.get(https_url)
|
151
151
|
#
|
152
|
+
# 4. Revocation check. On JRuby you can set following options to let
|
153
|
+
# HTTPClient to perform revocation check with CRL and OCSP:
|
154
|
+
#
|
155
|
+
# -J-Dcom.sun.security.enableCRLDP=true -J-Dcom.sun.net.ssl.checkRevocation=true
|
156
|
+
# ex. jruby -J-Dcom.sun.security.enableCRLDP=true -J-Dcom.sun.net.ssl.checkRevocation=true app.rb
|
157
|
+
# Revoked cert example: https://test-sspev.verisign.com:2443/test-SSPEV-revoked-verisign.html
|
158
|
+
#
|
159
|
+
# On other platform you can download CRL by yourself and set it via
|
160
|
+
# SSLConfig#add_crl.
|
161
|
+
#
|
152
162
|
# === Handling Cookies
|
153
163
|
#
|
154
164
|
# 1. Using volatile Cookies. Nothing to do. HTTPClient handles Cookies.
|
@@ -229,7 +239,7 @@ require 'httpclient/cookie'
|
|
229
239
|
# ruby -rhttpclient -e 'p HTTPClient.head(ARGV.shift).header["last-modified"]' http://dev.ctor.org/
|
230
240
|
#
|
231
241
|
class HTTPClient
|
232
|
-
RUBY_VERSION_STRING = "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})
|
242
|
+
RUBY_VERSION_STRING = "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
|
233
243
|
LIB_NAME = "(#{VERSION}, #{RUBY_VERSION_STRING})"
|
234
244
|
|
235
245
|
include Util
|
@@ -299,7 +309,6 @@ class HTTPClient
|
|
299
309
|
if assignable
|
300
310
|
aname = name + '='
|
301
311
|
define_method(aname) { |rhs|
|
302
|
-
reset_all
|
303
312
|
@session_manager.__send__(aname, rhs)
|
304
313
|
}
|
305
314
|
end
|
@@ -308,7 +317,7 @@ class HTTPClient
|
|
308
317
|
|
309
318
|
# HTTPClient::SSLConfig:: SSL configurator.
|
310
319
|
attr_reader :ssl_config
|
311
|
-
#
|
320
|
+
# HTTPClient::CookieManager:: Cookies configurator.
|
312
321
|
attr_accessor :cookie_manager
|
313
322
|
# An array of response HTTP message body String which is used for loop-back
|
314
323
|
# test. See test/* to see how to use it. If you want to do loop-back test
|
@@ -324,6 +333,10 @@ class HTTPClient
|
|
324
333
|
# How many times get_content and post_content follows HTTP redirect.
|
325
334
|
# 10 by default.
|
326
335
|
attr_accessor :follow_redirect_count
|
336
|
+
# Base url of resources.
|
337
|
+
attr_accessor :base_url
|
338
|
+
# Default request header.
|
339
|
+
attr_accessor :default_header
|
327
340
|
|
328
341
|
# Set HTTP version as a String:: 'HTTP/1.0' or 'HTTP/1.1'
|
329
342
|
attr_proxy(:protocol_version, true)
|
@@ -342,6 +355,8 @@ class HTTPClient
|
|
342
355
|
# if your ruby is older than 2005-09-06, do not set socket_sync = false to
|
343
356
|
# avoid an SSL socket blocking bug in openssl/buffering.rb.
|
344
357
|
attr_proxy(:socket_sync, true)
|
358
|
+
# Enables TCP keepalive; no timing settings exist at present
|
359
|
+
attr_proxy(:tcp_keepalive, true)
|
345
360
|
# User-Agent header in HTTP request.
|
346
361
|
attr_proxy(:agent_name, true)
|
347
362
|
# From header in HTTP request.
|
@@ -351,48 +366,90 @@ class HTTPClient
|
|
351
366
|
attr_proxy(:test_loopback_http_response)
|
352
367
|
# Decompress a compressed (with gzip or deflate) content body transparently. false by default.
|
353
368
|
attr_proxy(:transparent_gzip_decompression, true)
|
369
|
+
# Raise BadResponseError if response size does not match with Content-Length header in response. false by default.
|
370
|
+
# TODO: enable by default
|
371
|
+
attr_proxy(:strict_response_size_check, true)
|
354
372
|
# Local socket address. Set HTTPClient#socket_local.host and HTTPClient#socket_local.port to specify local binding hostname and port of TCP socket.
|
355
373
|
attr_proxy(:socket_local, true)
|
356
374
|
|
357
375
|
# Default header for PROPFIND request.
|
358
376
|
PROPFIND_DEFAULT_EXTHEADER = { 'Depth' => '0' }
|
359
377
|
|
378
|
+
# Default User-Agent header
|
379
|
+
DEFAULT_AGENT_NAME = 'HTTPClient/1.0'
|
380
|
+
|
360
381
|
# Creates a HTTPClient instance which manages sessions, cookies, etc.
|
361
382
|
#
|
362
|
-
# HTTPClient.new takes
|
363
|
-
#
|
364
|
-
#
|
365
|
-
#
|
383
|
+
# HTTPClient.new takes optional arguments as a Hash.
|
384
|
+
# * :proxy - proxy url string
|
385
|
+
# * :agent_name - User-Agent String
|
386
|
+
# * :from - from header String
|
387
|
+
# * :base_url - base URL of resources
|
388
|
+
# * :default_header - header Hash all HTTP requests should have
|
389
|
+
# * :force_basic_auth - flag for sending Authorization header w/o gettin 401 first
|
390
|
+
# User-Agent and From are embedded in HTTP request Header if given.
|
391
|
+
# From header is not set without setting it explicitly.
|
366
392
|
#
|
367
393
|
# proxy = 'http://myproxy:8080'
|
368
394
|
# agent_name = 'MyAgent/0.1'
|
369
395
|
# from = 'from@example.com'
|
370
396
|
# HTTPClient.new(proxy, agent_name, from)
|
371
397
|
#
|
372
|
-
#
|
373
|
-
#
|
398
|
+
# After you set :base_url, all resources you pass to get, post and other
|
399
|
+
# methods are recognized to be prefixed with base_url. Say base_url is
|
400
|
+
# 'https://api.example.com/v1/, get('users') is the same as
|
401
|
+
# get('https://api.example.com/v1/users') internally. You can also pass
|
402
|
+
# full URL from 'http://' even after setting base_url.
|
403
|
+
#
|
404
|
+
# The expected base_url and path behavior is the following. Please take
|
405
|
+
# care of '/' in base_url and path.
|
374
406
|
#
|
375
|
-
#
|
376
|
-
|
377
|
-
|
407
|
+
# The last '/' is important for base_url:
|
408
|
+
# 1. http://foo/bar/baz/ + path -> http://foo/bar/baz/path
|
409
|
+
# 2. http://foo/bar/baz + path -> http://foo/bar/path
|
410
|
+
# Relative path handling:
|
411
|
+
# 3. http://foo/bar/baz/ + ../path -> http://foo/bar/path
|
412
|
+
# 4. http://foo/bar/baz + ../path -> http://foo/path
|
413
|
+
# 5. http://foo/bar/baz/ + ./path -> http://foo/bar/baz/path
|
414
|
+
# 6. http://foo/bar/baz + ./path -> http://foo/bar/path
|
415
|
+
# The leading '/' of path means absolute path:
|
416
|
+
# 7. http://foo/bar/baz/ + /path -> http://foo/path
|
417
|
+
# 8. http://foo/bar/baz + /path -> http://foo/path
|
418
|
+
#
|
419
|
+
# :default_header is for providing default headers Hash that all HTTP
|
420
|
+
# requests should have, such as custom 'Authorization' header in API.
|
421
|
+
# You can override :default_header with :header Hash parameter in HTTP
|
422
|
+
# request methods.
|
423
|
+
#
|
424
|
+
# :force_basic_auth turns on/off the BasicAuth force flag. Generally
|
425
|
+
# HTTP client must send Authorization header after it gets 401 error
|
426
|
+
# from server from security reason. But in some situation (e.g. API
|
427
|
+
# client) you might want to send Authorization from the beginning.
|
428
|
+
def initialize(*args, &block)
|
429
|
+
proxy, agent_name, from, base_url, default_header, force_basic_auth =
|
430
|
+
keyword_argument(args, :proxy, :agent_name, :from, :base_url, :default_header, :force_basic_auth)
|
378
431
|
@proxy = nil # assigned later.
|
379
432
|
@no_proxy = nil
|
380
433
|
@no_proxy_regexps = []
|
434
|
+
@base_url = base_url
|
435
|
+
@default_header = default_header || {}
|
381
436
|
@www_auth = WWWAuth.new
|
382
437
|
@proxy_auth = ProxyAuth.new
|
438
|
+
@www_auth.basic_auth.force_auth = @proxy_auth.basic_auth.force_auth = force_basic_auth
|
383
439
|
@request_filter = [@proxy_auth, @www_auth]
|
384
440
|
@debug_dev = nil
|
385
441
|
@redirect_uri_callback = method(:default_redirect_uri_callback)
|
386
442
|
@test_loopback_response = []
|
387
443
|
@session_manager = SessionManager.new(self)
|
388
|
-
@session_manager.agent_name = agent_name
|
444
|
+
@session_manager.agent_name = agent_name || DEFAULT_AGENT_NAME
|
389
445
|
@session_manager.from = from
|
390
446
|
@session_manager.ssl_config = @ssl_config = SSLConfig.new(self)
|
391
|
-
@cookie_manager =
|
447
|
+
@cookie_manager = CookieManager.new
|
392
448
|
@follow_redirect_count = 10
|
393
449
|
load_environment
|
394
450
|
self.proxy = proxy if proxy
|
395
451
|
keep_webmock_compat
|
452
|
+
instance_eval(&block) if block
|
396
453
|
end
|
397
454
|
|
398
455
|
# webmock 1.6.2 depends on HTTP::Message#body.content to work.
|
@@ -480,7 +537,7 @@ class HTTPClient
|
|
480
537
|
@no_proxy = no_proxy
|
481
538
|
@no_proxy_regexps.clear
|
482
539
|
if @no_proxy
|
483
|
-
@no_proxy.scan(/([
|
540
|
+
@no_proxy.scan(/([^\s:,]+)(?::(\d+))?/) do |host, port|
|
484
541
|
if host[0] == ?.
|
485
542
|
regexp = /#{Regexp.quote(host)}\z/i
|
486
543
|
else
|
@@ -506,14 +563,14 @@ class HTTPClient
|
|
506
563
|
#
|
507
564
|
# Calling this method resets all existing sessions.
|
508
565
|
def set_auth(domain, user, passwd)
|
509
|
-
uri =
|
566
|
+
uri = to_resource_url(domain)
|
510
567
|
@www_auth.set_auth(uri, user, passwd)
|
511
568
|
reset_all
|
512
569
|
end
|
513
570
|
|
514
571
|
# Deprecated. Use set_auth instead.
|
515
572
|
def set_basic_auth(domain, user, passwd)
|
516
|
-
uri =
|
573
|
+
uri = to_resource_url(domain)
|
517
574
|
@www_auth.basic_auth.set(uri, user, passwd)
|
518
575
|
reset_all
|
519
576
|
end
|
@@ -528,6 +585,14 @@ class HTTPClient
|
|
528
585
|
reset_all
|
529
586
|
end
|
530
587
|
|
588
|
+
# Turn on/off the BasicAuth force flag. Generally HTTP client must
|
589
|
+
# send Authorization header after it gets 401 error from server from
|
590
|
+
# security reason. But in some situation (e.g. API client) you might
|
591
|
+
# want to send Authorization from the beginning.
|
592
|
+
def force_basic_auth=(force_basic_auth)
|
593
|
+
@www_auth.basic_auth.force_auth = @proxy_auth.basic_auth.force_auth = force_basic_auth
|
594
|
+
end
|
595
|
+
|
531
596
|
# Sets the filename where non-volatile Cookies be saved by calling
|
532
597
|
# save_cookie_store.
|
533
598
|
# This method tries to load and managing Cookies from the specified file.
|
@@ -541,7 +606,6 @@ class HTTPClient
|
|
541
606
|
|
542
607
|
# Try to save Cookies to the file specified in set_cookie_store. Unexpected
|
543
608
|
# error will be raised if you don't call set_cookie_store first.
|
544
|
-
# (interface mismatch between WebAgent::CookieManager implementation)
|
545
609
|
def save_cookie_store
|
546
610
|
@cookie_manager.save_cookies
|
547
611
|
end
|
@@ -624,8 +688,13 @@ class HTTPClient
|
|
624
688
|
# If you need to get full HTTP response including HTTP status and headers,
|
625
689
|
# use post method.
|
626
690
|
def post_content(uri, *args, &block)
|
627
|
-
|
628
|
-
|
691
|
+
if hashy_argument_has_keys(args, :query, :body)
|
692
|
+
query, body, header = keyword_argument(args, :query, :body, :header)
|
693
|
+
else
|
694
|
+
query = nil
|
695
|
+
body, header = keyword_argument(args, :body, :header)
|
696
|
+
end
|
697
|
+
success_content(follow_redirect(:post, uri, query, body, header || {}, &block))
|
629
698
|
end
|
630
699
|
|
631
700
|
# A method for redirect uri callback. How to use:
|
@@ -653,9 +722,9 @@ class HTTPClient
|
|
653
722
|
def default_redirect_uri_callback(uri, res)
|
654
723
|
newuri = urify(res.header['location'][0])
|
655
724
|
if !http?(newuri) && !https?(newuri)
|
656
|
-
newuri
|
657
|
-
warn("could be a relative URI in location header which is not recommended")
|
725
|
+
warn("#{newuri}: a relative URI in location header which is not recommended")
|
658
726
|
warn("'The field value consists of a single absolute URI' in HTTP spec")
|
727
|
+
newuri = uri + newuri
|
659
728
|
end
|
660
729
|
if https?(uri) && !https?(newuri)
|
661
730
|
raise BadResponseError.new("redirecting to non-https resource")
|
@@ -674,26 +743,47 @@ class HTTPClient
|
|
674
743
|
request(:get, uri, argument_to_hash(args, :query, :header, :follow_redirect), &block)
|
675
744
|
end
|
676
745
|
|
746
|
+
# Sends PATCH request to the specified URL. See request for arguments.
|
747
|
+
def patch(uri, *args, &block)
|
748
|
+
if hashy_argument_has_keys(args, :query, :body)
|
749
|
+
new_args = args[0]
|
750
|
+
else
|
751
|
+
new_args = argument_to_hash(args, :body, :header)
|
752
|
+
end
|
753
|
+
request(:patch, uri, new_args, &block)
|
754
|
+
end
|
755
|
+
|
677
756
|
# Sends POST request to the specified URL. See request for arguments.
|
678
757
|
# You should not depend on :follow_redirect => true for POST method. It
|
679
758
|
# sends the same POST method to the new location which is prohibited in HTTP spec.
|
680
759
|
def post(uri, *args, &block)
|
681
|
-
|
760
|
+
if hashy_argument_has_keys(args, :query, :body)
|
761
|
+
new_args = args[0]
|
762
|
+
else
|
763
|
+
new_args = argument_to_hash(args, :body, :header, :follow_redirect)
|
764
|
+
end
|
765
|
+
request(:post, uri, new_args, &block)
|
682
766
|
end
|
683
767
|
|
684
768
|
# Sends PUT request to the specified URL. See request for arguments.
|
685
769
|
def put(uri, *args, &block)
|
686
|
-
|
770
|
+
if hashy_argument_has_keys(args, :query, :body)
|
771
|
+
new_args = args[0]
|
772
|
+
else
|
773
|
+
new_args = argument_to_hash(args, :body, :header)
|
774
|
+
end
|
775
|
+
request(:put, uri, new_args, &block)
|
687
776
|
end
|
688
777
|
|
689
778
|
# Sends DELETE request to the specified URL. See request for arguments.
|
690
779
|
def delete(uri, *args, &block)
|
691
|
-
request(:delete, uri, argument_to_hash(args, :body, :header), &block)
|
780
|
+
request(:delete, uri, argument_to_hash(args, :body, :header, :query), &block)
|
692
781
|
end
|
693
782
|
|
694
783
|
# Sends OPTIONS request to the specified URL. See request for arguments.
|
695
784
|
def options(uri, *args, &block)
|
696
|
-
|
785
|
+
new_args = argument_to_hash(args, :header, :query, :body)
|
786
|
+
request(:options, uri, new_args, &block)
|
697
787
|
end
|
698
788
|
|
699
789
|
# Sends PROPFIND request to the specified URL. See request for arguments.
|
@@ -747,23 +837,18 @@ class HTTPClient
|
|
747
837
|
#
|
748
838
|
# When you pass an IO as a body, HTTPClient sends it as a HTTP request with
|
749
839
|
# chunked encoding (Transfer-Encoding: chunked in HTTP header) if IO does not
|
750
|
-
# respond to :
|
840
|
+
# respond to :size. Bear in mind that some server application does not support
|
751
841
|
# chunked request. At least cgi.rb does not support it.
|
752
842
|
def request(method, uri, *args, &block)
|
753
843
|
query, body, header, follow_redirect = keyword_argument(args, :query, :body, :header, :follow_redirect)
|
754
|
-
if [:post, :put].include?(method)
|
755
|
-
body ||= ''
|
756
|
-
end
|
757
844
|
if method == :propfind
|
758
845
|
header ||= PROPFIND_DEFAULT_EXTHEADER
|
759
846
|
else
|
760
847
|
header ||= {}
|
761
848
|
end
|
762
|
-
uri =
|
849
|
+
uri = to_resource_url(uri)
|
763
850
|
if block
|
764
|
-
filtered_block =
|
765
|
-
block.call(str)
|
766
|
-
}
|
851
|
+
filtered_block = adapt_block(&block)
|
767
852
|
end
|
768
853
|
if follow_redirect
|
769
854
|
follow_redirect(method, uri, query, body, header, &block)
|
@@ -775,64 +860,76 @@ class HTTPClient
|
|
775
860
|
# Sends HEAD request in async style. See request_async for arguments.
|
776
861
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
777
862
|
def head_async(uri, *args)
|
778
|
-
|
779
|
-
request_async(:head, uri, query, nil, header || {})
|
863
|
+
request_async2(:head, uri, argument_to_hash(args, :query, :header))
|
780
864
|
end
|
781
865
|
|
782
866
|
# Sends GET request in async style. See request_async for arguments.
|
783
867
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
784
868
|
def get_async(uri, *args)
|
785
|
-
|
786
|
-
|
869
|
+
request_async2(:get, uri, argument_to_hash(args, :query, :header))
|
870
|
+
end
|
871
|
+
|
872
|
+
# Sends PATCH request in async style. See request_async2 for arguments.
|
873
|
+
# It immediately returns a HTTPClient::Connection instance as a result.
|
874
|
+
def patch_async(uri, *args)
|
875
|
+
if hashy_argument_has_keys(args, :query, :body)
|
876
|
+
new_args = args[0]
|
877
|
+
else
|
878
|
+
new_args = argument_to_hash(args, :body, :header)
|
879
|
+
end
|
880
|
+
request_async2(:patch, uri, new_args)
|
787
881
|
end
|
788
882
|
|
789
883
|
# Sends POST request in async style. See request_async for arguments.
|
790
884
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
791
885
|
def post_async(uri, *args)
|
792
|
-
|
793
|
-
|
886
|
+
if hashy_argument_has_keys(args, :query, :body)
|
887
|
+
new_args = args[0]
|
888
|
+
else
|
889
|
+
new_args = argument_to_hash(args, :body, :header)
|
890
|
+
end
|
891
|
+
request_async2(:post, uri, new_args)
|
794
892
|
end
|
795
893
|
|
796
|
-
# Sends PUT request in async style. See
|
894
|
+
# Sends PUT request in async style. See request_async2 for arguments.
|
797
895
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
798
896
|
def put_async(uri, *args)
|
799
|
-
|
800
|
-
|
897
|
+
if hashy_argument_has_keys(args, :query, :body)
|
898
|
+
new_args = args[0]
|
899
|
+
else
|
900
|
+
new_args = argument_to_hash(args, :body, :header)
|
901
|
+
end
|
902
|
+
request_async2(:put, uri, new_args)
|
801
903
|
end
|
802
904
|
|
803
|
-
# Sends DELETE request in async style. See
|
905
|
+
# Sends DELETE request in async style. See request_async2 for arguments.
|
804
906
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
805
907
|
def delete_async(uri, *args)
|
806
|
-
|
807
|
-
request_async(:delete, uri, nil, nil, header || {})
|
908
|
+
request_async2(:delete, uri, argument_to_hash(args, :body, :header, :query))
|
808
909
|
end
|
809
910
|
|
810
|
-
# Sends OPTIONS request in async style. See
|
911
|
+
# Sends OPTIONS request in async style. See request_async2 for arguments.
|
811
912
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
812
913
|
def options_async(uri, *args)
|
813
|
-
|
814
|
-
request_async(:options, uri, nil, nil, header || {})
|
914
|
+
request_async2(:options, uri, argument_to_hash(args, :header, :query, :body))
|
815
915
|
end
|
816
916
|
|
817
|
-
# Sends PROPFIND request in async style. See
|
917
|
+
# Sends PROPFIND request in async style. See request_async2 for arguments.
|
818
918
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
819
919
|
def propfind_async(uri, *args)
|
820
|
-
|
821
|
-
request_async(:propfind, uri, nil, nil, header || PROPFIND_DEFAULT_EXTHEADER)
|
920
|
+
request_async2(:propfind, uri, argument_to_hash(args, :body, :header))
|
822
921
|
end
|
823
922
|
|
824
|
-
# Sends PROPPATCH request in async style. See
|
923
|
+
# Sends PROPPATCH request in async style. See request_async2 for arguments.
|
825
924
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
826
925
|
def proppatch_async(uri, *args)
|
827
|
-
|
828
|
-
request_async(:proppatch, uri, nil, body, header || {})
|
926
|
+
request_async2(:proppatch, uri, argument_to_hash(args, :body, :header))
|
829
927
|
end
|
830
928
|
|
831
|
-
# Sends TRACE request in async style. See
|
929
|
+
# Sends TRACE request in async style. See request_async2 for arguments.
|
832
930
|
# It immediately returns a HTTPClient::Connection instance as a result.
|
833
931
|
def trace_async(uri, *args)
|
834
|
-
|
835
|
-
request_async(:trace, uri, query, body, header || {})
|
932
|
+
request_async2(:trace, uri, argument_to_hash(args, :query, :header))
|
836
933
|
end
|
837
934
|
|
838
935
|
# Sends a request in async style. request method creates new Thread for
|
@@ -840,14 +937,29 @@ class HTTPClient
|
|
840
937
|
#
|
841
938
|
# Arguments definition is the same as request.
|
842
939
|
def request_async(method, uri, query = nil, body = nil, header = {})
|
843
|
-
uri =
|
940
|
+
uri = to_resource_url(uri)
|
941
|
+
do_request_async(method, uri, query, body, header)
|
942
|
+
end
|
943
|
+
|
944
|
+
# new method that has same signature as 'request'
|
945
|
+
def request_async2(method, uri, *args)
|
946
|
+
query, body, header = keyword_argument(args, :query, :body, :header)
|
947
|
+
if [:post, :put].include?(method)
|
948
|
+
body ||= ''
|
949
|
+
end
|
950
|
+
if method == :propfind
|
951
|
+
header ||= PROPFIND_DEFAULT_EXTHEADER
|
952
|
+
else
|
953
|
+
header ||= {}
|
954
|
+
end
|
955
|
+
uri = to_resource_url(uri)
|
844
956
|
do_request_async(method, uri, query, body, header)
|
845
957
|
end
|
846
958
|
|
847
959
|
# Resets internal session for the given URL. Keep-alive connection for the
|
848
960
|
# site (host-port pair) is disconnected if exists.
|
849
961
|
def reset(uri)
|
850
|
-
uri =
|
962
|
+
uri = to_resource_url(uri)
|
851
963
|
@session_manager.reset(uri)
|
852
964
|
end
|
853
965
|
|
@@ -859,34 +971,62 @@ class HTTPClient
|
|
859
971
|
private
|
860
972
|
|
861
973
|
class RetryableResponse < StandardError # :nodoc:
|
974
|
+
attr_reader :res
|
975
|
+
|
976
|
+
def initialize(res = nil)
|
977
|
+
@res = res
|
978
|
+
end
|
862
979
|
end
|
863
980
|
|
864
981
|
class KeepAliveDisconnected < StandardError # :nodoc:
|
865
982
|
attr_reader :sess
|
866
|
-
|
983
|
+
attr_reader :cause
|
984
|
+
def initialize(sess = nil, cause = nil)
|
985
|
+
super("#{self.class.name}: #{cause ? cause.message : nil}")
|
867
986
|
@sess = sess
|
987
|
+
@cause = cause
|
868
988
|
end
|
869
989
|
end
|
870
990
|
|
991
|
+
def hashy_argument_has_keys(args, *key)
|
992
|
+
# if the given arg is a single Hash and...
|
993
|
+
args.size == 1 and Hash === args[0] and
|
994
|
+
# it has any one of the key
|
995
|
+
key.all? { |e| args[0].key?(e) }
|
996
|
+
end
|
997
|
+
|
871
998
|
def do_request(method, uri, query, body, header, &block)
|
872
|
-
conn = Connection.new
|
873
999
|
res = nil
|
874
1000
|
if HTTP::Message.file?(body)
|
875
1001
|
pos = body.pos rescue nil
|
876
1002
|
end
|
877
1003
|
retry_count = @session_manager.protocol_retry_count
|
878
1004
|
proxy = no_proxy?(uri) ? nil : @proxy
|
1005
|
+
previous_request = previous_response = nil
|
879
1006
|
while retry_count > 0
|
880
1007
|
body.pos = pos if pos
|
881
1008
|
req = create_request(method, uri, query, body, header)
|
1009
|
+
if previous_request
|
1010
|
+
# to remember IO positions to read
|
1011
|
+
req.http_body.positions = previous_request.http_body.positions
|
1012
|
+
end
|
882
1013
|
begin
|
883
1014
|
protect_keep_alive_disconnected do
|
884
|
-
|
1015
|
+
# TODO: remove Connection.new
|
1016
|
+
# We want to delete Connection usage in do_get_block but Newrelic gem depends on it.
|
1017
|
+
# https://github.com/newrelic/rpm/blob/master/lib/new_relic/agent/instrumentation/httpclient.rb#L34-L36
|
1018
|
+
conn = Connection.new
|
1019
|
+
res = do_get_block(req, proxy, conn, &block)
|
1020
|
+
# Webmock's do_get_block returns ConditionVariable
|
1021
|
+
if !res.respond_to?(:previous)
|
1022
|
+
res = conn.pop
|
1023
|
+
end
|
885
1024
|
end
|
886
|
-
res =
|
1025
|
+
res.previous = previous_response
|
887
1026
|
break
|
888
|
-
rescue RetryableResponse
|
889
|
-
|
1027
|
+
rescue RetryableResponse => e
|
1028
|
+
previous_request = req
|
1029
|
+
previous_response = res = e.res
|
890
1030
|
retry_count -= 1
|
891
1031
|
end
|
892
1032
|
end
|
@@ -914,8 +1054,8 @@ private
|
|
914
1054
|
retry_count -= 1
|
915
1055
|
end
|
916
1056
|
end
|
917
|
-
rescue Exception
|
918
|
-
conn.push
|
1057
|
+
rescue Exception => e
|
1058
|
+
conn.push e
|
919
1059
|
end
|
920
1060
|
}
|
921
1061
|
conn.async_thread = t
|
@@ -940,23 +1080,38 @@ private
|
|
940
1080
|
ENV[name.downcase] || ENV[name.upcase]
|
941
1081
|
end
|
942
1082
|
|
1083
|
+
def adapt_block(&block)
|
1084
|
+
return block if block.arity == 2
|
1085
|
+
proc { |r, str| block.call(str) }
|
1086
|
+
end
|
1087
|
+
|
943
1088
|
def follow_redirect(method, uri, query, body, header, &block)
|
944
|
-
uri =
|
1089
|
+
uri = to_resource_url(uri)
|
945
1090
|
if block
|
1091
|
+
b = adapt_block(&block)
|
946
1092
|
filtered_block = proc { |r, str|
|
947
|
-
|
1093
|
+
b.call(r, str) if r.ok?
|
948
1094
|
}
|
949
1095
|
end
|
950
1096
|
if HTTP::Message.file?(body)
|
951
1097
|
pos = body.pos rescue nil
|
952
1098
|
end
|
953
1099
|
retry_number = 0
|
1100
|
+
previous = nil
|
1101
|
+
request_query = query
|
954
1102
|
while retry_number < @follow_redirect_count
|
955
1103
|
body.pos = pos if pos
|
956
|
-
res = do_request(method, uri,
|
1104
|
+
res = do_request(method, uri, request_query, body, header, &filtered_block)
|
1105
|
+
res.previous = previous
|
957
1106
|
if res.redirect?
|
1107
|
+
if res.header['location'].empty?
|
1108
|
+
raise BadResponseError.new("Missing Location header for redirect", res)
|
1109
|
+
end
|
958
1110
|
method = :get if res.see_other? # See RFC2616 10.3.4
|
959
1111
|
uri = urify(@redirect_uri_callback.call(uri, res))
|
1112
|
+
# To avoid duped query parameter. 'location' must include query part.
|
1113
|
+
request_query = nil
|
1114
|
+
previous = res
|
960
1115
|
retry_number += 1
|
961
1116
|
else
|
962
1117
|
return res
|
@@ -976,25 +1131,28 @@ private
|
|
976
1131
|
def protect_keep_alive_disconnected
|
977
1132
|
begin
|
978
1133
|
yield
|
979
|
-
rescue KeepAliveDisconnected
|
980
|
-
|
981
|
-
|
1134
|
+
rescue KeepAliveDisconnected
|
1135
|
+
# Force to create new connection
|
1136
|
+
Thread.current[:HTTPClient_AcquireNewConnection] = true
|
1137
|
+
begin
|
1138
|
+
yield
|
1139
|
+
ensure
|
1140
|
+
Thread.current[:HTTPClient_AcquireNewConnection] = false
|
982
1141
|
end
|
983
|
-
yield
|
984
1142
|
end
|
985
1143
|
end
|
986
1144
|
|
987
1145
|
def create_request(method, uri, query, body, header)
|
988
1146
|
method = method.to_s.upcase
|
989
1147
|
if header.is_a?(Hash)
|
990
|
-
header = header.to_a
|
1148
|
+
header = @default_header.merge(header).to_a
|
991
1149
|
else
|
992
|
-
header = header.dup
|
1150
|
+
header = @default_header.to_a + header.dup
|
993
1151
|
end
|
994
1152
|
boundary = nil
|
995
1153
|
if body
|
996
1154
|
_, content_type = header.find { |key, value|
|
997
|
-
key.downcase == 'content-type'
|
1155
|
+
key.to_s.downcase == 'content-type'
|
998
1156
|
}
|
999
1157
|
if content_type
|
1000
1158
|
if /\Amultipart/ =~ content_type
|
@@ -1003,7 +1161,7 @@ private
|
|
1003
1161
|
else
|
1004
1162
|
boundary = create_boundary
|
1005
1163
|
content_type = "#{content_type}; boundary=#{boundary}"
|
1006
|
-
header = override_header(header, '
|
1164
|
+
header = override_header(header, 'content-type', content_type)
|
1007
1165
|
end
|
1008
1166
|
end
|
1009
1167
|
else
|
@@ -1020,8 +1178,11 @@ private
|
|
1020
1178
|
header.each do |key, value|
|
1021
1179
|
req.header.add(key.to_s, value)
|
1022
1180
|
end
|
1023
|
-
if @cookie_manager
|
1024
|
-
|
1181
|
+
if @cookie_manager
|
1182
|
+
cookie_value = @cookie_manager.cookie_value(uri)
|
1183
|
+
if cookie_value
|
1184
|
+
req.header.add('Cookie', cookie_value)
|
1185
|
+
end
|
1025
1186
|
end
|
1026
1187
|
req
|
1027
1188
|
end
|
@@ -1038,7 +1199,7 @@ private
|
|
1038
1199
|
def override_header(header, key, value)
|
1039
1200
|
result = []
|
1040
1201
|
header.each do |k, v|
|
1041
|
-
if k.downcase == key
|
1202
|
+
if k.to_s.downcase == key
|
1042
1203
|
result << [key, value]
|
1043
1204
|
else
|
1044
1205
|
result << [k, v]
|
@@ -1071,8 +1232,9 @@ private
|
|
1071
1232
|
end
|
1072
1233
|
if str = @test_loopback_response.shift
|
1073
1234
|
dump_dummy_request_response(req.http_body.dump, str) if @debug_dev
|
1074
|
-
|
1075
|
-
|
1235
|
+
res = HTTP::Message.new_response(str, req.header)
|
1236
|
+
conn.push(res)
|
1237
|
+
return res
|
1076
1238
|
end
|
1077
1239
|
content = block ? nil : ''
|
1078
1240
|
res = HTTP::Message.new_response(content, req.header)
|
@@ -1085,7 +1247,7 @@ private
|
|
1085
1247
|
sess.get_body do |part|
|
1086
1248
|
set_encoding(part, res.body_encoding)
|
1087
1249
|
if block
|
1088
|
-
block.call(res, part)
|
1250
|
+
block.call(res, part.dup)
|
1089
1251
|
else
|
1090
1252
|
content << part
|
1091
1253
|
end
|
@@ -1097,8 +1259,9 @@ private
|
|
1097
1259
|
filter.filter_response(req, res)
|
1098
1260
|
}
|
1099
1261
|
if commands.find { |command| command == :retry }
|
1100
|
-
raise RetryableResponse.new
|
1262
|
+
raise RetryableResponse.new(res)
|
1101
1263
|
end
|
1264
|
+
res
|
1102
1265
|
end
|
1103
1266
|
|
1104
1267
|
def do_get_stream(req, proxy, conn)
|
@@ -1111,6 +1274,7 @@ private
|
|
1111
1274
|
return
|
1112
1275
|
end
|
1113
1276
|
piper, pipew = IO.pipe
|
1277
|
+
pipew.binmode
|
1114
1278
|
res = HTTP::Message.new_response(piper, req.header)
|
1115
1279
|
@debug_dev << "= Request\n\n" if @debug_dev
|
1116
1280
|
sess = @session_manager.query(req, proxy)
|
@@ -1128,6 +1292,7 @@ private
|
|
1128
1292
|
filter.filter_response(req, res)
|
1129
1293
|
}
|
1130
1294
|
# ignore commands (not retryable in async mode)
|
1295
|
+
res
|
1131
1296
|
end
|
1132
1297
|
|
1133
1298
|
def do_get_header(req, res, sess)
|
@@ -1150,4 +1315,13 @@ private
|
|
1150
1315
|
def set_encoding(str, encoding)
|
1151
1316
|
str.force_encoding(encoding) if encoding
|
1152
1317
|
end
|
1318
|
+
|
1319
|
+
def to_resource_url(uri)
|
1320
|
+
u = urify(uri)
|
1321
|
+
if @base_url && u.scheme.nil? && u.host.nil?
|
1322
|
+
urify(@base_url) + uri
|
1323
|
+
else
|
1324
|
+
u
|
1325
|
+
end
|
1326
|
+
end
|
1153
1327
|
end
|