shin-faraday 0.4.7
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.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +84 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/faraday.gemspec +87 -0
- data/lib/faraday.rb +94 -0
- data/lib/faraday/adapter/action_dispatch.rb +33 -0
- data/lib/faraday/adapter/net_http.rb +59 -0
- data/lib/faraday/adapter/patron.rb +33 -0
- data/lib/faraday/adapter/test.rb +117 -0
- data/lib/faraday/adapter/typhoeus.rb +65 -0
- data/lib/faraday/builder.rb +82 -0
- data/lib/faraday/connection.rb +263 -0
- data/lib/faraday/error.rb +8 -0
- data/lib/faraday/middleware.rb +54 -0
- data/lib/faraday/request.rb +87 -0
- data/lib/faraday/request/active_support_json.rb +21 -0
- data/lib/faraday/request/yajl.rb +18 -0
- data/lib/faraday/response.rb +56 -0
- data/lib/faraday/response/active_support_json.rb +31 -0
- data/lib/faraday/response/nokogiri.rb +21 -0
- data/lib/faraday/response/yajl.rb +26 -0
- data/test/adapters/live_test.rb +141 -0
- data/test/adapters/test_middleware_test.rb +26 -0
- data/test/adapters/typhoeus_test.rb +26 -0
- data/test/connection_app_test.rb +60 -0
- data/test/connection_test.rb +284 -0
- data/test/env_test.rb +56 -0
- data/test/helper.rb +32 -0
- data/test/live_server.rb +34 -0
- data/test/request_middleware_test.rb +24 -0
- data/test/response_middleware_test.rb +44 -0
- metadata +142 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module Faraday
|
2
|
+
module Adapter
|
3
|
+
class Patron < Middleware
|
4
|
+
begin
|
5
|
+
require 'patron'
|
6
|
+
rescue LoadError, NameError => e
|
7
|
+
self.load_error = e
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
process_body_for_request(env)
|
12
|
+
|
13
|
+
sess = ::Patron::Session.new
|
14
|
+
args = [env[:method], env[:url].to_s, env[:request_headers]]
|
15
|
+
if Faraday::Connection::METHODS_WITH_BODIES.include?(env[:method])
|
16
|
+
args.insert(2, env[:body].to_s)
|
17
|
+
end
|
18
|
+
resp = sess.send *args
|
19
|
+
|
20
|
+
env.update \
|
21
|
+
:status => resp.status,
|
22
|
+
:response_headers => resp.headers.
|
23
|
+
inject({}) { |memo, (k, v)| memo.update(k.downcase => v) },
|
24
|
+
:body => resp.body
|
25
|
+
env[:response].finish(env)
|
26
|
+
|
27
|
+
@app.call env
|
28
|
+
rescue Errno::ECONNREFUSED
|
29
|
+
raise Error::ConnectionFailed, "connection refused"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Faraday
|
2
|
+
module Adapter
|
3
|
+
# test = Faraday::Connection.new do
|
4
|
+
# use Faraday::Adapter::Test do |stub|
|
5
|
+
# stub.get '/nigiri/sake.json' do
|
6
|
+
# [200, {}, 'hi world']
|
7
|
+
# end
|
8
|
+
# end
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# resp = test.get '/nigiri/sake.json'
|
12
|
+
# resp.body # => 'hi world'
|
13
|
+
#
|
14
|
+
class Test < Middleware
|
15
|
+
attr_accessor :stubs
|
16
|
+
|
17
|
+
def self.loaded?() false end
|
18
|
+
|
19
|
+
class Stubs
|
20
|
+
def initialize
|
21
|
+
# {:get => [Stub, Stub]}
|
22
|
+
@stack = {}
|
23
|
+
yield self if block_given?
|
24
|
+
end
|
25
|
+
|
26
|
+
def empty?
|
27
|
+
@stack.empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
def match(request_method, path, body)
|
31
|
+
return false if !@stack.key?(request_method)
|
32
|
+
stub = @stack[request_method].detect { |stub| stub.matches?(path, body) }
|
33
|
+
@stack[request_method].delete stub
|
34
|
+
end
|
35
|
+
|
36
|
+
def get(path, &block)
|
37
|
+
new_stub(:get, path, &block)
|
38
|
+
end
|
39
|
+
|
40
|
+
def head(path, &block)
|
41
|
+
new_stub(:head, path, &block)
|
42
|
+
end
|
43
|
+
|
44
|
+
def post(path, body=nil, &block)
|
45
|
+
new_stub(:post, path, body, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def put(path, body=nil, &block)
|
49
|
+
new_stub(:put, path, body, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def delete(path, &block)
|
53
|
+
new_stub(:delete, path, &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
def new_stub(request_method, path, body=nil, &block)
|
57
|
+
(@stack[request_method] ||= []) << Stub.new(path, body, block)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Raises an error if any of the stubbed calls have not been made.
|
61
|
+
def verify_stubbed_calls
|
62
|
+
failed_stubs = []
|
63
|
+
@stack.each do |method, stubs|
|
64
|
+
unless stubs.size == 0
|
65
|
+
failed_stubs.concat(stubs.map {|stub|
|
66
|
+
"Expected #{method} #{stub}."
|
67
|
+
})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
raise failed_stubs.join(" ") unless failed_stubs.size == 0
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Stub < Struct.new(:path, :body, :block)
|
75
|
+
def matches?(request_path, request_body)
|
76
|
+
request_path == path && (body.to_s.size.zero? || request_body == body)
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_s
|
80
|
+
"#{path} #{body}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def initialize(app, stubs=nil, &block)
|
85
|
+
super(app)
|
86
|
+
@stubs = stubs || Stubs.new
|
87
|
+
configure(&block) if block
|
88
|
+
end
|
89
|
+
|
90
|
+
def configure
|
91
|
+
yield stubs
|
92
|
+
end
|
93
|
+
|
94
|
+
def request_uri(url)
|
95
|
+
(url.path != "" ? url.path : "/") +
|
96
|
+
(url.query ? "?#{sort_query_params(url.query)}" : "")
|
97
|
+
end
|
98
|
+
|
99
|
+
def sort_query_params(query)
|
100
|
+
query.split('&').sort.join('&')
|
101
|
+
end
|
102
|
+
|
103
|
+
def call(env)
|
104
|
+
if stub = stubs.match(env[:method], request_uri(env[:url]), env[:body])
|
105
|
+
status, headers, body = stub.block.call(env)
|
106
|
+
env.update \
|
107
|
+
:status => status,
|
108
|
+
:response_headers => headers,
|
109
|
+
:body => body
|
110
|
+
else
|
111
|
+
raise "no stubbed request for #{env[:method]} #{request_uri(env[:url])} #{env[:body]}"
|
112
|
+
end
|
113
|
+
@app.call(env)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Faraday
|
2
|
+
module Adapter
|
3
|
+
class Typhoeus < Middleware
|
4
|
+
self.supports_parallel_requests = true
|
5
|
+
|
6
|
+
def self.setup_parallel_manager(options = {})
|
7
|
+
options.empty? ? ::Typhoeus::Hydra.hydra : ::Typhoeus::Hydra.new(options)
|
8
|
+
end
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'typhoeus'
|
12
|
+
rescue LoadError, NameError => e
|
13
|
+
self.load_error = e
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
process_body_for_request(env)
|
18
|
+
|
19
|
+
hydra = env[:parallel_manager] || self.class.setup_parallel_manager
|
20
|
+
req = ::Typhoeus::Request.new env[:url].to_s,
|
21
|
+
:method => env[:method],
|
22
|
+
:body => env[:body],
|
23
|
+
:headers => env[:request_headers],
|
24
|
+
:disable_ssl_peer_verification => (env[:ssl][:verify] == false)
|
25
|
+
|
26
|
+
env_req = env[:request]
|
27
|
+
req.timeout = req.connect_timeout = (env_req[:timeout] * 1000) if env_req[:timeout]
|
28
|
+
req.connect_timeout = (env_req[:open_timeout] * 1000) if env_req[:open_timeout]
|
29
|
+
|
30
|
+
req.on_complete do |resp|
|
31
|
+
env.update \
|
32
|
+
:status => resp.code,
|
33
|
+
:response_headers => parse_response_headers(resp.headers),
|
34
|
+
:body => resp.body
|
35
|
+
env[:response].finish(env)
|
36
|
+
end
|
37
|
+
|
38
|
+
hydra.queue req
|
39
|
+
|
40
|
+
if !env[:parallel_manager]
|
41
|
+
hydra.run
|
42
|
+
end
|
43
|
+
|
44
|
+
@app.call env
|
45
|
+
rescue Errno::ECONNREFUSED
|
46
|
+
raise Error::ConnectionFailed, "connection refused"
|
47
|
+
end
|
48
|
+
|
49
|
+
def in_parallel(options = {})
|
50
|
+
@hydra = ::Typhoeus::Hydra.new(options)
|
51
|
+
yield
|
52
|
+
@hydra.run
|
53
|
+
@hydra = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse_response_headers(header_string)
|
57
|
+
return {} unless header_string && !header_string.empty?
|
58
|
+
Hash[*header_string.split(/\r\n/).
|
59
|
+
tap { |a| a.shift }. # drop the HTTP status line
|
60
|
+
map! { |h| h.split(/:\s+/,2) }. # split key and value
|
61
|
+
map! { |(k, v)| [k.downcase, v] }.flatten!]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Faraday
|
2
|
+
# Possibly going to extend this a bit.
|
3
|
+
#
|
4
|
+
# Faraday::Connection.new(:url => 'http://sushi.com') do |b|
|
5
|
+
# b.request :yajl # Faraday::Request::Yajl
|
6
|
+
# b.adapter :logger # Faraday::Adapter::Logger
|
7
|
+
# b.response :yajl # Faraday::Response::Yajl
|
8
|
+
# end
|
9
|
+
class Builder
|
10
|
+
attr_accessor :handlers
|
11
|
+
|
12
|
+
def self.create(&block)
|
13
|
+
Builder.new(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.inner_app
|
17
|
+
lambda do |env|
|
18
|
+
env[:parallel_manager] ? env[:response] : env[:response].finish(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(handlers = [], &block)
|
23
|
+
@handlers = handlers
|
24
|
+
build(&block) if block_given?
|
25
|
+
end
|
26
|
+
|
27
|
+
def build(options = {}, &block)
|
28
|
+
inner = @handlers.shift
|
29
|
+
if !options[:keep]
|
30
|
+
@handlers.clear
|
31
|
+
end
|
32
|
+
block.call(self)
|
33
|
+
run(inner || self.class.inner_app)
|
34
|
+
end
|
35
|
+
|
36
|
+
def [](index)
|
37
|
+
# @handlers are stored in reverse order
|
38
|
+
@handlers[-(index+1)]
|
39
|
+
end
|
40
|
+
|
41
|
+
def run(app)
|
42
|
+
@handlers.unshift app
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_app
|
46
|
+
if @handlers.empty?
|
47
|
+
build { |b| b.adapter Faraday.default_adapter }
|
48
|
+
end
|
49
|
+
|
50
|
+
inner_app = @handlers.first
|
51
|
+
@handlers[1..-1].inject(inner_app) { |app, middleware| middleware.call(app) }
|
52
|
+
end
|
53
|
+
|
54
|
+
def use(klass, *args, &block)
|
55
|
+
run(lambda { |app| klass.new(app, *args, &block) })
|
56
|
+
end
|
57
|
+
|
58
|
+
def request(key, *args, &block)
|
59
|
+
use_symbol(Faraday::Request, key, *args, &block)
|
60
|
+
end
|
61
|
+
|
62
|
+
def response(key, *args, &block)
|
63
|
+
use_symbol(Faraday::Response, key, *args, &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def adapter(key, *args, &block)
|
67
|
+
use_symbol(Faraday::Adapter, key, *args, &block)
|
68
|
+
end
|
69
|
+
|
70
|
+
def use_symbol(mod, key, *args, &block)
|
71
|
+
use(mod.lookup_module(key), *args, &block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def ==(other)
|
75
|
+
other.is_a?(self.class) && @handlers == other.handlers
|
76
|
+
end
|
77
|
+
|
78
|
+
def dup
|
79
|
+
self.class.new(@handlers.dup)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,263 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'set'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
module Faraday
|
6
|
+
class Connection
|
7
|
+
include Addressable, Rack::Utils
|
8
|
+
|
9
|
+
HEADERS = Hash.new do |h, k|
|
10
|
+
if k.respond_to?(:to_str)
|
11
|
+
k
|
12
|
+
else
|
13
|
+
k.to_s.split('_'). # :user_agent => %w(user agent)
|
14
|
+
each { |w| w.capitalize! }. # => %w(User Agent)
|
15
|
+
join('-') # => "User-Agent"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
HEADERS.update \
|
19
|
+
:etag => "ETag"
|
20
|
+
HEADERS.values.each { |v| v.freeze }
|
21
|
+
|
22
|
+
METHODS = Set.new [:get, :post, :put, :delete, :head]
|
23
|
+
METHODS_WITH_BODIES = Set.new [:post, :put]
|
24
|
+
|
25
|
+
attr_accessor :host, :port, :scheme, :params, :headers, :parallel_manager
|
26
|
+
attr_reader :path_prefix, :builder, :options, :ssl
|
27
|
+
|
28
|
+
# :url
|
29
|
+
# :params
|
30
|
+
# :headers
|
31
|
+
# :request
|
32
|
+
# :ssl
|
33
|
+
def initialize(url = nil, options = {}, &block)
|
34
|
+
if url.is_a?(Hash)
|
35
|
+
options = url
|
36
|
+
url = options[:url]
|
37
|
+
end
|
38
|
+
@headers = HeaderHash.new
|
39
|
+
@params = {}
|
40
|
+
@options = options[:request] || {}
|
41
|
+
@ssl = options[:ssl] || {}
|
42
|
+
@parallel_manager = options[:parallel]
|
43
|
+
self.url_prefix = url if url
|
44
|
+
proxy(options[:proxy])
|
45
|
+
merge_params @params, options[:params] if options[:params]
|
46
|
+
merge_headers @headers, options[:headers] if options[:headers]
|
47
|
+
|
48
|
+
if block
|
49
|
+
@builder = Builder.new
|
50
|
+
@builder.build { block.call(self) }
|
51
|
+
else
|
52
|
+
@builder = options[:builder] || Builder.new
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def use(klass, *args, &block)
|
57
|
+
@builder.use(klass, *args, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def request(key, *args, &block)
|
61
|
+
@builder.request(key, *args, &block)
|
62
|
+
end
|
63
|
+
|
64
|
+
def response(key, *args, &block)
|
65
|
+
@builder.response(key, *args, &block)
|
66
|
+
end
|
67
|
+
|
68
|
+
def adapter(key, *args, &block)
|
69
|
+
@builder.adapter(key, *args, &block)
|
70
|
+
end
|
71
|
+
|
72
|
+
def build(options = {}, &block)
|
73
|
+
@builder.build(options, &block)
|
74
|
+
end
|
75
|
+
|
76
|
+
def get(url = nil, headers = nil, &block)
|
77
|
+
run_request(:get, url, nil, headers, &block)
|
78
|
+
end
|
79
|
+
|
80
|
+
def post(url = nil, body = nil, headers = nil, &block)
|
81
|
+
run_request(:post, url, body, headers, &block)
|
82
|
+
end
|
83
|
+
|
84
|
+
def put(url = nil, body = nil, headers = nil, &block)
|
85
|
+
run_request(:put, url, body, headers, &block)
|
86
|
+
end
|
87
|
+
|
88
|
+
def head(url = nil, headers = nil, &block)
|
89
|
+
run_request(:head, url, nil, headers, &block)
|
90
|
+
end
|
91
|
+
|
92
|
+
def delete(url = nil, headers = nil, &block)
|
93
|
+
run_request(:delete, url, nil, headers, &block)
|
94
|
+
end
|
95
|
+
|
96
|
+
def basic_auth(login, pass)
|
97
|
+
@headers['authorization'] = "Basic #{Base64.encode64("#{login}:#{pass}").strip}"
|
98
|
+
end
|
99
|
+
|
100
|
+
def token_auth(token, options = {})
|
101
|
+
values = ["token=#{token.to_s.inspect}"]
|
102
|
+
options.each do |key, value|
|
103
|
+
values << "#{key}=#{value.to_s.inspect}"
|
104
|
+
end
|
105
|
+
# 21 = "Authorization: Token ".size
|
106
|
+
comma = ",\n#{' ' * 21}"
|
107
|
+
@headers['authorization'] = "Token #{values * comma}"
|
108
|
+
end
|
109
|
+
|
110
|
+
def in_parallel?
|
111
|
+
!!@parallel_manager
|
112
|
+
end
|
113
|
+
|
114
|
+
def in_parallel(manager)
|
115
|
+
@parallel_manager = manager
|
116
|
+
yield
|
117
|
+
@parallel_manager && @parallel_manager.run
|
118
|
+
ensure
|
119
|
+
@parallel_manager = nil
|
120
|
+
end
|
121
|
+
|
122
|
+
def proxy(arg = nil)
|
123
|
+
return @proxy if arg.nil?
|
124
|
+
|
125
|
+
@proxy =
|
126
|
+
case arg
|
127
|
+
when String then {:uri => proxy_arg_to_uri(arg)}
|
128
|
+
when URI then {:uri => arg}
|
129
|
+
when Hash then arg
|
130
|
+
if arg[:uri] = proxy_arg_to_uri(arg[:uri])
|
131
|
+
arg
|
132
|
+
else
|
133
|
+
raise ArgumentError, "no :uri option."
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Parses the giving url with Addressable::URI and stores the individual
|
139
|
+
# components in this connection. These components serve as defaults for
|
140
|
+
# requests made by this connection.
|
141
|
+
#
|
142
|
+
# conn = Faraday::Connection.new { ... }
|
143
|
+
# conn.url_prefix = "https://sushi.com/api"
|
144
|
+
# conn.scheme # => https
|
145
|
+
# conn.path_prefix # => "/api"
|
146
|
+
#
|
147
|
+
# conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
|
148
|
+
#
|
149
|
+
def url_prefix=(url)
|
150
|
+
uri = URI.parse(url)
|
151
|
+
self.scheme = uri.scheme
|
152
|
+
self.host = uri.host
|
153
|
+
self.port = uri.port
|
154
|
+
self.path_prefix = uri.path
|
155
|
+
if uri.query && !uri.query.empty?
|
156
|
+
merge_params @params, parse_query(uri.query)
|
157
|
+
end
|
158
|
+
if uri.user && uri.password
|
159
|
+
basic_auth(uri.user, uri.password)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Ensures that the path prefix always has a leading / and no trailing /
|
164
|
+
def path_prefix=(value)
|
165
|
+
if value
|
166
|
+
value.chomp! "/"
|
167
|
+
value.replace "/#{value}" if value !~ /^\//
|
168
|
+
end
|
169
|
+
@path_prefix = value
|
170
|
+
end
|
171
|
+
|
172
|
+
# return the assembled Rack application for this instance.
|
173
|
+
def to_app
|
174
|
+
@builder.to_app
|
175
|
+
end
|
176
|
+
|
177
|
+
def run_request(method, url, body, headers)
|
178
|
+
if !METHODS.include?(method)
|
179
|
+
raise ArgumentError, "unknown http method: #{method}"
|
180
|
+
end
|
181
|
+
|
182
|
+
Request.run(self, method) do |req|
|
183
|
+
req.url(url) if url
|
184
|
+
req.headers.update(headers) if headers
|
185
|
+
req.body = body if body
|
186
|
+
yield req if block_given?
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Takes a relative url for a request and combines it with the defaults
|
191
|
+
# set on the connection instance.
|
192
|
+
#
|
193
|
+
# conn = Faraday::Connection.new { ... }
|
194
|
+
# conn.url_prefix = "https://sushi.com/api?token=abc"
|
195
|
+
# conn.scheme # => https
|
196
|
+
# conn.path_prefix # => "/api"
|
197
|
+
#
|
198
|
+
# conn.build_url("nigiri?page=2") # => https://sushi.com/api/nigiri?token=abc&page=2
|
199
|
+
# conn.build_url("nigiri", :page => 2) # => https://sushi.com/api/nigiri?token=abc&page=2
|
200
|
+
#
|
201
|
+
def build_url(url, params = nil)
|
202
|
+
uri = URI.parse(url.to_s)
|
203
|
+
if @path_prefix && uri.path !~ /^\//
|
204
|
+
uri.path = "#{@path_prefix.size > 1 ? @path_prefix : nil}/#{uri.path}"
|
205
|
+
end
|
206
|
+
uri.host ||= @host
|
207
|
+
uri.port ||= @port
|
208
|
+
uri.scheme ||= @scheme
|
209
|
+
replace_query(uri, params)
|
210
|
+
uri
|
211
|
+
end
|
212
|
+
|
213
|
+
def dup
|
214
|
+
self.class.new(build_url(''), :headers => headers.dup, :params => params.dup, :builder => builder.dup)
|
215
|
+
end
|
216
|
+
|
217
|
+
def replace_query(uri, params)
|
218
|
+
url_params = @params.dup
|
219
|
+
if uri.query && !uri.query.empty?
|
220
|
+
merge_params(url_params, parse_query(uri.query))
|
221
|
+
end
|
222
|
+
if params && !params.empty?
|
223
|
+
merge_params(url_params, params)
|
224
|
+
end
|
225
|
+
uri.query = url_params.empty? ? nil : build_query(url_params)
|
226
|
+
uri
|
227
|
+
end
|
228
|
+
|
229
|
+
# turns param keys into strings
|
230
|
+
def merge_params(existing_params, new_params)
|
231
|
+
new_params.each do |key, value|
|
232
|
+
existing_params[key.to_s] = value
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# turns headers keys and values into strings. Look up symbol keys in the
|
237
|
+
# the HEADERS hash.
|
238
|
+
#
|
239
|
+
# h = merge_headers(HeaderHash.new, :content_type => 'text/plain')
|
240
|
+
# h['Content-Type'] # = 'text/plain'
|
241
|
+
#
|
242
|
+
def merge_headers(existing_headers, new_headers)
|
243
|
+
new_headers.each do |key, value|
|
244
|
+
existing_headers[HEADERS[key]] = value.to_s
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Be sure to URI escape '+' symbols to %2B. Otherwise, they get interpreted
|
249
|
+
# as spaces.
|
250
|
+
def escape(s)
|
251
|
+
s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) do
|
252
|
+
'%' << $1.unpack('H2'*bytesize($1)).join('%').tap { |c| c.upcase! }
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def proxy_arg_to_uri(arg)
|
257
|
+
case arg
|
258
|
+
when String then URI.parse(arg)
|
259
|
+
when URI then arg
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|