faraday 0.8.11 → 0.9.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. data/.document +6 -0
  2. data/CONTRIBUTING.md +36 -0
  3. data/Gemfile +7 -6
  4. data/LICENSE.md +1 -1
  5. data/README.md +38 -51
  6. data/Rakefile +2 -18
  7. data/faraday.gemspec +34 -0
  8. data/lib/faraday.rb +181 -67
  9. data/lib/faraday/adapter.rb +19 -34
  10. data/lib/faraday/adapter/em_http.rb +24 -10
  11. data/lib/faraday/adapter/em_synchrony.rb +1 -15
  12. data/lib/faraday/adapter/excon.rb +6 -12
  13. data/lib/faraday/adapter/httpclient.rb +92 -0
  14. data/lib/faraday/adapter/net_http.rb +2 -3
  15. data/lib/faraday/adapter/net_http_persistent.rb +3 -15
  16. data/lib/faraday/adapter/patron.rb +13 -21
  17. data/lib/faraday/adapter/rack.rb +0 -2
  18. data/lib/faraday/adapter/test.rb +35 -36
  19. data/lib/faraday/adapter/typhoeus.rb +10 -12
  20. data/lib/faraday/autoload.rb +87 -0
  21. data/lib/faraday/connection.rb +196 -99
  22. data/lib/faraday/error.rb +33 -33
  23. data/lib/faraday/options.rb +215 -0
  24. data/lib/faraday/parameters.rb +193 -0
  25. data/lib/faraday/{builder.rb → rack_builder.rb} +78 -21
  26. data/lib/faraday/request.rb +12 -25
  27. data/lib/faraday/request/authorization.rb +3 -3
  28. data/lib/faraday/request/basic_authentication.rb +1 -1
  29. data/lib/faraday/request/instrumentation.rb +38 -0
  30. data/lib/faraday/request/multipart.rb +10 -9
  31. data/lib/faraday/request/retry.rb +70 -6
  32. data/lib/faraday/request/token_authentication.rb +2 -2
  33. data/lib/faraday/request/url_encoded.rb +7 -6
  34. data/lib/faraday/response.rb +17 -22
  35. data/lib/faraday/response/logger.rb +4 -4
  36. data/lib/faraday/response/raise_error.rb +4 -5
  37. data/lib/faraday/utils.rb +54 -67
  38. data/script/console +7 -0
  39. data/script/release +6 -3
  40. data/script/server +3 -1
  41. data/script/test +7 -33
  42. data/test/adapters/em_http_test.rb +6 -1
  43. data/test/adapters/em_synchrony_test.rb +7 -1
  44. data/test/adapters/excon_test.rb +0 -7
  45. data/test/adapters/httpclient_test.rb +16 -0
  46. data/test/adapters/integration.rb +8 -39
  47. data/test/adapters/logger_test.rb +1 -1
  48. data/test/adapters/net_http_test.rb +0 -31
  49. data/test/adapters/patron_test.rb +1 -1
  50. data/test/adapters/rack_test.rb +0 -5
  51. data/test/adapters/test_middleware_test.rb +19 -4
  52. data/test/adapters/typhoeus_test.rb +20 -3
  53. data/test/authentication_middleware_test.rb +7 -7
  54. data/test/connection_test.rb +52 -75
  55. data/test/env_test.rb +33 -24
  56. data/test/helper.rb +15 -13
  57. data/test/live_server.rb +10 -4
  58. data/test/middleware/instrumentation_test.rb +75 -0
  59. data/test/middleware/retry_test.rb +44 -38
  60. data/test/middleware_stack_test.rb +12 -11
  61. data/test/options_test.rb +126 -0
  62. data/test/request_middleware_test.rb +17 -7
  63. data/test/response_middleware_test.rb +2 -4
  64. data/test/strawberry.rb +2 -0
  65. metadata +82 -28
  66. checksums.yaml +0 -7
  67. data/script/proxy-server +0 -41
  68. data/test/multibyte.txt +0 -1
  69. data/test/parameters_test.rb +0 -24
  70. data/test/utils_test.rb +0 -30
@@ -13,43 +13,37 @@ module Faraday
13
13
  # Override this to modify the environment after the response has finished.
14
14
  # Calls the `parse` method if defined
15
15
  def on_complete(env)
16
- if respond_to? :parse
17
- env[:body] = parse(env[:body]) unless [204,304].index env[:status]
18
- end
16
+ env.body = parse(env.body) if respond_to?(:parse) && env.parse_body?
19
17
  end
20
18
  end
21
19
 
22
20
  extend Forwardable
23
- extend AutoloadHelper
24
21
  extend MiddlewareRegistry
25
22
 
26
- autoload_all 'faraday/response',
27
- :RaiseError => 'raise_error',
28
- :Logger => 'logger'
29
-
30
- register_middleware \
31
- :raise_error => :RaiseError,
32
- :logger => :Logger
23
+ register_middleware File.expand_path('../response', __FILE__),
24
+ :raise_error => [:RaiseError, 'raise_error'],
25
+ :logger => [:Logger, 'logger']
33
26
 
34
27
  def initialize(env = nil)
35
- @env = env
28
+ @env = Env.from(env) if env
36
29
  @on_complete_callbacks = []
37
30
  end
38
31
 
39
32
  attr_reader :env
40
- alias_method :to_hash, :env
33
+
34
+ def_delegators :env, :to_hash
41
35
 
42
36
  def status
43
- finished? ? env[:status] : nil
37
+ finished? ? env.status : nil
44
38
  end
45
39
 
46
40
  def headers
47
- finished? ? env[:response_headers] : {}
41
+ finished? ? env.response_headers : {}
48
42
  end
49
43
  def_delegator :headers, :[]
50
44
 
51
45
  def body
52
- finished? ? env[:body] : nil
46
+ finished? ? env.body : nil
53
47
  end
54
48
 
55
49
  def finished?
@@ -67,33 +61,34 @@ module Faraday
67
61
 
68
62
  def finish(env)
69
63
  raise "response already finished" if finished?
70
- @env = env
64
+ @env = Env.from(env)
71
65
  @on_complete_callbacks.each { |callback| callback.call(env) }
72
66
  return self
73
67
  end
74
68
 
75
69
  def success?
76
- (200..299).include?(status)
70
+ finished? && env.success?
77
71
  end
78
72
 
79
73
  # because @on_complete_callbacks cannot be marshalled
80
74
  def marshal_dump
81
75
  !finished? ? nil : {
82
- :status => @env[:status], :body => @env[:body],
83
- :response_headers => @env[:response_headers]
76
+ :status => @env.status, :body => @env.body,
77
+ :response_headers => @env.response_headers
84
78
  }
85
79
  end
86
80
 
87
81
  def marshal_load(env)
88
- @env = env
82
+ @env = Env.from(env)
89
83
  end
90
84
 
91
85
  # Expand the env with more properties, without overriding existing ones.
92
86
  # Useful for applying request params after restoring a marshalled Response.
93
87
  def apply_request(request_env)
94
88
  raise "response didn't finish yet" unless finished?
95
- @env = request_env.merge @env
89
+ @env = Env.from(request_env).merge(@env)
96
90
  return self
97
91
  end
98
92
  end
99
93
  end
94
+
@@ -15,14 +15,14 @@ module Faraday
15
15
  def_delegators :@logger, :debug, :info, :warn, :error, :fatal
16
16
 
17
17
  def call(env)
18
- info "#{env[:method]} #{env[:url].to_s}"
19
- debug('request') { dump_headers env[:request_headers] }
18
+ info "#{env.method} #{env.url.to_s}"
19
+ debug('request') { dump_headers env.request_headers }
20
20
  super
21
21
  end
22
22
 
23
23
  def on_complete(env)
24
- info('Status') { env[:status].to_s }
25
- debug('response') { dump_headers env[:response_headers] }
24
+ info('Status') { env.status.to_s }
25
+ debug('response') { dump_headers env.response_headers }
26
26
  end
27
27
 
28
28
  private
@@ -1,19 +1,18 @@
1
1
  module Faraday
2
2
  class Response::RaiseError < Response::Middleware
3
+ ClientErrorStatuses = 400...600
4
+
3
5
  def on_complete(env)
4
6
  case env[:status]
5
7
  when 404
6
8
  raise Faraday::Error::ResourceNotFound, response_values(env)
7
- when 407
8
- # mimic the behavior that we get with proxy requests with HTTPS
9
- raise Faraday::Error::ConnectionFailed, %{407 "Proxy Authentication Required "}
10
- when 400...600
9
+ when ClientErrorStatuses
11
10
  raise Faraday::Error::ClientError, response_values(env)
12
11
  end
13
12
  end
14
13
 
15
14
  def response_values(env)
16
- {:status => env[:status], :headers => env[:response_headers], :body => env[:body]}
15
+ {:status => env.status, :headers => env.response_headers, :body => env.body}
17
16
  end
18
17
  end
19
18
  end
@@ -1,4 +1,6 @@
1
- require 'cgi'
1
+ require 'uri'
2
+ require 'thread'
3
+ Faraday.require_libs 'parameters'
2
4
 
3
5
  module Faraday
4
6
  module Utils
@@ -6,26 +8,28 @@ module Faraday
6
8
 
7
9
  # Adapted from Rack::Utils::HeaderHash
8
10
  class Headers < ::Hash
11
+ def self.from(value)
12
+ new(value)
13
+ end
14
+
9
15
  def initialize(hash={})
10
16
  super()
11
17
  @names = {}
12
18
  self.update hash
13
19
  end
14
20
 
15
- # on dup/clone, we need to duplicate @names hash
16
- def initialize_copy(other)
17
- super
18
- @names = other.names.dup
19
- end
21
+ # need to synchronize concurrent writes to the shared KeyMap
22
+ keymap_mutex = Mutex.new
20
23
 
21
24
  # symbol -> string mapper + cache
22
25
  KeyMap = Hash.new do |map, key|
23
- map[key] = if key.respond_to?(:to_str) then key
26
+ value = if key.respond_to?(:to_str) then key
24
27
  else
25
28
  key.to_s.split('_'). # :user_agent => %w(user agent)
26
29
  each { |w| w.capitalize! }. # => %w(User Agent)
27
30
  join('-') # => "User-Agent"
28
31
  end
32
+ keymap_mutex.synchronize { map[key] = value }
29
33
  end
30
34
  KeyMap[:etag] = "ETag"
31
35
 
@@ -71,7 +75,6 @@ module Faraday
71
75
 
72
76
  def replace(other)
73
77
  clear
74
- @names.clear
75
78
  self.update other
76
79
  self
77
80
  end
@@ -90,12 +93,6 @@ module Faraday
90
93
  end
91
94
  }
92
95
  end
93
-
94
- protected
95
-
96
- def names
97
- @names
98
- end
99
96
  end
100
97
 
101
98
  # hash with stringified keys
@@ -137,15 +134,15 @@ module Faraday
137
134
  update(other)
138
135
  end
139
136
 
140
- def merge_query(query)
137
+ def merge_query(query, encoder = nil)
141
138
  if query && !query.empty?
142
- update Utils.parse_nested_query(query)
139
+ update((encoder || Utils.default_params_encoder).decode(query))
143
140
  end
144
141
  self
145
142
  end
146
143
 
147
- def to_query
148
- Utils.build_nested_query(self)
144
+ def to_query(encoder = nil)
145
+ (encoder || Utils.default_params_encoder).encode(self)
149
146
  end
150
147
 
151
148
  private
@@ -155,39 +152,19 @@ module Faraday
155
152
  end
156
153
  end
157
154
 
158
- # Copied from Rack
159
155
  def build_query(params)
160
- params.map { |k, v|
161
- if v.class == Array
162
- build_query(v.map { |x| [k, x] })
163
- else
164
- v.nil? ? escape(k) : "#{escape(k)}=#{escape(v)}"
165
- end
166
- }.join("&")
156
+ FlatParamsEncoder.encode(params)
167
157
  end
168
158
 
169
- # Rack's version modified to handle non-String values
170
- def build_nested_query(value, prefix = nil)
171
- case value
172
- when Array
173
- value.map { |v| build_nested_query(v, "#{prefix}%5B%5D") }.join("&")
174
- when Hash
175
- value.map { |k, v|
176
- build_nested_query(v, prefix ? "#{prefix}%5B#{escape(k)}%5D" : escape(k))
177
- }.join("&")
178
- when NilClass
179
- prefix
180
- else
181
- raise ArgumentError, "value must be a Hash" if prefix.nil?
182
- "#{prefix}=#{escape(value)}"
183
- end
159
+ def build_nested_query(params)
160
+ NestedParamsEncoder.encode(params)
184
161
  end
185
162
 
186
163
  ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
187
164
 
188
165
  def escape(s)
189
- s.to_s.gsub(ESCAPE_RE) {|match|
190
- '%' + match.unpack('H2' * match.bytesize).join('%').upcase
166
+ s.to_s.gsub(ESCAPE_RE) {
167
+ '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase
191
168
  }.tr(' ', '+')
192
169
  end
193
170
 
@@ -196,31 +173,20 @@ module Faraday
196
173
  DEFAULT_SEP = /[&;] */n
197
174
 
198
175
  # Adapted from Rack
199
- def parse_query(qs)
200
- params = {}
201
-
202
- (qs || '').split(DEFAULT_SEP).each do |p|
203
- k, v = p.split('=', 2).map { |x| unescape(x) }
176
+ def parse_query(query)
177
+ FlatParamsEncoder.decode(query)
178
+ end
204
179
 
205
- if cur = params[k]
206
- if cur.class == Array then params[k] << v
207
- else params[k] = [cur, v]
208
- end
209
- else
210
- params[k] = v
211
- end
212
- end
213
- params
180
+ def parse_nested_query(query)
181
+ NestedParamsEncoder.decode(query)
214
182
  end
215
183
 
216
- def parse_nested_query(qs)
217
- params = {}
184
+ def default_params_encoder
185
+ @default_params_encoder ||= NestedParamsEncoder
186
+ end
218
187
 
219
- (qs || '').split(DEFAULT_SEP).each do |p|
220
- k, v = p.split('=', 2).map { |s| unescape(s) }
221
- normalize_params(params, k, v)
222
- end
223
- params
188
+ class << self
189
+ attr_writer :default_params_encoder
224
190
  end
225
191
 
226
192
  # Stolen from Rack
@@ -232,7 +198,12 @@ module Faraday
232
198
  return if k.empty?
233
199
 
234
200
  if after == ""
235
- params[k] = v
201
+ if params[k]
202
+ params[k] = Array[params[k]] unless params[k].kind_of?(Array)
203
+ params[k] << v
204
+ else
205
+ params[k] = v
206
+ end
236
207
  elsif after == "[]"
237
208
  params[k] ||= []
238
209
  raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array)
@@ -255,9 +226,25 @@ module Faraday
255
226
  return params
256
227
  end
257
228
 
258
- # Receives a URL and returns just the path with the query string sorted.
229
+ # Normalize URI() behavior across Ruby versions
230
+ #
231
+ # url - A String or URI.
232
+ #
233
+ # Returns a parsed URI.
234
+ def URI(url)
235
+ if url.respond_to?(:host)
236
+ url
237
+ elsif url.respond_to?(:to_str)
238
+ Kernel.URI(url)
239
+ else
240
+ raise ArgumentError, "bad argument (expected URI object or URI string)"
241
+ end
242
+ end
243
+
244
+ # Receives a String or URI and returns just the path with the query string sorted.
259
245
  def normalize_path(url)
260
- (url.path != "" ? url.path : "/") +
246
+ url = URI(url)
247
+ (url.path.start_with?('/') ? url.path : '/' + url.path) +
261
248
  (url.query ? "?#{sort_query_params(url.query)}" : "")
262
249
  end
263
250
 
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+ # Usage: script/console
3
+ # Starts an IRB console with this library loaded.
4
+
5
+ gemspec="$(ls *.gemspec | head -1)"
6
+
7
+ exec bundle exec irb -r "${gemspec%.*}"
@@ -8,7 +8,10 @@ set -e
8
8
  version="$(script/package | grep Version: | awk '{print $2}')"
9
9
  [ -n "$version" ] || exit 1
10
10
 
11
- git commit -a -m "faraday $version"
12
- git tag "v${version}"
13
- git push origin "v${version}" HEAD
11
+ git commit --allow-empty -a -m "Release $version"
12
+ git tag "v$version"
13
+ git push origin
14
+ git push origin "v$version"
15
+ git push legacy
16
+ git push legacy "v$version"
14
17
  gem push pkg/*-${version}.gem
@@ -6,13 +6,15 @@ ensure
6
6
  $VERBOSE = old_verbose
7
7
  end
8
8
  require 'webrick'
9
+ require 'fileutils'
9
10
 
10
11
  port = 4000
11
12
  if found = ARGV.index('-p')
12
13
  port = ARGV[found + 1].to_i
13
14
  end
14
15
 
15
- log_io = $stdout
16
+ FileUtils.mkdir_p('log')
17
+ log_io = File.open('log/test.log', 'w')
16
18
  log_io.sync = true
17
19
 
18
20
  webrick_opts = {
@@ -24,7 +24,6 @@ if [[ "$RUBYOPT" != *"bundler/setup"* ]]; then
24
24
  fi
25
25
 
26
26
  port=3999
27
- proxy_port=3998
28
27
  scheme=http
29
28
 
30
29
  if [ "$SSL" = "yes" ]; then
@@ -48,18 +47,12 @@ filter_matching() {
48
47
 
49
48
  start_server() {
50
49
  mkdir -p log
51
- script/server -p $port >log/test.log 2>&1 &
52
- echo $!
53
- }
54
-
55
- start_proxy() {
56
- mkdir -p log
57
- script/proxy-server -p $proxy_port -u "faraday@test.local:there is cake" >log/proxy.log 2>&1 &
50
+ script/server -p $port >log/server.log 2>&1 &
58
51
  echo $!
59
52
  }
60
53
 
61
54
  server_started() {
62
- lsof -i :${1?} >/dev/null
55
+ lsof -i :$port >/dev/null
63
56
  }
64
57
 
65
58
  timestamp() {
@@ -69,7 +62,7 @@ timestamp() {
69
62
  wait_for_server() {
70
63
  timeout=$(( `timestamp` + $1 ))
71
64
  while true; do
72
- if server_started "$2"; then
65
+ if server_started; then
73
66
  break
74
67
  elif [ `timestamp` -gt "$timeout" ]; then
75
68
  echo "timed out after $1 seconds" >&2
@@ -116,45 +109,27 @@ fi
116
109
 
117
110
  # If there are any adapter tests, spin up the HTTP server
118
111
  if [ -n "$(filter_matching "adapters" "${test_files[@]}")" ]; then
119
- if server_started $port; then
112
+ if server_started; then
120
113
  echo "aborted: another instance of server running on $port" >&2
121
114
  exit 1
122
115
  fi
123
116
  server_pid=$(start_server)
124
- proxy_pid=$(start_proxy)
125
- wait_for_server 15 $port || {
126
- cat log/test.log
117
+ wait_for_server 15 || {
118
+ cat log/server.log
127
119
  exit 1
128
120
  }
129
- wait_for_server 5 $proxy_port
130
121
  cleanup() {
131
122
  if [ $? -ne 0 ] && [ -n "$TRAVIS" ]; then
132
123
  cat log/test.log
133
124
  fi
134
125
  kill "$server_pid"
135
- kill "$proxy_pid"
136
126
  }
137
127
  trap cleanup INT EXIT
138
128
  export LIVE="${scheme}://localhost:${port}"
139
- export LIVE_PROXY="http://faraday%40test.local:there%20is%20cake@localhost:${proxy_port}"
140
129
  fi
141
130
 
142
- warnings="${TMPDIR:-/tmp}/faraday-warnings.$$"
143
-
144
131
  run_test_files() {
145
- # Save warnings on stderr to a separate file
146
- RUBYOPT="$RUBYOPT -w" ruby -e 'while f=ARGV.shift and f!="--"; load f; end' "${test_files[@]}" -- "$@" \
147
- 2> >(tee >(grep 'warning:' >"$warnings") | grep -v 'warning:')
148
- }
149
-
150
- check_warnings() {
151
- # Display Ruby warnings from this project's source files. Abort if any were found.
152
- num="$(grep -F "$PWD" "$warnings" | grep -v "${PWD}/vendor/bundle" | sort | uniq -c | sort -rn | tee /dev/stderr | wc -l)"
153
- rm -f "$warnings"
154
- if [ "$num" -gt 0 ]; then
155
- echo "FAILED: this test suite doesn't tolerate Ruby syntax warnings!" >&2
156
- exit 1
157
- fi
132
+ ruby -e 'while f=ARGV.shift and f!="--"; load f; end' "${test_files[@]}" -- "$@"
158
133
  }
159
134
 
160
135
  if [ -n "$RBENV_VERSIONS" ]; then
@@ -167,4 +142,3 @@ else
167
142
  run_test_files "$@"
168
143
  fi
169
144
 
170
- check_warnings