Compare commits

..

No commits in common. "main" and "v2.10.0" have entirely different histories.

31 changed files with 141 additions and 613 deletions

View File

@ -24,7 +24,7 @@ These resources can help:
This project attempts to improve in these areas. Join us in doing that important work. This project attempts to improve in these areas. Join us in doing that important work.
If you want to privately raise any breach to this policy with the Faraday team, feel free to reach out to [@iMacTia](https://ruby.social/@iMacTia) and [@olleolleolle](https://ruby.social/@olleolleolle) on the Mastodon instance ruby.social. If you want to privately raise any breach to this policy with the Faraday team, feel free to reach out to [@iMacTia](https://twitter.com/iMacTia) and [@olleolleolle](https://twitter.com/olleolleolle) on Twitter.
### Required Checks ### Required Checks

View File

@ -43,7 +43,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
ruby: [ '3.0', '3.1', '3.2', '3.3', '3.4' ] ruby: [ '3.0', '3.1', '3.2', '3.3' ]
experimental: [false] experimental: [false]
include: include:
- ruby: head - ruby: head

View File

@ -31,7 +31,7 @@ Metrics/AbcSize:
# Offense count: 3 # Offense count: 3
# Configuration parameters: CountComments, CountAsOne. # Configuration parameters: CountComments, CountAsOne.
Metrics/ClassLength: Metrics/ClassLength:
Max: 230 Max: 225
# Offense count: 9 # Offense count: 9
# Configuration parameters: AllowedMethods, AllowedPatterns. # Configuration parameters: AllowedMethods, AllowedPatterns.

View File

@ -517,7 +517,7 @@ Breaking changes:
- Drop support for Ruby 1.8 - Drop support for Ruby 1.8
Features: Features:
- Include wrapped exception/response in ClientErrors - Include wrapped exception/reponse in ClientErrors
- Add `response.reason_phrase` - Add `response.reason_phrase`
- Provide option to selectively skip logging request/response headers - Provide option to selectively skip logging request/response headers
- Add regex support for pattern matching in `test` adapter - Add regex support for pattern matching in `test` adapter

View File

@ -26,4 +26,8 @@ group :development, :lint do
gem 'yard-junk' gem 'yard-junk'
end end
group :deployment do
gem 'rubygems-await', github: 'segiddins/rubygems-await', ref: 'f5e2b0413ec6f17e35d9bb7902dcb28b31804701'
end
gemspec gemspec

View File

@ -19,7 +19,7 @@ We've taken this decision for the following technical reasons:
focused on the Faraday API more quickly, without having to push it on all adapters immediately. focused on the Faraday API more quickly, without having to push it on all adapters immediately.
* With the community creating more and more adapters, we wanted to avoid having first and second-class adapters * With the community creating more and more adapters, we wanted to avoid having first and second-class adapters
by having some of them included with the gem and others available externally. by having some of them included with the gem and others available externally.
* Moving adapters into separate gems solve the dependency issues once and for all. * Moving adapters into separate gems allow to solve the dependency issues once and for all.
Faraday will remain a dependency-free gem, while adapter gems will be able to automatically pull Faraday will remain a dependency-free gem, while adapter gems will be able to automatically pull
any necessary dependency, without having to rely on the developer to do so. any necessary dependency, without having to rely on the developer to do so.

View File

@ -2,7 +2,6 @@
* [Quick Start](getting-started/quick-start.md) * [Quick Start](getting-started/quick-start.md)
* [The Env Object](getting-started/env-object.md) * [The Env Object](getting-started/env-object.md)
* [Dealing with Errors](getting-started/errors.md) * [Dealing with Errors](getting-started/errors.md)
* [Migrating from rest-client](getting-started/rest-client-migration.md)
* Customization * Customization
* [Configuration](customization/index.md) * [Configuration](customization/index.md)
* [Connection Options](customization/connection-options.md) * [Connection Options](customization/connection-options.md)

View File

@ -42,34 +42,18 @@ class FlorpHttp < ::Faraday::Adapter
def self.setup_parallel_manager(_options = nil) def self.setup_parallel_manager(_options = nil)
FlorpParallelManager.new # NB: we will need to define this FlorpParallelManager.new # NB: we will need to define this
end end
def call(env)
# NB: you can call `in_parallel?` here to check if the current request
# is part of a parallel batch. Useful if you need to collect all requests
# into the ParallelManager before running them.
end
end end
class FlorpParallelManager class FlorpParallelManager
# The execute method will be passed the same block as `in_parallel`, def add(request, method, *args, &block)
# so you can either collect the requests or just wrap them into a wrapper, # Collect the requests
# depending on how your adapter works. end
def execute(&block)
run_async(&block) def run
# Process the requests
end end
end end
``` ```
### A note on the old, deprecated interface Compare to the finished example [em-synchrony](https://github.com/lostisland/faraday-em_synchrony/blob/main/lib/faraday/adapter/em_synchrony.rb)
Prior to the introduction of the `execute` method, the `ParallelManager` was expected to implement a `run` method
and the execution of the block was done by the Faraday connection BEFORE calling that method.
This approach made the `ParallelManager` implementation harder and forced you to keep state around.
The new `execute` implementation allows to avoid this shortfall and support different flows.
As of Faraday 2.0, `run` is still supported in case `execute` is not implemented by the `ParallelManager`,
but this method should be considered deprecated.
For reference, please see an example using `run` from [em-synchrony](https://github.com/lostisland/faraday-em_synchrony/blob/main/lib/faraday/adapter/em_synchrony.rb)
and its [ParallelManager implementation](https://github.com/lostisland/faraday-em_synchrony/blob/main/lib/faraday/adapter/em_synchrony/parallel_manager.rb). and its [ParallelManager implementation](https://github.com/lostisland/faraday-em_synchrony/blob/main/lib/faraday/adapter/em_synchrony/parallel_manager.rb).

View File

@ -2,24 +2,22 @@
Faraday supports a number of SSL options, which can be provided while initializing the connection. Faraday supports a number of SSL options, which can be provided while initializing the connection.
| Option | Type | Default | Description | | Option | Type | Default | Description |
|--------------------|----------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------| |--------------------|----------------------------------------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| `:verify` | Boolean | true | Verify SSL certificate. Defaults to `true`. | | `:verify` | Boolean | true | Verify SSL certificate. Defaults to `true`. |
| `:verify_hostname` | Boolean | true | Verify SSL certificate hostname. Defaults to `true`. | | `:verify_hostname` | Boolean | true | Verify SSL certificate hostname. Defaults to `true`. |
| `:hostname` | String | nil | Server hostname for SNI (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLSocket.html#method-i-hostname-3D)). | | `:ca_file` | String | nil | Path to a CA file in PEM format. |
| `:ca_file` | String | nil | Path to a CA file in PEM format. | | `:ca_path` | String | nil | Path to a CA directory. |
| `:ca_path` | String | nil | Path to a CA directory. |
| `:verify_mode` | Integer | nil | Any `OpenSSL::SSL::` constant (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL.html)). | | `:verify_mode` | Integer | nil | Any `OpenSSL::SSL::` constant (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL.html)). |
| `:cert_store` | OpenSSL::X509::Store | nil | OpenSSL certificate store. | | `:cert_store` | OpenSSL::X509::Store | nil | OpenSSL certificate store. |
| `:client_cert` | OpenSSL::X509::Certificate | nil | Client certificate. | | `:client_cert` | OpenSSL::X509::Certificate | nil | Client certificate. |
| `:client_key` | OpenSSL::PKey::RSA, OpenSSL::PKey::DSA | nil | Client private key. | | `:client_key` | OpenSSL::PKey::RSA, OpenSSL::PKey::DSA | nil | Client private key. |
| `:certificate` | OpenSSL::X509::Certificate | nil | Certificate (Excon only). | | `:certificate` | OpenSSL::X509::Certificate | nil | Certificate (Excon only). |
| `:private_key` | OpenSSL::PKey::RSA | nil | Private key (Excon only). | | `:private_key` | OpenSSL::PKey::RSA | nil | Private key (Excon only). |
| `:verify_depth` | Integer | nil | Maximum depth for the certificate chain verification. | | `:verify_depth` | Integer | nil | Maximum depth for the certificate chain verification. |
| `:version` | Integer | nil | SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D)). | | `:version` | Integer | nil | SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D)). |
| `:min_version` | Integer | nil | Minimum SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D)). | | `:min_version` | Integer | nil | Minimum SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D)). |
| `:max_version` | Integer | nil | Maximum SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)). | | `:max_version` | Integer | nil | Maximum SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)). |
| `:ciphers` | String | nil | Ciphers supported (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-ciphers-3D)). |
## Example ## Example

View File

@ -1,225 +0,0 @@
# Migrating from `rest-client` to `Faraday`
The `rest-client` gem is in maintenance mode, and developers are encouraged to migrate to actively maintained alternatives like [`faraday`](https://github.com/lostisland/faraday). This guide highlights common usage patterns in `rest-client` and how to migrate them to `faraday`.
---
## Quick Comparison
| Task | rest-client example | faraday example |
| ----------------- | -------------------------------------------------------- | -------------------------------------------------------------------------- |
| Simple GET | `RestClient.get("https://httpbingo.org/get")` | `Faraday.get("https://httpbingo.org/get")` |
| GET with params | `RestClient.get(url, params: { id: 1 })` | `Faraday.get(url, { id: 1 })` |
| POST form data | `RestClient.post(url, { a: 1 })` | `Faraday.post(url, { a: 1 })` |
| POST JSON | `RestClient.post(url, obj.to_json, content_type: :json)` | `Faraday.post(url, obj.to_json, { 'Content-Type' => 'application/json' })` |
| Custom headers | `RestClient.get(url, { Authorization: 'Bearer token' })` | `Faraday.get(url, nil, { 'Authorization' => 'Bearer token' })` |
| Get response body | `response.body` | `response.body` |
| Get status code | `response.code` | `response.status` |
| Get headers | `response.headers` (returns `Hash<Symbol, String>`) | `response.headers` (returns `Hash<String, String>`) |
---
## Installation
In your `Gemfile`, replace `rest-client` with:
```ruby
gem "faraday"
```
Then run:
```sh
bundle install
```
---
## Basic HTTP Requests
### GET request
**rest-client:**
```ruby
RestClient.get("https://httpbingo.org/get")
```
**faraday:**
```ruby
Faraday.get("https://httpbingo.org/get")
```
---
### GET with Params
**rest-client:**
```ruby
RestClient.get("https://httpbingo.org/get", params: { id: 1, foo: "bar" })
```
**faraday:**
```ruby
Faraday.get("https://httpbingo.org/get", { id: 1, foo: "bar" })
```
---
### POST Requests
**rest-client:**
```ruby
RestClient.post("https://httpbingo.org/post", { foo: "bar" })
```
**faraday:**
```ruby
Faraday.post("https://httpbingo.org/post", { foo: "bar" })
```
---
### Sending JSON
**rest-client:**
```ruby
RestClient.post("https://httpbingo.org/post", { foo: "bar" }.to_json, content_type: :json)
```
**faraday (manual):**
```ruby
Faraday.post("https://httpbingo.org/post", { foo: "bar" }.to_json, { 'Content-Type' => 'application/json' })
```
**faraday (with middleware):**
```ruby
conn = Faraday.new(url: "https://httpbingo.org") do |f|
f.request :json # encode request body as JSON and set Content-Type
f.response :json # parse response body as JSON
end
conn.post("/post", { foo: "bar" })
```
---
## Handling Responses
**rest-client:**
```ruby
response = RestClient.get("https://httpbingo.org/headers")
response.code # => 200
response.body # => "..."
response.headers # => { content_type: "application/json", ... }
```
**faraday:**
> notice headers Hash keys are stringified, not symbolized like in rest-client
```ruby
response = Faraday.get("https://httpbingo.org/headers")
response.status # => 200
response.body # => "..."
response.headers # => { "content-type" => "application/json", ... }
```
---
## Error Handling
**rest-client:**
```ruby
begin
RestClient.get("https://httpbingo.org/status/404")
rescue RestClient::NotFound => e
puts e.response.code # 404
end
```
**faraday:**
> By default, Faraday does **not** raise exceptions for HTTP errors (like 404 or 500); it simply returns the response. If you want exceptions to be raised on HTTP error responses, include the `:raise_error` middleware.
>
> With `:raise_error`, Faraday will raise `Faraday::ResourceNotFound` for 404s and other exceptions for other 4xx/5xx responses.
>
> See also:
>
> * [Dealing with Errors](getting-started/errors.md)
> * [Raising Errors](middleware/included/raising-errors.md)
```ruby
conn = Faraday.new(url: "https://httpbingo.org") do |f|
f.response :raise_error
end
begin
conn.get("/status/404")
rescue Faraday::ResourceNotFound => e
puts e.response[:status] # 404
end
```
---
## Advanced Request Configuration
**rest-client:**
```ruby
RestClient::Request.execute(method: :get, url: "https://httpbingo.org/get", timeout: 10)
```
**faraday:**
```ruby
conn = Faraday.new(url: "https://httpbingo.org", request: { timeout: 10 })
conn.get("/get")
```
---
## Headers
**rest-client:**
```ruby
RestClient.get("https://httpbingo.org/headers", { Authorization: "Bearer token" })
```
**faraday:**
> Notice headers Hash expects stringified keys.
```ruby
Faraday.get("https://httpbingo.org/headers", nil, { "Authorization" => "Bearer token" })
```
---
## Redirects
**rest-client:**
Automatically follows GET/HEAD redirects by default.
**faraday:**
Use the `follow_redirects` middleware (not included by default):
```ruby
require "faraday/follow_redirects"
conn = Faraday.new(url: "https://httpbingo.org") do |f|
f.response :follow_redirects
end
```

View File

@ -63,28 +63,23 @@ and raised as `Faraday::NilStatusError`, which inherits from `Faraday::ServerErr
The behavior of this middleware can be customized with the following options: The behavior of this middleware can be customized with the following options:
| Option | Default | Description | | Option | Default | Description |
|----------------------|---------|-------------| |---------------------|---------|-------------|
| **include_request** | true | When true, exceptions are initialized with request information including `method`, `url`, `url_path`, `params`, `headers`, and `body`. | | **include_request** | true | When true, exceptions are initialized with request information including `method`, `url`, `url_path`, `params`, `headers`, and `body`. |
| **allowed_statuses** | [] | An array of status codes that should not raise an error. |
### Example Usage ### Example Usage
```ruby ```ruby
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday| conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
faraday.response :raise_error, include_request: true, allowed_statuses: [404] faraday.response :raise_error, include_request: true
end end
begin begin
conn.get('/wrong-url') # => Assume this raises a 404 response conn.get('/wrong-url') # => Assume this raises a 404 response
conn.get('/protected-url') # => Assume this raises a 401 response rescue Faraday::ResourceNotFound => e
rescue Faraday::UnauthorizedError => e e.response[:status] #=> 404
e.response[:status] # => 401 e.response[:headers] #=> { ... }
e.response[:headers] # => { ... } e.response[:body] #=> "..."
e.response[:body] # => "..." e.response[:request][:url_path] #=> "/wrong-url"
e.response[:request][:url_path] # => "/protected-url"
end end
``` ```
In this example, a `Faraday::UnauthorizedError` exception is raised for the `/protected-url` request, while the
`/wrong-url` request does not raise an error because the status code `404` is in the `allowed_statuses` array.

View File

@ -147,7 +147,7 @@ In the case where it is desirable to change the default option for all instances
Faraday::Response::RaiseError.default_options = { include_request: false } Faraday::Response::RaiseError.default_options = { include_request: false }
``` ```
After app initialization, all instances of the middleware will have the newly configured option(s). They can still be overridden on a per-instance bases (if handled in the middleware), like this: After app initialization, all instances of the middleware will have the newly configured option(s). They can still be overriden on a per-instance bases (if handled in the middleware), like this:
```ruby ```ruby
Faraday.new do |f| Faraday.new do |f|

View File

@ -21,8 +21,7 @@ Gem::Specification.new do |spec|
# always fix its required version to the next MINOR version. # always fix its required version to the next MINOR version.
# This way, we can release minor versions of the adapter with "breaking" changes for older versions of Faraday # This way, we can release minor versions of the adapter with "breaking" changes for older versions of Faraday
# and then bump the version requirement on the next compatible version of faraday. # and then bump the version requirement on the next compatible version of faraday.
spec.add_dependency 'faraday-net_http', '>= 2.0', '< 3.5' spec.add_dependency 'faraday-net_http', '>= 2.0', '< 3.2'
spec.add_dependency 'json'
spec.add_dependency 'logger' spec.add_dependency 'logger'
# Includes `examples` and `spec` to allow external adapter gems to run Faraday unit and integration tests # Includes `examples` and `spec` to allow external adapter gems to run Faraday unit and integration tests
@ -33,7 +32,6 @@ Gem::Specification.new do |spec|
'changelog_uri' => 'changelog_uri' =>
"https://github.com/lostisland/faraday/releases/tag/v#{spec.version}", "https://github.com/lostisland/faraday/releases/tag/v#{spec.version}",
'source_code_uri' => 'https://github.com/lostisland/faraday', 'source_code_uri' => 'https://github.com/lostisland/faraday',
'bug_tracker_uri' => 'https://github.com/lostisland/faraday/issues', 'bug_tracker_uri' => 'https://github.com/lostisland/faraday/issues'
'rubygems_mfa_required' => 'true'
} }
end end

View File

@ -1,7 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'cgi/escape' require 'cgi'
require 'cgi/util' if RUBY_VERSION < '3.5'
require 'date' require 'date'
require 'set' require 'set'
require 'forwardable' require 'forwardable'

View File

@ -314,23 +314,15 @@ module Faraday
# #
# @yield a block to execute multiple requests. # @yield a block to execute multiple requests.
# @return [void] # @return [void]
def in_parallel(manager = nil, &block) def in_parallel(manager = nil)
@parallel_manager = manager || default_parallel_manager do @parallel_manager = manager || default_parallel_manager do
warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \ warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \
'on Faraday stack' 'on Faraday stack'
warn caller[2, 10].join("\n") warn caller[2, 10].join("\n")
nil nil
end end
return yield unless @parallel_manager yield
@parallel_manager&.run
if @parallel_manager.respond_to?(:execute)
# Execute is the new method that is responsible for executing the block.
@parallel_manager.execute(&block)
else
# TODO: Old behaviour, deprecate and remove in 3.0
yield
@parallel_manager.run
end
ensure ensure
@parallel_manager = nil @parallel_manager = nil
end end

View File

@ -79,46 +79,12 @@ module Faraday
# Pulls out potential parent exception and response hash. # Pulls out potential parent exception and response hash.
def exc_msg_and_response(exc, response = nil) def exc_msg_and_response(exc, response = nil)
case exc return [exc, exc.message, response] if exc.respond_to?(:backtrace)
when Exception
[exc, exc.message, response]
when Hash
[nil, build_error_message_from_hash(exc), exc]
when Faraday::Env
[nil, build_error_message_from_env(exc), exc]
else
[nil, exc.to_s, response]
end
end
private return [nil, "the server responded with status #{exc[:status]}", exc] \
if exc.respond_to?(:each_key)
def build_error_message_from_hash(hash) [nil, exc.to_s, response]
# Be defensive with external Hash objects - they might be missing keys
status = hash.fetch(:status, nil)
request = hash.fetch(:request, nil)
return fallback_error_message(status) if request.nil?
method = request.fetch(:method, nil)
url = request.fetch(:url, nil)
build_status_error_message(status, method, url)
end
def build_error_message_from_env(env)
# Faraday::Env is internal - we can make reasonable assumptions about its structure
build_status_error_message(env.status, env.method, env.url)
end
def build_status_error_message(status, method, url)
method_str = method ? method.to_s.upcase : ''
url_str = url ? url.to_s : ''
"the server responded with status #{status} for #{method_str} #{url_str}"
end
def fallback_error_message(status)
"the server responded with status #{status} - method and url are not available " \
'due to include_request: false on Faraday::Response::RaiseError middleware'
end end
end end

View File

@ -23,8 +23,8 @@ module Faraday
def_delegators :@logger, :debug, :info, :warn, :error, :fatal def_delegators :@logger, :debug, :info, :warn, :error, :fatal
def request(env) def request(env)
public_send(log_level) do public_send(log_level, 'request') do
"request: #{env.method.upcase} #{apply_filters(env.url.to_s)}" "#{env.method.upcase} #{apply_filters(env.url.to_s)}"
end end
log_headers('request', env.request_headers) if log_headers?(:request) log_headers('request', env.request_headers) if log_headers?(:request)
@ -32,7 +32,7 @@ module Faraday
end end
def response(env) def response(env)
public_send(log_level) { "response: Status #{env.status}" } public_send(log_level, 'response') { "Status #{env.status}" }
log_headers('response', env.response_headers) if log_headers?(:response) log_headers('response', env.response_headers) if log_headers?(:response)
log_body('response', env[:body]) if env[:body] && log_body?(:response) log_body('response', env[:body]) if env[:body] && log_body?(:response)
@ -41,7 +41,7 @@ module Faraday
def exception(exc) def exception(exc)
return unless log_errors? return unless log_errors?
public_send(log_level) { "error: #{exc.full_message}" } public_send(log_level, 'error') { exc.full_message }
log_headers('error', exc.response_headers) if exc.respond_to?(:response_headers) && log_headers?(:error) log_headers('error', exc.response_headers) if exc.respond_to?(:response_headers) && log_headers?(:error)
return unless exc.respond_to?(:response_body) && exc.response_body && log_body?(:error) return unless exc.respond_to?(:response_body) && exc.response_body && log_body?(:error)
@ -107,11 +107,11 @@ module Faraday
end end
def log_headers(type, headers) def log_headers(type, headers)
public_send(log_level) { "#{type}: #{apply_filters(dump_headers(headers))}" } public_send(log_level, type) { apply_filters(dump_headers(headers)) }
end end
def log_body(type, body) def log_body(type, body)
public_send(log_level) { "#{type}: #{apply_filters(dump_body(body))}" } public_send(log_level, type) { apply_filters(dump_body(body)) }
end end
end end
end end

View File

@ -10,7 +10,6 @@ module Faraday
attr_reader :app, :options attr_reader :app, :options
DEFAULT_OPTIONS = {}.freeze DEFAULT_OPTIONS = {}.freeze
LOCK = Mutex.new
def initialize(app = nil, options = {}) def initialize(app = nil, options = {})
@app = app @app = app
@ -28,7 +27,7 @@ module Faraday
# #
def default_options=(options = {}) def default_options=(options = {})
validate_default_options(options) validate_default_options(options)
LOCK.synchronize do lock.synchronize do
@default_options = default_options.merge(options) @default_options = default_options.merge(options)
end end
end end
@ -42,6 +41,10 @@ module Faraday
private private
def lock
@lock ||= Monitor.new
end
def validate_default_options(options) def validate_default_options(options)
invalid_keys = options.keys.reject { |opt| self::DEFAULT_OPTIONS.key?(opt) } invalid_keys = options.keys.reject { |opt| self::DEFAULT_OPTIONS.key?(opt) }
return unless invalid_keys.any? return unless invalid_keys.any?

View File

@ -60,7 +60,7 @@ module Faraday
:reason_phrase, :response_body) do :reason_phrase, :response_body) do
const_set(:ContentLength, 'Content-Length') const_set(:ContentLength, 'Content-Length')
const_set(:StatusesWithoutBody, Set.new([204, 304])) const_set(:StatusesWithoutBody, Set.new([204, 304]))
const_set(:SuccessfulStatuses, 200..299) const_set(:SuccessfulStatuses, (200..299))
# A Set of HTTP verbs that typically send a body. If no body is set for # A Set of HTTP verbs that typically send a body. If no body is set for
# these requests, the Content-Length header is set to 0. # these requests, the Content-Length header is set to 0.

View File

@ -22,10 +22,8 @@ module Faraday
when URI when URI
value = { uri: value } value = { uri: value }
when Hash, Options when Hash, Options
if value[:uri] if (uri = value.delete(:uri))
value = value.dup.tap do |duped| value[:uri] = Utils.URI(uri)
duped[:uri] = Utils.URI(duped[:uri])
end
end end
end end

View File

@ -11,9 +11,6 @@ module Faraday
# # @return [Boolean] whether to enable hostname verification on server certificates # # @return [Boolean] whether to enable hostname verification on server certificates
# # during the handshake or not (see https://github.com/ruby/openssl/pull/60) # # during the handshake or not (see https://github.com/ruby/openssl/pull/60)
# # # #
# # @!attribute hostname
# # @return [String] Server hostname used for SNI (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLSocket.html#method-i-hostname-3D)
# #
# # @!attribute ca_file # # @!attribute ca_file
# # @return [String] CA file # # @return [String] CA file
# # # #
@ -49,15 +46,12 @@ module Faraday
# # # #
# # @!attribute max_version # # @!attribute max_version
# # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D) # # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)
# #
# # @!attribute ciphers
# # @return [String] cipher list in OpenSSL format (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ciphers-3D)
# class SSLOptions < Options; end # class SSLOptions < Options; end
SSLOptions = Options.new(:verify, :verify_hostname, :hostname, SSLOptions = Options.new(:verify, :verify_hostname,
:ca_file, :ca_path, :verify_mode, :ca_file, :ca_path, :verify_mode,
:cert_store, :client_cert, :client_key, :cert_store, :client_cert, :client_key,
:certificate, :private_key, :verify_depth, :certificate, :private_key, :verify_depth,
:version, :min_version, :max_version, :ciphers) do :version, :min_version, :max_version) do
# @return [Boolean] true if should verify # @return [Boolean] true if should verify
def verify? def verify?
verify != false verify != false

View File

@ -27,11 +27,10 @@ module Faraday
attr_reader :name attr_reader :name
def initialize(klass, *args, **kwargs, &block) ruby2_keywords def initialize(klass, *args, &block)
@name = klass.to_s @name = klass.to_s
REGISTRY.set(klass) if klass.respond_to?(:name) REGISTRY.set(klass) if klass.respond_to?(:name)
@args = args @args = args
@kwargs = kwargs
@block = block @block = block
end end
@ -54,7 +53,7 @@ module Faraday
end end
def build(app = nil) def build(app = nil)
klass.new(app, *@args, **@kwargs, &@block) klass.new(app, *@args, &@block)
end end
end end
@ -89,52 +88,52 @@ module Faraday
@handlers.frozen? @handlers.frozen?
end end
def use(klass, ...) ruby2_keywords def use(klass, *args, &block)
if klass.is_a? Symbol if klass.is_a? Symbol
use_symbol(Faraday::Middleware, klass, ...) use_symbol(Faraday::Middleware, klass, *args, &block)
else else
raise_if_locked raise_if_locked
raise_if_adapter(klass) raise_if_adapter(klass)
@handlers << self.class::Handler.new(klass, ...) @handlers << self.class::Handler.new(klass, *args, &block)
end end
end end
def request(key, ...) ruby2_keywords def request(key, *args, &block)
use_symbol(Faraday::Request, key, ...) use_symbol(Faraday::Request, key, *args, &block)
end end
def response(...) ruby2_keywords def response(key, *args, &block)
use_symbol(Faraday::Response, ...) use_symbol(Faraday::Response, key, *args, &block)
end end
def adapter(klass = NO_ARGUMENT, *args, **kwargs, &block) ruby2_keywords def adapter(klass = NO_ARGUMENT, *args, &block)
return @adapter if klass == NO_ARGUMENT || klass.nil? return @adapter if klass == NO_ARGUMENT || klass.nil?
klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol) klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
@adapter = self.class::Handler.new(klass, *args, **kwargs, &block) @adapter = self.class::Handler.new(klass, *args, &block)
end end
## methods to push onto the various positions in the stack: ## methods to push onto the various positions in the stack:
def insert(index, ...) ruby2_keywords def insert(index, *args, &block)
raise_if_locked raise_if_locked
index = assert_index(index) index = assert_index(index)
handler = self.class::Handler.new(...) handler = self.class::Handler.new(*args, &block)
@handlers.insert(index, handler) @handlers.insert(index, handler)
end end
alias insert_before insert alias insert_before insert
def insert_after(index, ...) ruby2_keywords def insert_after(index, *args, &block)
index = assert_index(index) index = assert_index(index)
insert(index + 1, ...) insert(index + 1, *args, &block)
end end
def swap(index, ...) ruby2_keywords def swap(index, *args, &block)
raise_if_locked raise_if_locked
index = assert_index(index) index = assert_index(index)
@handlers.delete_at(index) @handlers.delete_at(index)
insert(index, ...) insert(index, *args, &block)
end end
def delete(handler) def delete(handler)
@ -221,7 +220,7 @@ module Faraday
end end
def raise_if_adapter(klass) def raise_if_adapter(klass)
return unless klass <= Faraday::Adapter return unless is_adapter?(klass)
raise 'Adapter should be set using the `adapter` method, not `use`' raise 'Adapter should be set using the `adapter` method, not `use`'
end end
@ -234,8 +233,12 @@ module Faraday
!@adapter.nil? !@adapter.nil?
end end
def use_symbol(mod, key, ...) def is_adapter?(klass) # rubocop:disable Naming/PredicateName
use(mod.lookup_middleware(key), ...) klass <= Faraday::Adapter
end
ruby2_keywords def use_symbol(mod, key, *args, &block)
use(mod.lookup_middleware(key), *args, &block)
end end
def assert_index(index) def assert_index(index)

View File

@ -10,13 +10,11 @@ module Faraday
# lifecycle to a given Logger object. By default, this logs to STDOUT. See # lifecycle to a given Logger object. By default, this logs to STDOUT. See
# Faraday::Logging::Formatter to see specifically what is logged. # Faraday::Logging::Formatter to see specifically what is logged.
class Logger < Middleware class Logger < Middleware
DEFAULT_OPTIONS = { formatter: Logging::Formatter }.merge(Logging::Formatter::DEFAULT_OPTIONS).freeze
def initialize(app, logger = nil, options = {}) def initialize(app, logger = nil, options = {})
super(app, options) super(app)
logger ||= ::Logger.new($stdout) logger ||= ::Logger.new($stdout)
formatter_class = @options.delete(:formatter) formatter_class = options.delete(:formatter) || Logging::Formatter
@formatter = formatter_class.new(logger: logger, options: @options) @formatter = formatter_class.new(logger: logger, options: options)
yield @formatter if block_given? yield @formatter if block_given?
end end

View File

@ -8,30 +8,32 @@ module Faraday
# rubocop:disable Naming/ConstantName # rubocop:disable Naming/ConstantName
ClientErrorStatuses = (400...500) ClientErrorStatuses = (400...500)
ServerErrorStatuses = (500...600) ServerErrorStatuses = (500...600)
ClientErrorStatusesWithCustomExceptions = {
400 => Faraday::BadRequestError,
401 => Faraday::UnauthorizedError,
403 => Faraday::ForbiddenError,
404 => Faraday::ResourceNotFound,
408 => Faraday::RequestTimeoutError,
409 => Faraday::ConflictError,
422 => Faraday::UnprocessableEntityError,
429 => Faraday::TooManyRequestsError
}.freeze
# rubocop:enable Naming/ConstantName # rubocop:enable Naming/ConstantName
DEFAULT_OPTIONS = { include_request: true, allowed_statuses: [] }.freeze DEFAULT_OPTIONS = { include_request: true }.freeze
def on_complete(env) def on_complete(env)
return if Array(options[:allowed_statuses]).include?(env[:status])
case env[:status] case env[:status]
when *ClientErrorStatusesWithCustomExceptions.keys when 400
raise ClientErrorStatusesWithCustomExceptions[env[:status]], response_values(env) raise Faraday::BadRequestError, response_values(env)
when 401
raise Faraday::UnauthorizedError, response_values(env)
when 403
raise Faraday::ForbiddenError, response_values(env)
when 404
raise Faraday::ResourceNotFound, response_values(env)
when 407 when 407
# mimic the behavior that we get with proxy requests with HTTPS # mimic the behavior that we get with proxy requests with HTTPS
msg = %(407 "Proxy Authentication Required") msg = %(407 "Proxy Authentication Required")
raise Faraday::ProxyAuthError.new(msg, response_values(env)) raise Faraday::ProxyAuthError.new(msg, response_values(env))
when 408
raise Faraday::RequestTimeoutError, response_values(env)
when 409
raise Faraday::ConflictError, response_values(env)
when 422
raise Faraday::UnprocessableEntityError, response_values(env)
when 429
raise Faraday::TooManyRequestsError, response_values(env)
when ClientErrorStatuses when ClientErrorStatuses
raise Faraday::ClientError, response_values(env) raise Faraday::ClientError, response_values(env)
when ServerErrorStatuses when ServerErrorStatuses

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
module Faraday module Faraday
VERSION = '2.13.4' VERSION = '2.10.0'
end end

32
package-lock.json generated
View File

@ -169,12 +169,11 @@
} }
}, },
"node_modules/braces": { "node_modules/braces": {
"version": "3.0.3", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"license": "MIT",
"dependencies": { "dependencies": {
"fill-range": "^7.1.1" "fill-range": "^7.0.1"
}, },
"engines": { "engines": {
"node": ">=8" "node": ">=8"
@ -613,10 +612,9 @@
} }
}, },
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.1.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"to-regex-range": "^5.0.1" "to-regex-range": "^5.0.1"
}, },
@ -933,7 +931,6 @@
"version": "7.0.0", "version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"license": "MIT",
"engines": { "engines": {
"node": ">=0.12.0" "node": ">=0.12.0"
} }
@ -1447,10 +1444,9 @@
} }
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "6.3.1", "version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"license": "ISC",
"bin": { "bin": {
"semver": "bin/semver.js" "semver": "bin/semver.js"
} }
@ -1635,7 +1631,6 @@
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"license": "MIT",
"dependencies": { "dependencies": {
"is-number": "^7.0.0" "is-number": "^7.0.0"
}, },
@ -1900,10 +1895,9 @@
} }
}, },
"node_modules/ws": { "node_modules/ws": {
"version": "7.5.10", "version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
"license": "MIT",
"engines": { "engines": {
"node": ">=8.3.0" "node": ">=8.3.0"
}, },

View File

@ -23,12 +23,8 @@ RSpec.describe Faraday::Error do
it { expect(subject.wrapped_exception).to be_nil } it { expect(subject.wrapped_exception).to be_nil }
it { expect(subject.response).to eq(exception) } it { expect(subject.response).to eq(exception) }
it { expect(subject.message).to eq('the server responded with status 400 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') } it { expect(subject.message).to eq('the server responded with status 400') }
if RUBY_VERSION >= '3.4' it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
else
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
end
it { expect(subject.response_status).to eq(400) } it { expect(subject.response_status).to eq(400) }
it { expect(subject.response_headers).to be_nil } it { expect(subject.response_headers).to be_nil }
it { expect(subject.response_body).to be_nil } it { expect(subject.response_body).to be_nil }
@ -65,11 +61,7 @@ RSpec.describe Faraday::Error do
it { expect(subject.wrapped_exception).to be_nil } it { expect(subject.wrapped_exception).to be_nil }
it { expect(subject.response).to eq(response) } it { expect(subject.response).to eq(response) }
it { expect(subject.message).to eq('custom message') } it { expect(subject.message).to eq('custom message') }
if RUBY_VERSION >= '3.4' it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
it { expect(subject.inspect).to eq('#<Faraday::Error response={status: 400}>') }
else
it { expect(subject.inspect).to eq('#<Faraday::Error response={:status=>400}>') }
end
it { expect(subject.response_status).to eq(400) } it { expect(subject.response_status).to eq(400) }
it { expect(subject.response_headers).to be_nil } it { expect(subject.response_headers).to be_nil }
it { expect(subject.response_body).to be_nil } it { expect(subject.response_body).to be_nil }
@ -89,87 +81,5 @@ RSpec.describe Faraday::Error do
it { expect(subject.response_headers).to eq(headers) } it { expect(subject.response_headers).to eq(headers) }
it { expect(subject.response_body).to eq(body) } it { expect(subject.response_body).to eq(body) }
end end
context 'with hash missing status key' do
let(:exception) { { body: 'error body' } }
it { expect(subject.wrapped_exception).to be_nil }
it { expect(subject.response).to eq(exception) }
it { expect(subject.message).to eq('the server responded with status - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
end
context 'with hash with status but missing request data' do
let(:exception) { { status: 404, body: 'not found' } } # missing request key
it { expect(subject.wrapped_exception).to be_nil }
it { expect(subject.response).to eq(exception) }
it { expect(subject.message).to eq('the server responded with status 404 - method and url are not available due to include_request: false on Faraday::Response::RaiseError middleware') }
end
context 'with hash with status and request but missing method in request' do
let(:exception) { { status: 404, body: 'not found', request: { url: 'http://example.com/test' } } } # missing method
it { expect(subject.wrapped_exception).to be_nil }
it { expect(subject.response).to eq(exception) }
it { expect(subject.message).to eq('the server responded with status 404 for http://example.com/test') }
end
context 'with hash with status and request but missing url in request' do
let(:exception) { { status: 404, body: 'not found', request: { method: :get } } } # missing url
it { expect(subject.wrapped_exception).to be_nil }
it { expect(subject.response).to eq(exception) }
it { expect(subject.message).to eq('the server responded with status 404 for GET ') }
end
context 'with properly formed Faraday::Env' do
# This represents the normal case - a well-formed Faraday::Env object
# with all the standard properties populated as they would be during
# a typical HTTP request/response cycle
let(:exception) { Faraday::Env.new }
before do
exception.status = 500
exception.method = :post
exception.url = URI('https://api.example.com/users')
exception.request = Faraday::RequestOptions.new
exception.response_headers = { 'content-type' => 'application/json' }
exception.response_body = '{"error": "Internal server error"}'
exception.request_headers = { 'authorization' => 'Bearer token123' }
exception.request_body = '{"name": "John"}'
end
it { expect(subject.wrapped_exception).to be_nil }
it { expect(subject.response).to eq(exception) }
it { expect(subject.message).to eq('the server responded with status 500 for POST https://api.example.com/users') }
end
context 'with Faraday::Env missing status key' do
let(:exception) { Faraday::Env.new }
before do
exception[:body] = 'error body'
# Intentionally not setting status
end
it { expect(subject.wrapped_exception).to be_nil }
it { expect(subject.response).to eq(exception) }
it { expect(subject.message).to eq('the server responded with status for ') }
end
context 'with Faraday::Env with direct method and url properties' do
let(:exception) { Faraday::Env.new }
before do
exception.status = 404
exception.method = :get
exception.url = URI('http://example.com/test')
exception[:body] = 'not found'
end
it { expect(subject.wrapped_exception).to be_nil }
it { expect(subject.response).to eq(exception) }
it { expect(subject.message).to eq('the server responded with status 404 for GET http://example.com/test') }
end
end end
end end

View File

@ -27,33 +27,6 @@ RSpec.describe Faraday::ProxyOptions do
expect(options.inspect).to eq('#<Faraday::ProxyOptions (empty)>') expect(options.inspect).to eq('#<Faraday::ProxyOptions (empty)>')
end end
it 'works with hash' do
hash = { user: 'user', password: 'pass', uri: 'http://@example.org' }
options = Faraday::ProxyOptions.from(hash)
expect(options.user).to eq('user')
expect(options.password).to eq('pass')
expect(options.uri).to be_a_kind_of(URI)
expect(options.path).to eq('')
expect(options.port).to eq(80)
expect(options.host).to eq('example.org')
expect(options.scheme).to eq('http')
expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
end
it 'works with option' do
opt_arg = { user: 'user', password: 'pass', uri: 'http://@example.org' }
option = Faraday::ConnectionOptions.from(proxy: opt_arg)
options = Faraday::ProxyOptions.from(option.proxy)
expect(options.user).to eq('user')
expect(options.password).to eq('pass')
expect(options.uri).to be_a_kind_of(URI)
expect(options.path).to eq('')
expect(options.port).to eq(80)
expect(options.host).to eq('example.org')
expect(options.scheme).to eq('http')
expect(options.inspect).to match('#<Faraday::ProxyOptions uri=')
end
it 'works with no auth' do it 'works with no auth' do
proxy = Faraday::ProxyOptions.from 'http://example.org' proxy = Faraday::ProxyOptions.from 'http://example.org'
expect(proxy.user).to be_nil expect(proxy.user).to be_nil

View File

@ -55,26 +55,6 @@ RSpec.describe Faraday::Response::Logger do
end end
end end
context 'when logger with program name' do
let(:logger) { Logger.new(string_io, progname: 'my_best_program') }
it 'logs with program name' do
conn.get '/hello'
expect(string_io.string).to match('-- my_best_program: request:')
expect(string_io.string).to match('-- my_best_program: response:')
end
end
context 'when logger without program name' do
it 'logs without program name' do
conn.get '/hello'
expect(string_io.string).to match('-- : request:')
expect(string_io.string).to match('-- : response:')
end
end
context 'with default formatter' do context 'with default formatter' do
let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) } let(:formatter) { instance_double(Faraday::Logging::Formatter, request: true, response: true, filter: []) }
@ -189,7 +169,7 @@ RSpec.describe Faraday::Response::Logger do
context 'when logging request body' do context 'when logging request body' do
let(:logger_options) { { bodies: { request: true } } } let(:logger_options) { { bodies: { request: true } } }
it 'logs only request body' do it 'log only request body' do
conn.post '/ohyes', 'name=Tamago', accept: 'text/html' conn.post '/ohyes', 'name=Tamago', accept: 'text/html'
expect(string_io.string).to match(%(name=Tamago)) expect(string_io.string).to match(%(name=Tamago))
expect(string_io.string).not_to match(%(pebbles)) expect(string_io.string).not_to match(%(pebbles))
@ -199,7 +179,7 @@ RSpec.describe Faraday::Response::Logger do
context 'when logging response body' do context 'when logging response body' do
let(:logger_options) { { bodies: { response: true } } } let(:logger_options) { { bodies: { response: true } } }
it 'logs only response body' do it 'log only response body' do
conn.post '/ohyes', 'name=Hamachi', accept: 'text/html' conn.post '/ohyes', 'name=Hamachi', accept: 'text/html'
expect(string_io.string).to match(%(pebbles)) expect(string_io.string).to match(%(pebbles))
expect(string_io.string).not_to match(%(name=Hamachi)) expect(string_io.string).not_to match(%(name=Hamachi))
@ -209,13 +189,13 @@ RSpec.describe Faraday::Response::Logger do
context 'when logging request and response bodies' do context 'when logging request and response bodies' do
let(:logger_options) { { bodies: true } } let(:logger_options) { { bodies: true } }
it 'logs request and response body' do it 'log request and response body' do
conn.post '/ohyes', 'name=Ebi', accept: 'text/html' conn.post '/ohyes', 'name=Ebi', accept: 'text/html'
expect(string_io.string).to match(%(name=Ebi)) expect(string_io.string).to match(%(name=Ebi))
expect(string_io.string).to match(%(pebbles)) expect(string_io.string).to match(%(pebbles))
end end
it 'logs response body object' do it 'log response body object' do
conn.get '/rubbles', nil, accept: 'text/html' conn.get '/rubbles', nil, accept: 'text/html'
expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n)) expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n))
end end
@ -228,21 +208,6 @@ RSpec.describe Faraday::Response::Logger do
end end
end end
context 'when bodies are logged by default' do
before do
described_class.default_options = { bodies: true }
end
it 'logs response body' do
conn.post '/ohai'
expect(string_io.string).to match(%(fred))
end
after do
described_class.default_options = { bodies: false }
end
end
context 'when logging errors' do context 'when logging errors' do
let(:logger_options) { { errors: true } } let(:logger_options) { { errors: true } }

View File

@ -28,7 +28,7 @@ RSpec.describe Faraday::Response::RaiseError do
it 'raises Faraday::BadRequestError for 400 responses' do it 'raises Faraday::BadRequestError for 400 responses' do
expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex| expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError) do |ex|
expect(ex.message).to eq('the server responded with status 400 for GET http:/bad-request') expect(ex.message).to eq('the server responded with status 400')
expect(ex.response[:headers]['X-Reason']).to eq('because') expect(ex.response[:headers]['X-Reason']).to eq('because')
expect(ex.response[:status]).to eq(400) expect(ex.response[:status]).to eq(400)
expect(ex.response_status).to eq(400) expect(ex.response_status).to eq(400)
@ -39,7 +39,7 @@ RSpec.describe Faraday::Response::RaiseError do
it 'raises Faraday::UnauthorizedError for 401 responses' do it 'raises Faraday::UnauthorizedError for 401 responses' do
expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex| expect { conn.get('unauthorized') }.to raise_error(Faraday::UnauthorizedError) do |ex|
expect(ex.message).to eq('the server responded with status 401 for GET http:/unauthorized') expect(ex.message).to eq('the server responded with status 401')
expect(ex.response[:headers]['X-Reason']).to eq('because') expect(ex.response[:headers]['X-Reason']).to eq('because')
expect(ex.response[:status]).to eq(401) expect(ex.response[:status]).to eq(401)
expect(ex.response_status).to eq(401) expect(ex.response_status).to eq(401)
@ -50,7 +50,7 @@ RSpec.describe Faraday::Response::RaiseError do
it 'raises Faraday::ForbiddenError for 403 responses' do it 'raises Faraday::ForbiddenError for 403 responses' do
expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex| expect { conn.get('forbidden') }.to raise_error(Faraday::ForbiddenError) do |ex|
expect(ex.message).to eq('the server responded with status 403 for GET http:/forbidden') expect(ex.message).to eq('the server responded with status 403')
expect(ex.response[:headers]['X-Reason']).to eq('because') expect(ex.response[:headers]['X-Reason']).to eq('because')
expect(ex.response[:status]).to eq(403) expect(ex.response[:status]).to eq(403)
expect(ex.response_status).to eq(403) expect(ex.response_status).to eq(403)
@ -61,7 +61,7 @@ RSpec.describe Faraday::Response::RaiseError do
it 'raises Faraday::ResourceNotFound for 404 responses' do it 'raises Faraday::ResourceNotFound for 404 responses' do
expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex| expect { conn.get('not-found') }.to raise_error(Faraday::ResourceNotFound) do |ex|
expect(ex.message).to eq('the server responded with status 404 for GET http:/not-found') expect(ex.message).to eq('the server responded with status 404')
expect(ex.response[:headers]['X-Reason']).to eq('because') expect(ex.response[:headers]['X-Reason']).to eq('because')
expect(ex.response[:status]).to eq(404) expect(ex.response[:status]).to eq(404)
expect(ex.response_status).to eq(404) expect(ex.response_status).to eq(404)
@ -83,7 +83,7 @@ RSpec.describe Faraday::Response::RaiseError do
it 'raises Faraday::RequestTimeoutError for 408 responses' do it 'raises Faraday::RequestTimeoutError for 408 responses' do
expect { conn.get('request-timeout') }.to raise_error(Faraday::RequestTimeoutError) do |ex| expect { conn.get('request-timeout') }.to raise_error(Faraday::RequestTimeoutError) do |ex|
expect(ex.message).to eq('the server responded with status 408 for GET http:/request-timeout') expect(ex.message).to eq('the server responded with status 408')
expect(ex.response[:headers]['X-Reason']).to eq('because') expect(ex.response[:headers]['X-Reason']).to eq('because')
expect(ex.response[:status]).to eq(408) expect(ex.response[:status]).to eq(408)
expect(ex.response_status).to eq(408) expect(ex.response_status).to eq(408)
@ -94,7 +94,7 @@ RSpec.describe Faraday::Response::RaiseError do
it 'raises Faraday::ConflictError for 409 responses' do it 'raises Faraday::ConflictError for 409 responses' do
expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex| expect { conn.get('conflict') }.to raise_error(Faraday::ConflictError) do |ex|
expect(ex.message).to eq('the server responded with status 409 for GET http:/conflict') expect(ex.message).to eq('the server responded with status 409')
expect(ex.response[:headers]['X-Reason']).to eq('because') expect(ex.response[:headers]['X-Reason']).to eq('because')
expect(ex.response[:status]).to eq(409) expect(ex.response[:status]).to eq(409)
expect(ex.response_status).to eq(409) expect(ex.response_status).to eq(409)
@ -105,7 +105,7 @@ RSpec.describe Faraday::Response::RaiseError do
it 'raises Faraday::UnprocessableEntityError for 422 responses' do it 'raises Faraday::UnprocessableEntityError for 422 responses' do
expect { conn.get('unprocessable-entity') }.to raise_error(Faraday::UnprocessableEntityError) do |ex| expect { conn.get('unprocessable-entity') }.to raise_error(Faraday::UnprocessableEntityError) do |ex|
expect(ex.message).to eq('the server responded with status 422 for GET http:/unprocessable-entity') expect(ex.message).to eq('the server responded with status 422')
expect(ex.response[:headers]['X-Reason']).to eq('because') expect(ex.response[:headers]['X-Reason']).to eq('because')
expect(ex.response[:status]).to eq(422) expect(ex.response[:status]).to eq(422)
expect(ex.response_status).to eq(422) expect(ex.response_status).to eq(422)
@ -116,7 +116,7 @@ RSpec.describe Faraday::Response::RaiseError do
it 'raises Faraday::TooManyRequestsError for 429 responses' do it 'raises Faraday::TooManyRequestsError for 429 responses' do
expect { conn.get('too-many-requests') }.to raise_error(Faraday::TooManyRequestsError) do |ex| expect { conn.get('too-many-requests') }.to raise_error(Faraday::TooManyRequestsError) do |ex|
expect(ex.message).to eq('the server responded with status 429 for GET http:/too-many-requests') expect(ex.message).to eq('the server responded with status 429')
expect(ex.response[:headers]['X-Reason']).to eq('because') expect(ex.response[:headers]['X-Reason']).to eq('because')
expect(ex.response[:status]).to eq(429) expect(ex.response[:status]).to eq(429)
expect(ex.response_status).to eq(429) expect(ex.response_status).to eq(429)
@ -138,7 +138,7 @@ RSpec.describe Faraday::Response::RaiseError do
it 'raises Faraday::ClientError for other 4xx responses' do it 'raises Faraday::ClientError for other 4xx responses' do
expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex| expect { conn.get('4xx') }.to raise_error(Faraday::ClientError) do |ex|
expect(ex.message).to eq('the server responded with status 499 for GET http:/4xx') expect(ex.message).to eq('the server responded with status 499')
expect(ex.response[:headers]['X-Reason']).to eq('because') expect(ex.response[:headers]['X-Reason']).to eq('because')
expect(ex.response[:status]).to eq(499) expect(ex.response[:status]).to eq(499)
expect(ex.response_status).to eq(499) expect(ex.response_status).to eq(499)
@ -149,7 +149,7 @@ RSpec.describe Faraday::Response::RaiseError do
it 'raises Faraday::ServerError for 500 responses' do it 'raises Faraday::ServerError for 500 responses' do
expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex| expect { conn.get('server-error') }.to raise_error(Faraday::ServerError) do |ex|
expect(ex.message).to eq('the server responded with status 500 for GET http:/server-error') expect(ex.message).to eq('the server responded with status 500')
expect(ex.response[:headers]['X-Error']).to eq('bailout') expect(ex.response[:headers]['X-Error']).to eq('bailout')
expect(ex.response[:status]).to eq(500) expect(ex.response[:status]).to eq(500)
expect(ex.response_status).to eq(500) expect(ex.response_status).to eq(500)
@ -252,24 +252,4 @@ RSpec.describe Faraday::Response::RaiseError do
end end
end end
end end
describe 'allowing certain status codes' do
let(:conn) do
Faraday.new do |b|
b.response :raise_error, allowed_statuses: [404]
b.adapter :test do |stub|
stub.get('bad-request') { [400, { 'X-Reason' => 'because' }, 'keep looking'] }
stub.get('not-found') { [404, { 'X-Reason' => 'because' }, 'keep looking'] }
end
end
end
it 'raises an error for status codes that are not explicitly allowed' do
expect { conn.get('bad-request') }.to raise_error(Faraday::BadRequestError)
end
it 'does not raise an error for allowed status codes' do
expect { conn.get('not-found') }.not_to raise_error
end
end
end end

View File

@ -103,9 +103,7 @@ RSpec.describe Faraday::Utils do
version: '2', version: '2',
min_version: nil, min_version: nil,
max_version: nil, max_version: nil,
verify_hostname: nil, verify_hostname: nil
hostname: nil,
ciphers: nil
} }
end end