mirror of
https://github.com/lostisland/faraday.git
synced 2025-10-04 00:02:03 -04:00
Begin v2 release (#1297)
* Update UPGRADING.md to highlight adapters being moved out * Cleanup tests * Cleanup Gemfile * Update CI
This commit is contained in:
parent
a089fb83fa
commit
6333b73d13
33
.github/workflows/ci.yml
vendored
33
.github/workflows/ci.yml
vendored
@ -4,7 +4,7 @@ on:
|
||||
pull_request:
|
||||
|
||||
push:
|
||||
branches: [main, 0.1x]
|
||||
branches: [main, 1.x, 0.1x]
|
||||
|
||||
env:
|
||||
GIT_COMMIT_SHA: ${{ github.sha }}
|
||||
@ -14,27 +14,24 @@ env:
|
||||
jobs:
|
||||
linting:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BUNDLE_WITH: lint
|
||||
BUNDLE_WITHOUT: development:test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Ruby 2.7
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.7
|
||||
bundler-cache: true
|
||||
|
||||
- name: Rubocop
|
||||
run: |
|
||||
gem install bundler
|
||||
bundle config set without 'development test'
|
||||
bundle config set with 'lint'
|
||||
bundle install
|
||||
bundle exec rubocop --format progress
|
||||
run: bundle exec rubocop --format progress
|
||||
|
||||
- name: Yard-Junk
|
||||
run: |
|
||||
gem install yard-junk --no-document
|
||||
yard-junk --path lib
|
||||
run: bundle exec yard-junk --path lib
|
||||
|
||||
build:
|
||||
needs: [linting]
|
||||
@ -43,22 +40,14 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ruby: ['2.4', '2.5', '2.6', '2.7', '3.0']
|
||||
ruby: ['2.6', '2.7', '3.0']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libcurl4-openssl-dev
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
gem install bundler -v '<2'
|
||||
bundle install --jobs 4 --retry 3
|
||||
bundler-cache: true
|
||||
|
||||
- name: Test
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
|
18
.github/workflows/publish.yml
vendored
18
.github/workflows/publish.yml
vendored
@ -10,20 +10,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Ruby 2.6
|
||||
- name: Set up Ruby 2.7
|
||||
uses: actions/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.6.x
|
||||
ruby-version: 2.7
|
||||
|
||||
- name: Publish to RubyGems
|
||||
run: |
|
||||
mkdir -p $HOME/.gem
|
||||
touch $HOME/.gem/credentials
|
||||
chmod 0600 $HOME/.gem/credentials
|
||||
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
||||
gem build faraday.gemspec
|
||||
gem push faraday-*.gem
|
||||
env:
|
||||
GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_AUTH_TOKEN }}
|
||||
uses: dawidd6/action-publish-gem@v1
|
||||
with:
|
||||
api_key: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
|
||||
|
33
Gemfile
33
Gemfile
@ -2,39 +2,28 @@
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
ruby RUBY_VERSION
|
||||
|
||||
# Even though we don't officially support JRuby, this dependency makes Faraday
|
||||
# compatible with it, so we're leaving it in for jruby users to use it.
|
||||
gem 'jruby-openssl', '~> 0.10.7', platforms: :jruby
|
||||
|
||||
group :development, :test do
|
||||
gem 'coveralls_reborn', require: false
|
||||
gem 'multipart-parser'
|
||||
gem 'pry'
|
||||
gem 'rack', '~> 2.2'
|
||||
gem 'rake'
|
||||
gem 'rspec', '~> 3.7'
|
||||
gem 'rspec_junit_formatter', '~> 0.4'
|
||||
gem 'simplecov'
|
||||
gem 'webmock', '~> 3.4'
|
||||
end
|
||||
|
||||
group :lint, :development do
|
||||
group :development, :lint do
|
||||
gem 'rubocop', '~> 0.90.0'
|
||||
gem 'rubocop-inclusivity', '~> 1.0'
|
||||
gem 'rubocop-packaging', '~> 0.5'
|
||||
gem 'rubocop-performance', '~> 1.0'
|
||||
end
|
||||
|
||||
group :test, :development do
|
||||
gem 'coveralls_reborn', require: false
|
||||
gem 'em-http-request', '>= 1.1', require: 'em-http', platform: :ruby
|
||||
gem 'em-synchrony', '>= 1.0.3', require: %w[em-synchrony em-synchrony/em-http], platform: :ruby
|
||||
gem 'excon', '>= 0.27.4'
|
||||
gem 'httpclient', '>= 2.2'
|
||||
gem 'multipart-parser'
|
||||
# TODO: remove this once v4 is released
|
||||
options = (RUBY_VERSION.start_with?('3') ? { github: 'grosser/net-http-persistent', branch: 'grosser/spec' } : {})
|
||||
gem 'net-http-persistent', '>= 3.0', **options
|
||||
gem 'patron', '>= 0.4.2', platforms: :ruby
|
||||
gem 'rack-test', '>= 0.6', require: 'rack/test'
|
||||
gem 'rspec', '~> 3.7'
|
||||
gem 'rspec_junit_formatter', '~> 0.4'
|
||||
gem 'simplecov'
|
||||
gem 'typhoeus', '~> 1.4'
|
||||
gem 'webmock', '~> 3.4'
|
||||
gem 'yard-junk'
|
||||
end
|
||||
|
||||
gemspec
|
||||
|
@ -16,10 +16,10 @@ Need more details? See the [Faraday API Documentation][apidoc] to see how it wor
|
||||
|
||||
## Supported Ruby versions
|
||||
|
||||
This library aims to support and is [tested against][actions] the following Ruby
|
||||
implementations:
|
||||
|
||||
* Ruby 2.4+
|
||||
This library aims to support and is [tested against][actions] the currently officially supported Ruby
|
||||
implementations. This means that, even without a major release, we could add or drop support for Ruby versions,
|
||||
following their [EOL](https://endoflife.date/ruby).
|
||||
Currently that means we support Ruby 2.6+
|
||||
|
||||
If something doesn't work on one of these Ruby versions, it's a bug.
|
||||
|
||||
|
32
UPGRADING.md
32
UPGRADING.md
@ -1,6 +1,38 @@
|
||||
## Faraday 2.0
|
||||
|
||||
### Adapters have moved!
|
||||
|
||||
With this release, we've officially moved all adapters out of Faraday.
|
||||
What that means, is that they won't be available out-of-the-box anymore,
|
||||
and you'll instead need to add them to your Gemfile.
|
||||
|
||||
#### Why was this decision made?
|
||||
|
||||
We've taken this decision for the following technical reasons:
|
||||
|
||||
* We wanted the Faraday gem to focus on providing a clean, standardized, middleware-stack-based API.
|
||||
* We wanted to free the core team from maintaining all the different adapters, relying more on the community to
|
||||
maintain them based on the broad interest. This will allow the core team to focus on implementing features
|
||||
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
|
||||
by having some of them included with the gem and others available externally.
|
||||
* 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
|
||||
any necessary dependency, without having to rely on the developer to do so.
|
||||
|
||||
#### So what will this mean for me?
|
||||
|
||||
We did our best to make this transition as painless as possible for you, so here is what we did.
|
||||
|
||||
* All adapters have already been moved out and released as separate gems.
|
||||
They've then been re-added into Faraday's dependencies so that you wouldn't notice.
|
||||
This means that immediately after v2.0 will be released, all the adapters that were previously available will be
|
||||
already compatible with Faraday 2.0!
|
||||
* We've setup an [Awesome Faraday](https://github.com/lostisland/awesome-faraday) repository, where you can find and discover adapters.
|
||||
We also highlighted their unique features and level of compliance with Faraday's features.
|
||||
|
||||
### Others
|
||||
|
||||
* Rename `Faraday::Request#method` to `#http_method`.
|
||||
|
||||
## Faraday 1.0
|
||||
|
@ -27,17 +27,6 @@ require 'faraday/error'
|
||||
require 'faraday/file_part'
|
||||
require 'faraday/param_part'
|
||||
|
||||
unless defined?(JRUBY_VERSION)
|
||||
require 'faraday/em_http'
|
||||
require 'faraday/em_synchrony'
|
||||
end
|
||||
require 'faraday/excon'
|
||||
require 'faraday/httpclient'
|
||||
require 'faraday/net_http'
|
||||
require 'faraday/net_http_persistent'
|
||||
require 'faraday/patron'
|
||||
require 'faraday/rack'
|
||||
|
||||
# This is the main namespace for Faraday.
|
||||
#
|
||||
# It provides methods to create {Connection} objects, and HTTP-related
|
||||
|
@ -1,49 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
unless defined?(JRUBY_VERSION)
|
||||
RSpec.describe Faraday::Adapter::EMHttp do
|
||||
features :request_body_on_query_methods, :reason_phrase_parse, :trace_method,
|
||||
:skip_response_body_on_head, :parallel, :local_socket_binding
|
||||
|
||||
it_behaves_like 'an adapter'
|
||||
|
||||
it 'allows to provide adapter specific configs' do
|
||||
url = URI('https://example.com:1234')
|
||||
adapter = described_class.new nil, inactivity_timeout: 20
|
||||
req = adapter.create_request(url: url, request: {})
|
||||
|
||||
expect(req.connopts.inactivity_timeout).to eq(20)
|
||||
end
|
||||
|
||||
context 'Options' do
|
||||
let(:request) { Faraday::RequestOptions.new }
|
||||
let(:env) { { request: request } }
|
||||
let(:options) { {} }
|
||||
let(:adapter) { Faraday::Adapter::EMHttp.new }
|
||||
|
||||
it 'configures timeout' do
|
||||
request.timeout = 5
|
||||
adapter.configure_timeout(options, env)
|
||||
expect(options[:inactivity_timeout]).to eq(5)
|
||||
expect(options[:connect_timeout]).to eq(5)
|
||||
end
|
||||
|
||||
it 'configures timeout and open_timeout' do
|
||||
request.timeout = 5
|
||||
request.open_timeout = 1
|
||||
adapter.configure_timeout(options, env)
|
||||
expect(options[:inactivity_timeout]).to eq(5)
|
||||
expect(options[:connect_timeout]).to eq(1)
|
||||
end
|
||||
|
||||
it 'configures all timeout settings' do
|
||||
request.timeout = 5
|
||||
request.read_timeout = 3
|
||||
request.open_timeout = 1
|
||||
adapter.configure_timeout(options, env)
|
||||
expect(options[:inactivity_timeout]).to eq(3)
|
||||
expect(options[:connect_timeout]).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,18 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
unless defined?(JRUBY_VERSION)
|
||||
RSpec.describe Faraday::Adapter::EMSynchrony do
|
||||
features :request_body_on_query_methods, :reason_phrase_parse,
|
||||
:skip_response_body_on_head, :parallel, :local_socket_binding
|
||||
|
||||
it_behaves_like 'an adapter'
|
||||
|
||||
it 'allows to provide adapter specific configs' do
|
||||
url = URI('https://example.com:1234')
|
||||
adapter = described_class.new nil, inactivity_timeout: 20
|
||||
req = adapter.create_request(url: url, request: {})
|
||||
|
||||
expect(req.connopts.inactivity_timeout).to eq(20)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,49 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Faraday::Adapter::Excon do
|
||||
features :request_body_on_query_methods, :reason_phrase_parse, :trace_method
|
||||
|
||||
it_behaves_like 'an adapter'
|
||||
|
||||
it 'allows to provide adapter specific configs' do
|
||||
url = URI('https://example.com:1234')
|
||||
|
||||
adapter = described_class.new(nil, debug_request: true)
|
||||
|
||||
conn = adapter.build_connection(url: url)
|
||||
|
||||
expect(conn.data[:debug_request]).to be_truthy
|
||||
end
|
||||
|
||||
context 'config' do
|
||||
let(:adapter) { Faraday::Adapter::Excon.new }
|
||||
let(:request) { Faraday::RequestOptions.new }
|
||||
let(:uri) { URI.parse('https://example.com') }
|
||||
let(:env) { { request: request, url: uri } }
|
||||
|
||||
it 'sets timeout' do
|
||||
request.timeout = 5
|
||||
options = adapter.send(:opts_from_env, env)
|
||||
expect(options[:read_timeout]).to eq(5)
|
||||
expect(options[:write_timeout]).to eq(5)
|
||||
expect(options[:connect_timeout]).to eq(5)
|
||||
end
|
||||
|
||||
it 'sets timeout and open_timeout' do
|
||||
request.timeout = 5
|
||||
request.open_timeout = 3
|
||||
options = adapter.send(:opts_from_env, env)
|
||||
expect(options[:read_timeout]).to eq(5)
|
||||
expect(options[:write_timeout]).to eq(5)
|
||||
expect(options[:connect_timeout]).to eq(3)
|
||||
end
|
||||
|
||||
it 'sets open_timeout' do
|
||||
request.open_timeout = 3
|
||||
options = adapter.send(:opts_from_env, env)
|
||||
expect(options[:read_timeout]).to eq(nil)
|
||||
expect(options[:write_timeout]).to eq(nil)
|
||||
expect(options[:connect_timeout]).to eq(3)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,73 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Faraday::Adapter::HTTPClient do
|
||||
# ruby gem defaults for testing purposes
|
||||
HTTPCLIENT_OPEN = 60
|
||||
HTTPCLIENT_READ = 60
|
||||
HTTPCLIENT_WRITE = 120
|
||||
|
||||
features :request_body_on_query_methods, :reason_phrase_parse, :compression,
|
||||
:trace_method, :local_socket_binding
|
||||
|
||||
it_behaves_like 'an adapter'
|
||||
|
||||
it 'allows to provide adapter specific configs' do
|
||||
adapter = described_class.new do |client|
|
||||
client.keep_alive_timeout = 20
|
||||
client.ssl_config.timeout = 25
|
||||
end
|
||||
|
||||
client = adapter.build_connection(url: URI.parse('https://example.com'))
|
||||
expect(client.keep_alive_timeout).to eq(20)
|
||||
expect(client.ssl_config.timeout).to eq(25)
|
||||
end
|
||||
|
||||
context 'Options' do
|
||||
let(:request) { Faraday::RequestOptions.new }
|
||||
let(:env) { { request: request } }
|
||||
let(:options) { {} }
|
||||
let(:adapter) { Faraday::Adapter::HTTPClient.new }
|
||||
let(:client) { adapter.connection(url: URI.parse('https://example.com')) }
|
||||
|
||||
it 'configures timeout' do
|
||||
assert_default_timeouts!
|
||||
|
||||
request.timeout = 5
|
||||
adapter.configure_timeouts(client, request)
|
||||
|
||||
expect(client.connect_timeout).to eq(5)
|
||||
expect(client.send_timeout).to eq(5)
|
||||
expect(client.receive_timeout).to eq(5)
|
||||
end
|
||||
|
||||
it 'configures open timeout' do
|
||||
assert_default_timeouts!
|
||||
|
||||
request.open_timeout = 1
|
||||
adapter.configure_timeouts(client, request)
|
||||
|
||||
expect(client.connect_timeout).to eq(1)
|
||||
expect(client.send_timeout).to eq(HTTPCLIENT_WRITE)
|
||||
expect(client.receive_timeout).to eq(HTTPCLIENT_READ)
|
||||
end
|
||||
|
||||
it 'configures multiple timeouts' do
|
||||
assert_default_timeouts!
|
||||
|
||||
request.open_timeout = 1
|
||||
request.write_timeout = 10
|
||||
request.read_timeout = 5
|
||||
adapter.configure_timeouts(client, request)
|
||||
|
||||
expect(client.connect_timeout).to eq(1)
|
||||
expect(client.send_timeout).to eq(10)
|
||||
expect(client.receive_timeout).to eq(5)
|
||||
end
|
||||
|
||||
def assert_default_timeouts!
|
||||
expect(client.connect_timeout).to eq(HTTPCLIENT_OPEN)
|
||||
expect(client.send_timeout).to eq(HTTPCLIENT_WRITE)
|
||||
expect(client.receive_timeout).to eq(HTTPCLIENT_READ)
|
||||
end
|
||||
end
|
||||
end
|
@ -1,64 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'faraday/net_http'
|
||||
|
||||
# Even though Faraday::Adapter::NetHttp is not shipped with Faraday anymore,
|
||||
# this is still useful to test `it_behaves_like 'an adapter'` shared examples.
|
||||
RSpec.describe Faraday::Adapter::NetHttp do
|
||||
features :request_body_on_query_methods, :reason_phrase_parse, :compression, :streaming, :trace_method
|
||||
|
||||
it_behaves_like 'an adapter'
|
||||
|
||||
context 'checking http' do
|
||||
let(:url) { URI('http://example.com') }
|
||||
let(:adapter) { described_class.new }
|
||||
let(:http) { adapter.send(:connection, url: url, request: {}) }
|
||||
|
||||
it { expect(http.port).to eq(80) }
|
||||
|
||||
it 'sets max_retries to 0' do
|
||||
adapter.send(:configure_request, http, {})
|
||||
|
||||
expect(http.max_retries).to eq(0) if http.respond_to?(:max_retries=)
|
||||
end
|
||||
|
||||
it 'supports write_timeout' do
|
||||
adapter.send(:configure_request, http, write_timeout: 10)
|
||||
|
||||
expect(http.write_timeout).to eq(10) if http.respond_to?(:write_timeout=)
|
||||
end
|
||||
|
||||
it 'supports open_timeout' do
|
||||
adapter.send(:configure_request, http, open_timeout: 10)
|
||||
|
||||
expect(http.open_timeout).to eq(10)
|
||||
end
|
||||
|
||||
it 'supports read_timeout' do
|
||||
adapter.send(:configure_request, http, read_timeout: 10)
|
||||
|
||||
expect(http.read_timeout).to eq(10)
|
||||
end
|
||||
|
||||
context 'with https url' do
|
||||
let(:url) { URI('https://example.com') }
|
||||
|
||||
it { expect(http.port).to eq(443) }
|
||||
end
|
||||
|
||||
context 'with http url including port' do
|
||||
let(:url) { URI('https://example.com:1234') }
|
||||
|
||||
it { expect(http.port).to eq(1234) }
|
||||
end
|
||||
|
||||
context 'with custom adapter config' do
|
||||
let(:adapter) do
|
||||
described_class.new do |http|
|
||||
http.continue_timeout = 123
|
||||
end
|
||||
end
|
||||
|
||||
it do
|
||||
adapter.send(:configure_request, http, {})
|
||||
expect(http.continue_timeout).to eq(123)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,18 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Faraday::Adapter::Patron, unless: defined?(JRUBY_VERSION) do
|
||||
features :reason_phrase_parse
|
||||
|
||||
it_behaves_like 'an adapter'
|
||||
|
||||
it 'allows to provide adapter specific configs' do
|
||||
conn = Faraday.new do |f|
|
||||
f.adapter :patron do |session|
|
||||
session.max_redirects = 10
|
||||
raise 'Configuration block called'
|
||||
end
|
||||
end
|
||||
|
||||
expect { conn.get('/') }.to raise_error(RuntimeError, 'Configuration block called')
|
||||
end
|
||||
end
|
@ -1,8 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Faraday::Adapter::Rack do
|
||||
features :request_body_on_query_methods, :trace_method,
|
||||
:skip_response_body_on_head
|
||||
|
||||
it_behaves_like 'an adapter', adapter_options: WebmockRackApp.new
|
||||
end
|
@ -1,7 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe Faraday::Adapter::Typhoeus do
|
||||
features :request_body_on_query_methods, :parallel, :trace_method
|
||||
|
||||
it_behaves_like 'an adapter'
|
||||
end
|
@ -1,68 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Rack app used to test the Rack adapter.
|
||||
# Uses Webmock to check if requests are registered, in which case it returns
|
||||
# the registered response.
|
||||
class WebmockRackApp
|
||||
def call(env)
|
||||
req_signature = WebMock::RequestSignature.new(
|
||||
req_method(env),
|
||||
req_uri(env),
|
||||
body: req_body(env),
|
||||
headers: req_headers(env)
|
||||
)
|
||||
|
||||
WebMock::RequestRegistry
|
||||
.instance
|
||||
.requested_signatures
|
||||
.put(req_signature)
|
||||
|
||||
process_response(req_signature)
|
||||
end
|
||||
|
||||
def req_method(env)
|
||||
env['REQUEST_METHOD'].downcase.to_sym
|
||||
end
|
||||
|
||||
def req_uri(env)
|
||||
scheme = env['rack.url_scheme']
|
||||
host = env['SERVER_NAME']
|
||||
port = env['SERVER_PORT']
|
||||
path = env['PATH_INFO']
|
||||
query = env['QUERY_STRING']
|
||||
|
||||
url = +"#{scheme}://#{host}:#{port}#{path}"
|
||||
url += "?#{query}" if query
|
||||
|
||||
uri = WebMock::Util::URI.heuristic_parse(url)
|
||||
uri.path = uri.normalized_path.gsub('[^:]//', '/')
|
||||
uri
|
||||
end
|
||||
|
||||
def req_headers(env)
|
||||
http_headers = env.select { |k, _| k.start_with?('HTTP_') }
|
||||
.map { |k, v| [k[5..-1], v] }
|
||||
.to_h
|
||||
|
||||
special_headers = Faraday::Adapter::Rack::SPECIAL_HEADERS
|
||||
http_headers.merge(env.select { |k, _| special_headers.include?(k) })
|
||||
end
|
||||
|
||||
def req_body(env)
|
||||
env['rack.input'].read
|
||||
end
|
||||
|
||||
def process_response(req_signature)
|
||||
res = WebMock::StubRegistry.instance.response_for_request(req_signature)
|
||||
|
||||
if res.nil? && req_signature.uri.host == 'localhost'
|
||||
raise Faraday::ConnectionFailed, 'Trying to connect to localhost'
|
||||
end
|
||||
|
||||
raise WebMock::NetConnectNotAllowedError, req_signature unless res
|
||||
|
||||
raise Faraday::TimeoutError if res.should_timeout
|
||||
|
||||
[res.status[0], res.headers || {}, [res.body || '']]
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user