Compare commits
270 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d099fafd65 | ||
|
cf32578f25 | ||
|
e76e60d3c0 | ||
|
674fc1583f | ||
|
da86ebae9c | ||
|
ad8fe1e89a | ||
|
1ddd281893 | ||
|
976369857e | ||
|
64e8a2bdb1 | ||
|
bbaa093dbc | ||
|
fa9424b05a | ||
|
4018769a30 | ||
|
b5a02d7300 | ||
|
b63eb9121f | ||
|
77204cc7e8 | ||
|
919dc8fdb3 | ||
|
064a54be6c | ||
|
cd1c44a4aa | ||
|
1551c32371 | ||
|
a9cf00425e | ||
|
529b5b043e | ||
|
b7b2bc19e9 | ||
|
f9f4ce5bc1 | ||
|
93ef9e0ea9 | ||
|
fff02307ac | ||
|
59c5003ceb | ||
|
98d5adf924 | ||
|
9e5c8a113f | ||
|
9fcff671ff | ||
|
3170e7df6f | ||
|
f208ffcc9a | ||
|
9056eccea6 | ||
|
99228e4743 | ||
|
9cdc025759 | ||
|
3835b48d80 | ||
|
3efc0a8982 | ||
|
f27f7ab801 | ||
|
051a635f4b | ||
|
4860f75372 | ||
|
073faf7539 | ||
|
18524c6e89 | ||
|
d51c392c2c | ||
|
d83a2818e7 | ||
|
1958cb1ce2 | ||
|
d7d5a6a36f | ||
|
5996054fd4 | ||
|
d8bfca25fa | ||
|
4abafa5c66 | ||
|
89107f9889 | ||
|
b5245081d9 | ||
|
04515f38b3 | ||
|
6933e9b70f | ||
|
6d82d716c2 | ||
|
7dc694150d | ||
|
c9cc1b30ec | ||
|
c0540b7ba3 | ||
|
87e655f306 | ||
|
cd2cdfd446 | ||
|
f56e9387c8 | ||
|
18154c8332 | ||
|
4b34b509fe | ||
|
d820a58314 | ||
|
cc5d607766 | ||
|
ceb01e42e8 | ||
|
074506e67c | ||
|
898f203584 | ||
|
f0f549d7ef | ||
|
caa4ff42f8 | ||
|
13732f7ff2 | ||
|
8cbfd758c2 | ||
|
9487833b42 | ||
|
7e12133b92 | ||
|
1206b3917c | ||
|
d0e0436e24 | ||
|
25da081f59 | ||
|
1e81e2c71a | ||
|
b8e2e454df | ||
|
09a635f30e | ||
|
fa6075c0f9 | ||
|
53c7b499db | ||
|
639e1b52e7 | ||
|
342e7e65cd | ||
|
3e27447a80 | ||
|
63ba2aaf0c | ||
|
5fccc20854 | ||
|
28a097f756 | ||
|
5ccddaafef | ||
|
ea30bd0b54 | ||
|
230fa1b1f5 | ||
|
7ce686503a | ||
|
26168c4ce4 | ||
|
63c38f7716 | ||
|
e88b55a3d4 | ||
|
1eca6a987d | ||
|
0580f101ce | ||
|
30c4205958 | ||
|
c88fa65e07 | ||
|
228f660a92 | ||
|
17d586cb2f | ||
|
15788115f5 | ||
|
1518a984c0 | ||
|
d1743e6476 | ||
|
3af5211900 | ||
|
1e3e4e056d | ||
|
83676df431 | ||
|
baa7b2724a | ||
|
b47e22bf5d | ||
|
2824eaf968 | ||
|
990799a850 | ||
|
be98e8ef6f | ||
|
b54e7aefcc | ||
|
de709cda8d | ||
|
e0ce4a4d79 | ||
|
86298d0c45 | ||
|
d40f3dbb99 | ||
|
819cfe7f24 | ||
|
98947e6853 | ||
|
6d37ef7e3c | ||
|
111d354b7f | ||
|
75dfbd6275 | ||
|
450b0d9ff8 | ||
|
39f1b35db6 | ||
|
f6c38689c6 | ||
|
2b9a6241c6 | ||
|
cc7e7bcea9 | ||
|
f26715c9f3 | ||
|
bf3ed115fa | ||
|
f77d3a18ab | ||
|
a384efbc72 | ||
|
85577e2613 | ||
|
0e16de8e5a | ||
|
16934978e8 | ||
|
8d7e5a7e0f | ||
|
a34861a3b0 | ||
|
20a5d573f9 | ||
|
86aa000b2d | ||
|
c02a104469 | ||
|
9a16831147 | ||
|
1914d594c4 | ||
|
3ee9c81335 | ||
|
c6668ef8a8 | ||
|
4ad9c1e921 | ||
|
8af0c875bc | ||
|
2862ae6212 | ||
|
12bab73ec3 | ||
|
93affec586 | ||
|
5d28006566 | ||
|
ebdcee2b6e | ||
|
26a35fd76a | ||
|
36916f0759 | ||
|
4024f4d402 | ||
|
fe6e71bbe0 | ||
|
e1dbdf065e | ||
|
7926e3aa89 | ||
|
4816043101 | ||
|
b3b922949d | ||
|
89b1fe3da9 | ||
|
43d6797770 | ||
|
5e01af3828 | ||
|
646c30b8cb | ||
|
591b58fc06 | ||
|
1ddf062331 | ||
|
1a5b794b88 | ||
|
8a6a12100d | ||
|
6799f5852d | ||
|
5d4c1fb962 | ||
|
af116aa0a3 | ||
|
7f444c3971 | ||
|
69e88b4d52 | ||
|
cfbea91a69 | ||
|
fecc0a24cb | ||
|
607a725719 | ||
|
9d4486616a | ||
|
d420a12a57 | ||
|
fcb2003178 | ||
|
71a70f1d8c | ||
|
6ea010be93 | ||
|
257268e0c8 | ||
|
a2b5f7f3a8 | ||
|
56d58442e5 | ||
|
24eafaa99a | ||
|
24b3fd36e6 | ||
|
8f00640dd5 | ||
|
fc11225474 | ||
|
f003443acc | ||
|
b2390ec2b3 | ||
|
3fc35dac23 | ||
|
948274e25e | ||
|
46f3b678f0 | ||
|
85dfdf824c | ||
|
33563e3d63 | ||
|
26d47ca5bb | ||
|
816d824bc1 | ||
|
9f80f875f7 | ||
|
ae55a744d1 | ||
|
7f004913eb | ||
|
577f0d3e75 | ||
|
996028a165 | ||
|
026e9569a1 | ||
|
295f112356 | ||
|
9825916400 | ||
|
309797d573 | ||
|
5f88f8ff85 | ||
|
565f463fdc | ||
|
50e3ebb805 | ||
|
0a5714e8c8 | ||
|
88d16b7e2d | ||
|
2d60ce0ac7 | ||
|
cb47eca810 | ||
|
93a693b9ef | ||
|
c9b8490bb7 | ||
|
1c3e27616d | ||
|
b1165eac65 | ||
|
a555580f5f | ||
|
5b8c8a6f2e | ||
|
23e2495636 | ||
|
9ef407a028 | ||
|
65b8d3904f | ||
|
64058b983c | ||
|
cc749c612d | ||
|
746ab737b5 | ||
|
3caf118cbc | ||
|
d159fac7b5 | ||
|
c6bff34abb | ||
|
498f6027a1 | ||
|
de1e9f82d2 | ||
|
b3f417dc20 | ||
|
de78dff26d | ||
|
c010031269 | ||
|
ceeaa7476f | ||
|
1d567df2ee | ||
|
77a464344a | ||
|
5366029282 | ||
|
506e97afe6 | ||
|
04c43898ee | ||
|
fdf797bb28 | ||
|
0f9626c48d | ||
|
dd1552f3ff | ||
|
94b7bf4bf8 | ||
|
7de0a389dc | ||
|
1dd6d98a28 | ||
|
abe78b5df5 | ||
|
fa26357178 | ||
|
120c56988e | ||
|
f1f36be663 | ||
|
f0bc54fecb | ||
|
b79a36f65f | ||
|
4f6d2f830e | ||
|
babfdeaf04 | ||
|
c95038abb5 | ||
|
9fa6a7e2aa | ||
|
c0d97cefa5 | ||
|
33235f482e | ||
|
b183095c52 | ||
|
01e840e151 | ||
|
cc5391bc0d | ||
|
e640be9866 | ||
|
59c32867b3 | ||
|
0b719c18a6 | ||
|
d143b2d4f0 | ||
|
006940a359 | ||
|
fe600c72ad | ||
|
7d727f4f02 | ||
|
ce0fdf8bca | ||
|
da878eaf38 | ||
|
456191715a | ||
|
4b6162d364 | ||
|
08b7d4d3b7 | ||
|
ca37a9420e | ||
|
6333b73d13 |
44
.github/CONTRIBUTING.md
vendored
@ -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://twitter.com/iMacTia) and [@olleolleolle](https://twitter.com/olleolleolle) on Twitter.
|
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.
|
||||||
|
|
||||||
|
|
||||||
### Required Checks
|
### Required Checks
|
||||||
@ -64,50 +64,16 @@ We encourage adapters that:
|
|||||||
1. have features not present in included adapters.
|
1. have features not present in included adapters.
|
||||||
|
|
||||||
|
|
||||||
### Changes to the Faraday Website
|
### Changes to the Faraday Docs
|
||||||
|
|
||||||
The [Faraday Website][website] is included in the Faraday repository, under the `/docs` folder.
|
The Faraday Docs are included in the Faraday repository, under the `/docs` folder and deployed to [GitHub Pages][website].
|
||||||
If you want to apply changes to it, please test it locally before opening your PR.
|
If you want to apply changes to it, please test it locally before opening your PR.
|
||||||
|
You can find more information in the [Faraday Docs README][docs], including how to preview changes locally.
|
||||||
|
|
||||||
|
|
||||||
#### Test website changes using Docker
|
|
||||||
|
|
||||||
Start by cloning the repository and navigate to the newly-cloned directory on your computer. Then run the following:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker container run -p 80:4000 -v $(pwd)/docs:/site bretfisher/jekyll-serve
|
|
||||||
```
|
|
||||||
|
|
||||||
And that's it! Open your browser and navigate to `http://localhost` to see the website running.
|
|
||||||
Any change done to files in the `/docs` folder will be automatically picked up (with the exception of config changes).
|
|
||||||
|
|
||||||
|
|
||||||
#### Test website changes using Jekyll
|
|
||||||
|
|
||||||
You can test website changes locally, on your machine, too. Here's how:
|
|
||||||
|
|
||||||
Navigate into the /docs folder:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cd docs
|
|
||||||
```
|
|
||||||
|
|
||||||
Install Jekyll dependencies, this bundle is different from Faraday's one.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ bundle install
|
|
||||||
```
|
|
||||||
|
|
||||||
Run the Jekyll server with the Faraday website
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ bundle exec jekyll serve
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, navigate to http://127.0.0.1:4000/faraday/ to see the website running.
|
|
||||||
|
|
||||||
[semver]: https://semver.org/
|
[semver]: https://semver.org/
|
||||||
[changelog]: https://github.com/lostisland/faraday/releases
|
[changelog]: https://github.com/lostisland/faraday/releases
|
||||||
[faraday_middleware]: https://github.com/lostisland/faraday_middleware
|
[faraday_middleware]: https://github.com/lostisland/faraday_middleware
|
||||||
[website]: https://lostisland.github.io/faraday
|
[website]: https://lostisland.github.io/faraday
|
||||||
|
[docs]: ../docs/README.md
|
||||||
[Code of Conduct]: ./CODE_OF_CONDUCT.md
|
[Code of Conduct]: ./CODE_OF_CONDUCT.md
|
||||||
|
14
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "bundler"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
- package-ecosystem: "npm"
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
69
.github/workflows/ci.yml
vendored
@ -4,63 +4,66 @@ on:
|
|||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
push:
|
push:
|
||||||
branches: [main, 0.1x]
|
branches: [ main, 1.x, 0.1x ]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GIT_COMMIT_SHA: ${{ github.sha }}
|
GIT_COMMIT_SHA: ${{ github.sha }}
|
||||||
GIT_BRANCH: ${{ github.ref }}
|
GIT_BRANCH: ${{ github.ref }}
|
||||||
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
linting:
|
linting:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
BUNDLE_WITH: lint
|
||||||
|
BUNDLE_WITHOUT: development:test
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Ruby 2.7
|
- name: Setup Ruby 3.x
|
||||||
uses: ruby/setup-ruby@v1
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.7
|
ruby-version: 3
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
- name: Rubocop
|
- name: Rubocop
|
||||||
run: |
|
run: bundle exec rubocop --format progress
|
||||||
gem install bundler
|
|
||||||
bundle config set without 'development test'
|
|
||||||
bundle config set with 'lint'
|
|
||||||
bundle install
|
|
||||||
bundle exec rubocop --format progress
|
|
||||||
|
|
||||||
- name: Yard-Junk
|
- name: Yard-Junk
|
||||||
run: |
|
run: bundle exec yard-junk --path lib
|
||||||
gem install yard-junk --no-document
|
|
||||||
yard-junk --path lib
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
needs: [linting]
|
needs: [ linting ]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
name: build ${{ matrix.ruby }}
|
name: build ${{ matrix.ruby }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby: ['2.4', '2.5', '2.6', '2.7', '3.0']
|
ruby: [ '3.0', '3.1', '3.2', '3.3', '3.4' ]
|
||||||
|
experimental: [false]
|
||||||
|
include:
|
||||||
|
- ruby: head
|
||||||
|
experimental: true
|
||||||
|
- ruby: truffleruby-head
|
||||||
|
experimental: true
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v4
|
||||||
- uses: ruby/setup-ruby@v1
|
- uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: ${{ matrix.ruby }}
|
ruby-version: ${{ matrix.ruby }}
|
||||||
|
bundler-cache: true
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: RSpec
|
||||||
run: |
|
continue-on-error: ${{ matrix.experimental }}
|
||||||
sudo apt-get install libcurl4-openssl-dev
|
run: bundle exec rake
|
||||||
|
|
||||||
- name: Build
|
- name: Test External Adapters
|
||||||
run: |
|
continue-on-error: ${{ matrix.experimental }}
|
||||||
gem install bundler -v '<2'
|
run: bundle exec bake test:external
|
||||||
bundle install --jobs 4 --retry 3
|
|
||||||
|
|
||||||
- name: Test
|
|
||||||
continue-on-error: ${{ matrix.experimental }}
|
|
||||||
run: bundle exec rake
|
|
||||||
|
|
||||||
|
25
.github/workflows/publish.yml
vendored
@ -9,21 +9,18 @@ jobs:
|
|||||||
name: Publish to Rubygems
|
name: Publish to Rubygems
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
permissions:
|
||||||
- uses: actions/checkout@master
|
contents: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
- name: Set up Ruby 2.6
|
steps:
|
||||||
uses: actions/setup-ruby@v1
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Ruby 3.x
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
with:
|
with:
|
||||||
ruby-version: 2.6.x
|
bundler-cache: true
|
||||||
|
ruby-version: 3
|
||||||
|
|
||||||
- name: Publish to RubyGems
|
- name: Publish to RubyGems
|
||||||
run: |
|
uses: rubygems/release-gem@v1
|
||||||
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 }}
|
|
||||||
|
15
.github/workflows/refresh_team_page.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
name: Refresh Team Page
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
permissions: {}
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Refresh Contributors Stats
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Call GitHub API
|
||||||
|
run: |
|
||||||
|
curl "https://api.github.com/repos/${{ github.repository }}/stats/contributors"
|
5
.gitignore
vendored
@ -8,12 +8,17 @@ tmp
|
|||||||
.rvmrc
|
.rvmrc
|
||||||
.ruby-version
|
.ruby-version
|
||||||
.yardoc
|
.yardoc
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
## BUNDLER
|
## BUNDLER
|
||||||
*.gem
|
*.gem
|
||||||
.bundle
|
.bundle
|
||||||
Gemfile.lock
|
Gemfile.lock
|
||||||
vendor/bundle
|
vendor/bundle
|
||||||
|
external
|
||||||
|
|
||||||
|
## NPM
|
||||||
|
node_modules
|
||||||
|
|
||||||
## PROJECT::SPECIFIC
|
## PROJECT::SPECIFIC
|
||||||
.rbx
|
.rbx
|
||||||
|
218
.rubocop.yml
@ -1,62 +1,200 @@
|
|||||||
inherit_from: .rubocop_todo.yml
|
inherit_from: .rubocop_todo.yml
|
||||||
|
|
||||||
require:
|
require:
|
||||||
- rubocop-inclusivity
|
|
||||||
- rubocop-packaging
|
- rubocop-packaging
|
||||||
- rubocop-performance
|
- rubocop-performance
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
DisplayCopNames: true
|
DisplayCopNames: true
|
||||||
DisplayStyleGuide: true
|
DisplayStyleGuide: true
|
||||||
TargetRubyVersion: 2.4
|
TargetRubyVersion: 3.0
|
||||||
|
|
||||||
Inclusivity/Race:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Metrics/BlockLength:
|
|
||||||
Exclude:
|
|
||||||
- spec/**/*.rb
|
|
||||||
- examples/**/*.rb
|
|
||||||
|
|
||||||
Layout/EmptyLinesAroundAttributeAccessor: # (0.83)
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
|
# Custom config
|
||||||
|
Gemspec/RequireMFA: # we don't know if this works with auto-deployments yet
|
||||||
|
Enabled: false
|
||||||
Layout/LineLength:
|
Layout/LineLength:
|
||||||
Exclude:
|
Exclude:
|
||||||
- spec/**/*.rb
|
- spec/**/*.rb
|
||||||
- examples/**/*.rb
|
- examples/**/*.rb
|
||||||
|
Metrics/BlockLength:
|
||||||
Layout/SpaceAroundMethodCallOperator:
|
Exclude:
|
||||||
Enabled: true
|
- lib/faraday/options/env.rb
|
||||||
|
- spec/**/*.rb
|
||||||
Lint/DeprecatedOpenSSLConstant: # (0.84)
|
- examples/**/*.rb
|
||||||
Enabled: true
|
Metrics/ModuleLength:
|
||||||
|
Exclude:
|
||||||
Lint/RaiseException:
|
- lib/faraday/options/env.rb
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Lint/StructNewOverride:
|
|
||||||
Enabled: true
|
|
||||||
|
|
||||||
Style/DoubleNegation:
|
|
||||||
Enabled: false
|
|
||||||
|
|
||||||
Style/Documentation:
|
Style/Documentation:
|
||||||
Exclude:
|
Exclude:
|
||||||
- 'spec/**/*'
|
- 'spec/**/*'
|
||||||
- 'examples/**/*'
|
- 'examples/**/*'
|
||||||
|
Style/DoubleNegation:
|
||||||
Style/ExponentialNotation:
|
Enabled: false
|
||||||
Enabled: true
|
|
||||||
Style/HashEachMethods:
|
|
||||||
Enabled: true
|
|
||||||
Style/HashTransformKeys:
|
|
||||||
Enabled: true
|
|
||||||
Style/HashTransformValues:
|
|
||||||
Enabled: true
|
|
||||||
Style/IfUnlessModifier:
|
Style/IfUnlessModifier:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
Style/SlicingWithRange: # (0.83)
|
# New cops
|
||||||
|
Gemspec/DeprecatedAttributeAssignment: # new in 1.30
|
||||||
|
Enabled: true
|
||||||
|
Layout/LineContinuationLeadingSpace: # new in 1.31
|
||||||
|
Enabled: true
|
||||||
|
Layout/LineContinuationSpacing: # new in 1.31
|
||||||
|
Enabled: true
|
||||||
|
Layout/LineEndStringConcatenationIndentation: # new in 1.18
|
||||||
|
Enabled: true
|
||||||
|
Layout/SpaceBeforeBrackets: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Lint/AmbiguousAssignment: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Lint/AmbiguousOperatorPrecedence: # new in 1.21
|
||||||
|
Enabled: true
|
||||||
|
Lint/AmbiguousRange: # new in 1.19
|
||||||
|
Enabled: true
|
||||||
|
Lint/ConstantOverwrittenInRescue: # new in 1.31
|
||||||
|
Enabled: true
|
||||||
|
Lint/DeprecatedConstants: # new in 1.8
|
||||||
|
Enabled: true
|
||||||
|
Lint/DuplicateBranch: # new in 1.3
|
||||||
|
Enabled: true
|
||||||
|
Lint/DuplicateRegexpCharacterClassElement: # new in 1.1
|
||||||
|
Enabled: true
|
||||||
|
Lint/EmptyBlock: # new in 1.1
|
||||||
|
Enabled: true
|
||||||
|
Lint/EmptyClass: # new in 1.3
|
||||||
|
Enabled: true
|
||||||
|
Lint/EmptyInPattern: # new in 1.16
|
||||||
|
Enabled: true
|
||||||
|
Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21
|
||||||
|
Enabled: true
|
||||||
|
Lint/LambdaWithoutLiteralBlock: # new in 1.8
|
||||||
|
Enabled: true
|
||||||
|
Lint/NoReturnInBeginEndBlocks: # new in 1.2
|
||||||
|
Enabled: true
|
||||||
|
Lint/NonAtomicFileOperation: # new in 1.31
|
||||||
|
Enabled: true
|
||||||
|
Lint/NumberedParameterAssignment: # new in 1.9
|
||||||
|
Enabled: true
|
||||||
|
Lint/OrAssignmentToConstant: # new in 1.9
|
||||||
|
Enabled: true
|
||||||
|
Lint/RedundantDirGlobSort: # new in 1.8
|
||||||
|
Enabled: true
|
||||||
|
Lint/RefinementImportMethods: # new in 1.27
|
||||||
|
Enabled: true
|
||||||
|
Lint/RequireRangeParentheses: # new in 1.32
|
||||||
|
Enabled: true
|
||||||
|
Lint/RequireRelativeSelfPath: # new in 1.22
|
||||||
|
Enabled: true
|
||||||
|
Lint/SymbolConversion: # new in 1.9
|
||||||
|
Enabled: true
|
||||||
|
Lint/ToEnumArguments: # new in 1.1
|
||||||
|
Enabled: true
|
||||||
|
Lint/TripleQuotes: # new in 1.9
|
||||||
|
Enabled: true
|
||||||
|
Lint/UnexpectedBlockArity: # new in 1.5
|
||||||
|
Enabled: true
|
||||||
|
Lint/UnmodifiedReduceAccumulator: # new in 1.1
|
||||||
|
Enabled: true
|
||||||
|
Lint/UselessRuby2Keywords: # new in 1.23
|
||||||
|
Enabled: true
|
||||||
|
Naming/BlockForwarding: # new in 1.24
|
||||||
|
Enabled: true
|
||||||
|
Security/CompoundHash: # new in 1.28
|
||||||
|
Enabled: true
|
||||||
|
Security/IoMethods: # new in 1.22
|
||||||
|
Enabled: true
|
||||||
|
Style/ArgumentsForwarding: # new in 1.1
|
||||||
|
Enabled: true
|
||||||
|
Style/CollectionCompact: # new in 1.2
|
||||||
|
Enabled: true
|
||||||
|
Style/DocumentDynamicEvalDefinition: # new in 1.1
|
||||||
|
Enabled: true
|
||||||
|
Style/EmptyHeredoc: # new in 1.32
|
||||||
|
Enabled: true
|
||||||
|
Style/EndlessMethod: # new in 1.8
|
||||||
|
Enabled: true
|
||||||
|
Style/EnvHome: # new in 1.29
|
||||||
|
Enabled: true
|
||||||
|
Style/FetchEnvVar: # new in 1.28
|
||||||
|
Enabled: true
|
||||||
|
Style/FileRead: # new in 1.24
|
||||||
|
Enabled: true
|
||||||
|
Style/FileWrite: # new in 1.24
|
||||||
|
Enabled: true
|
||||||
|
Style/HashConversion: # new in 1.10
|
||||||
|
Enabled: true
|
||||||
|
Style/HashExcept: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Style/IfWithBooleanLiteralBranches: # new in 1.9
|
||||||
|
Enabled: true
|
||||||
|
Style/InPatternThen: # new in 1.16
|
||||||
|
Enabled: true
|
||||||
|
Style/MapCompactWithConditionalBlock: # new in 1.30
|
||||||
|
Enabled: true
|
||||||
|
Style/MapToHash: # new in 1.24
|
||||||
|
Enabled: true
|
||||||
|
Style/MultilineInPatternThen: # new in 1.16
|
||||||
|
Enabled: true
|
||||||
|
Style/NegatedIfElseCondition: # new in 1.2
|
||||||
|
Enabled: true
|
||||||
|
Style/NestedFileDirname: # new in 1.26
|
||||||
|
Enabled: true
|
||||||
|
Style/NilLambda: # new in 1.3
|
||||||
|
Enabled: true
|
||||||
|
Style/NumberedParameters: # new in 1.22
|
||||||
|
Enabled: true
|
||||||
|
Style/NumberedParametersLimit: # new in 1.22
|
||||||
|
Enabled: true
|
||||||
|
Style/ObjectThen: # new in 1.28
|
||||||
|
Enabled: true
|
||||||
|
Style/OpenStructUse: # new in 1.23
|
||||||
|
Enabled: true
|
||||||
|
Style/QuotedSymbols: # new in 1.16
|
||||||
|
Enabled: true
|
||||||
|
Style/RedundantArgument: # new in 1.4
|
||||||
|
Enabled: true
|
||||||
|
Style/RedundantInitialize: # new in 1.27
|
||||||
|
Enabled: true
|
||||||
|
Style/RedundantSelfAssignmentBranch: # new in 1.19
|
||||||
|
Enabled: true
|
||||||
|
Style/SelectByRegexp: # new in 1.22
|
||||||
|
Enabled: true
|
||||||
|
Style/StringChars: # new in 1.12
|
||||||
|
Enabled: true
|
||||||
|
Style/SwapValues: # new in 1.1
|
||||||
|
Enabled: true
|
||||||
|
Performance/AncestorsInclude: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Performance/BigDecimalWithNumericArgument: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Performance/BlockGivenWithExplicitBlock: # new in 1.9
|
||||||
|
Enabled: true
|
||||||
|
Performance/CollectionLiteralInLoop: # new in 1.8
|
||||||
|
Enabled: true
|
||||||
|
Performance/ConcurrentMonotonicTime: # new in 1.12
|
||||||
|
Enabled: true
|
||||||
|
Performance/ConstantRegexp: # new in 1.9
|
||||||
|
Enabled: true
|
||||||
|
Performance/MapCompact: # new in 1.11
|
||||||
|
Enabled: true
|
||||||
|
Performance/MethodObjectAsBlock: # new in 1.9
|
||||||
|
Enabled: true
|
||||||
|
Performance/RedundantEqualityComparisonBlock: # new in 1.10
|
||||||
|
Enabled: true
|
||||||
|
Performance/RedundantSortBlock: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Performance/RedundantSplitRegexpArgument: # new in 1.10
|
||||||
|
Enabled: true
|
||||||
|
Performance/RedundantStringChars: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Performance/ReverseFirst: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Performance/SortReverse: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Performance/Squeeze: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Performance/StringIdentifierArgument: # new in 1.13
|
||||||
|
Enabled: true
|
||||||
|
Performance/StringInclude: # new in 1.7
|
||||||
|
Enabled: true
|
||||||
|
Performance/Sum: # new in 1.8
|
||||||
Enabled: true
|
Enabled: true
|
||||||
|
|
||||||
|
@ -1,32 +1,71 @@
|
|||||||
# This configuration was generated by
|
# This configuration was generated by
|
||||||
# `rubocop --auto-gen-config`
|
# `rubocop --auto-gen-config`
|
||||||
# on 2021-04-14 06:51:37 UTC using RuboCop version 0.90.0.
|
# on 2023-12-27 11:12:52 UTC using RuboCop version 1.59.0.
|
||||||
# The point is for the user to remove these configuration records
|
# The point is for the user to remove these configuration records
|
||||||
# one by one as the offenses are removed from the code base.
|
# one by one as the offenses are removed from the code base.
|
||||||
# Note that changes in the inspected code, or installation of new
|
# Note that changes in the inspected code, or installation of new
|
||||||
# versions of RuboCop, may require this file to be generated again.
|
# versions of RuboCop, may require this file to be generated again.
|
||||||
|
|
||||||
# Offense count: 26
|
# Offense count: 6
|
||||||
# Configuration parameters: IgnoredMethods.
|
# Configuration parameters: AllowedMethods.
|
||||||
|
# AllowedMethods: enums
|
||||||
|
Lint/ConstantDefinitionInBlock:
|
||||||
|
Exclude:
|
||||||
|
- 'spec/faraday/options/options_spec.rb'
|
||||||
|
- 'spec/faraday/rack_builder_spec.rb'
|
||||||
|
- 'spec/faraday/request/instrumentation_spec.rb'
|
||||||
|
|
||||||
|
# Offense count: 11
|
||||||
|
# Configuration parameters: AllowComments, AllowEmptyLambdas.
|
||||||
|
Lint/EmptyBlock:
|
||||||
|
Exclude:
|
||||||
|
- 'spec/faraday/connection_spec.rb'
|
||||||
|
- 'spec/faraday/rack_builder_spec.rb'
|
||||||
|
- 'spec/faraday/response_spec.rb'
|
||||||
|
|
||||||
|
# Offense count: 13
|
||||||
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||||
Metrics/AbcSize:
|
Metrics/AbcSize:
|
||||||
Max: 42
|
Max: 42
|
||||||
|
|
||||||
# Offense count: 5
|
# Offense count: 3
|
||||||
# Configuration parameters: CountComments, CountAsOne.
|
# Configuration parameters: CountComments, CountAsOne.
|
||||||
Metrics/ClassLength:
|
Metrics/ClassLength:
|
||||||
Max: 256
|
Max: 230
|
||||||
|
|
||||||
# Offense count: 15
|
# Offense count: 9
|
||||||
# Configuration parameters: IgnoredMethods.
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||||
Metrics/CyclomaticComplexity:
|
Metrics/CyclomaticComplexity:
|
||||||
Max: 13
|
Max: 13
|
||||||
|
|
||||||
# Offense count: 43
|
# Offense count: 27
|
||||||
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods.
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
||||||
Metrics/MethodLength:
|
Metrics/MethodLength:
|
||||||
Max: 37
|
Max: 33
|
||||||
|
|
||||||
# Offense count: 9
|
# Offense count: 1
|
||||||
# Configuration parameters: IgnoredMethods.
|
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
|
||||||
|
Metrics/ParameterLists:
|
||||||
|
Max: 6
|
||||||
|
|
||||||
|
# Offense count: 7
|
||||||
|
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||||
Metrics/PerceivedComplexity:
|
Metrics/PerceivedComplexity:
|
||||||
Max: 14
|
Max: 14
|
||||||
|
|
||||||
|
# Offense count: 19
|
||||||
|
# This cop supports safe autocorrection (--autocorrect).
|
||||||
|
# Configuration parameters: AllowOnlyRestArgument, UseAnonymousForwarding, RedundantRestArgumentNames, RedundantKeywordRestArgumentNames, RedundantBlockArgumentNames.
|
||||||
|
# RedundantRestArgumentNames: args, arguments
|
||||||
|
# RedundantKeywordRestArgumentNames: kwargs, options, opts
|
||||||
|
# RedundantBlockArgumentNames: blk, block, proc
|
||||||
|
Style/ArgumentsForwarding:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/faraday.rb'
|
||||||
|
- 'lib/faraday/rack_builder.rb'
|
||||||
|
|
||||||
|
# Offense count: 3
|
||||||
|
Style/DocumentDynamicEvalDefinition:
|
||||||
|
Exclude:
|
||||||
|
- 'lib/faraday/connection.rb'
|
||||||
|
- 'lib/faraday/options.rb'
|
||||||
|
202
CHANGELOG.md
@ -1,9 +1,196 @@
|
|||||||
# Faraday Changelog
|
# Faraday Changelog
|
||||||
|
|
||||||
|
## The changelog has moved!
|
||||||
|
|
||||||
|
This file is not being updated anymore. Instead, please check the [Releases](https://github.com/lostisland/faraday/releases) page.
|
||||||
|
|
||||||
|
## [2.2.0](https://github.com/lostisland/faraday/compare/v2.1.0...v2.2.0) (2022-02-03)
|
||||||
|
|
||||||
|
* Reintroduce the possibility to register middleware with symbols, strings or procs in [#1391](https://github.com/lostisland/faraday/pull/1391)
|
||||||
|
|
||||||
|
## [2.1.0](https://github.com/lostisland/faraday/compare/v2.0.1...v2.1.0) (2022-01-15)
|
||||||
|
|
||||||
|
* Fix test adapter thread safety by @iMacTia in [#1380](https://github.com/lostisland/faraday/pull/1380)
|
||||||
|
* Add default adapter options by @hirasawayuki in [#1382](https://github.com/lostisland/faraday/pull/1382)
|
||||||
|
* CI: Add Ruby 3.1 to matrix by @petergoldstein in [#1374](https://github.com/lostisland/faraday/pull/1374)
|
||||||
|
* docs: fix regex pattern in logger.md examples by @hirasawayuki in [#1378](https://github.com/lostisland/faraday/pull/1378)
|
||||||
|
|
||||||
|
## [2.0.1](https://github.com/lostisland/faraday/compare/v2.0.0...v2.0.1) (2022-01-05)
|
||||||
|
|
||||||
|
* Re-add `faraday-net_http` as default adapter by @iMacTia in [#1366](https://github.com/lostisland/faraday/pull/1366)
|
||||||
|
* Updated sample format in UPGRADING.md by @vimutter in [#1361](https://github.com/lostisland/faraday/pull/1361)
|
||||||
|
* docs: Make UPGRADING examples more copyable by @olleolleolle in [#1363](https://github.com/lostisland/faraday/pull/1363)
|
||||||
|
|
||||||
|
## [2.0.0](https://github.com/lostisland/faraday/compare/v1.8.0...v2.0.0) (2022-01-04)
|
||||||
|
|
||||||
|
The next major release is here, and it comes almost 2 years after the release of v1.0!
|
||||||
|
|
||||||
|
This release changes the way you use Faraday and embraces a new paradigm of Faraday as an ecosystem, rather than a library.
|
||||||
|
|
||||||
|
What does that mean? It means that Faraday is less of a bundled tool and more of a framework for the community to build on top of.
|
||||||
|
|
||||||
|
As a result, all adapters and some middleware have moved out and are now shipped as standalone gems 🙌!
|
||||||
|
|
||||||
|
But this doesn't mean that upgrading from Faraday 1.x to Faraday 2.0 should be hard, in fact we've listed everything you need to do in the [UPGRADING.md](https://github.com/lostisland/faraday/blob/main/UPGRADING.md) doc.
|
||||||
|
|
||||||
|
Moreover, we've setup a new [awesome-faraday](https://github.com/lostisland/awesome-faraday) repository that will showcase a curated list of adapters and middleware 😎.
|
||||||
|
|
||||||
|
This release was the result of the efforts of the core team and all the contributors, new and old, that have helped achieve this milestone 👏.
|
||||||
|
|
||||||
|
## What's Changed
|
||||||
|
|
||||||
|
* Autoloading, dependency loading and middleware registry cleanup by @iMacTia in [#1301](https://github.com/lostisland/faraday/pull/1301)
|
||||||
|
* Move JSON middleware (request and response) from faraday_middleware by @iMacTia in [#1300](https://github.com/lostisland/faraday/pull/1300)
|
||||||
|
* Remove deprecated `Faraday::Request#method` by @olleolleolle in [#1303](https://github.com/lostisland/faraday/pull/1303)
|
||||||
|
* Remove deprecated `Faraday::UploadIO` by @iMacTia in [#1307](https://github.com/lostisland/faraday/pull/1307)
|
||||||
|
* [1.x] Deprecate Authorization helpers in `Faraday::Connection` by @iMacTia in [#1306](https://github.com/lostisland/faraday/pull/1306)
|
||||||
|
* Drop deprecated auth helpers from Connection and refactor auth middleware by @iMacTia in [#1308](https://github.com/lostisland/faraday/pull/1308)
|
||||||
|
* Add Faraday 1.x examples in authentication.md docs by @iMacTia in [#1320](https://github.com/lostisland/faraday/pull/1320)
|
||||||
|
* Fix passing a URL with embedded basic auth by @iMacTia in [#1324](https://github.com/lostisland/faraday/pull/1324)
|
||||||
|
* Register JSON middleware by @mollerhoj in [#1331](https://github.com/lostisland/faraday/pull/1331)
|
||||||
|
* Retry middleware should handle string exception class name consistently by @jrochkind in [#1334](https://github.com/lostisland/faraday/pull/1334)
|
||||||
|
* Improve request info in exceptions raised by RaiseError Middleware by @willianzocolau in [#1335](https://github.com/lostisland/faraday/pull/1335)
|
||||||
|
* Remove net-http adapter and update docs by @iMacTia in [#1336](https://github.com/lostisland/faraday/pull/1336)
|
||||||
|
* Explain plan for faraday_middleware in UPGRADING.md by @iMacTia in [#1339](https://github.com/lostisland/faraday/pull/1339)
|
||||||
|
* Scripts folder cleanup by @iMacTia in [#1340](https://github.com/lostisland/faraday/pull/1340)
|
||||||
|
* Replace `Hash#merge` with `Utils#deep_merge` for connection options by @xkwd in [#1343](https://github.com/lostisland/faraday/pull/1343)
|
||||||
|
* Callable authorizers by @sled in [#1345](https://github.com/lostisland/faraday/pull/1345)
|
||||||
|
* Default value for exc error by @DariuszMusielak in [#1351](https://github.com/lostisland/faraday/pull/1351)
|
||||||
|
* Don't call `retry_block` unless a retry is going to happen by @jrochkind in [#1350](https://github.com/lostisland/faraday/pull/1350)
|
||||||
|
* Improve documentation for v2 by @iMacTia in [#1353](https://github.com/lostisland/faraday/pull/1353)
|
||||||
|
* Remove default `default_adapter` (yes, you read that right) by @iMacTia in [#1354](https://github.com/lostisland/faraday/pull/1354)
|
||||||
|
* Remove retry middleware by @iMacTia in [#1356](https://github.com/lostisland/faraday/pull/1356)
|
||||||
|
* Remove multipart middleware and all its documentation and tests by @iMacTia in [#1357](https://github.com/lostisland/faraday/pull/1357)
|
||||||
|
|
||||||
|
## [1.9.3](https://github.com/lostisland/faraday/compare/v1.9.2...v1.9.3) (2022-01-06)
|
||||||
|
|
||||||
|
* Re-add support for Ruby 2.4+ by @iMacTia in [#1371](https://github.com/lostisland/faraday/pull/1371)
|
||||||
|
|
||||||
|
## [1.9.2](https://github.com/lostisland/faraday/compare/v1.9.1...v1.9.2) (2022-01-06)
|
||||||
|
|
||||||
|
* Add alias with legacy name to gemified middleware by @iMacTia in [#1372](https://github.com/lostisland/faraday/pull/1372)
|
||||||
|
|
||||||
|
## [1.9.1](https://github.com/lostisland/faraday/compare/v1.9.0...v1.9.1) (2022-01-06)
|
||||||
|
|
||||||
|
* Update adapter dependencies in Gemspec by @iMacTia in [#1370](https://github.com/lostisland/faraday/pull/1370)
|
||||||
|
|
||||||
|
## [1.9.0](https://github.com/lostisland/faraday/compare/v1.8.0...v1.9.0) (2022-01-06)
|
||||||
|
|
||||||
|
* Use external multipart and retry middleware by @iMacTia in [#1367](https://github.com/lostisland/faraday/pull/1367)
|
||||||
|
|
||||||
|
## [1.8.0](https://github.com/lostisland/faraday/releases/tag/v1.8.0) (2021-09-18)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Backport authorization procs (#1322, @jarl-dk)
|
||||||
|
|
||||||
|
## [v1.7.0](https://github.com/lostisland/faraday/releases/tag/v1.7.0) (2021-08-09)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add strict_mode to Test::Stubs (#1298, @yykamei)
|
||||||
|
|
||||||
|
## [v1.6.0](https://github.com/lostisland/faraday/releases/tag/v1.6.0) (2021-08-01)
|
||||||
|
|
||||||
|
### Misc
|
||||||
|
|
||||||
|
* Use external Rack adapter (#1296, @iMacTia)
|
||||||
|
|
||||||
|
## [v1.5.1](https://github.com/lostisland/faraday/releases/tag/v1.5.1) (2021-07-11)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix JRuby incompatibility after moving out EM adapters (#1294, @ahorek)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
* Update YARD to follow RackBuilder (#1292, @kachick)
|
||||||
|
|
||||||
|
## [v1.5.0](https://github.com/lostisland/faraday/releases/tag/v1.5.0) (2021-07-04)
|
||||||
|
|
||||||
|
### Misc
|
||||||
|
|
||||||
|
* Use external httpclient adapter (#1289, @iMacTia)
|
||||||
|
* Use external patron adapter (#1290, @iMacTia)
|
||||||
|
|
||||||
|
## [v1.4.3](https://github.com/lostisland/faraday/releases/tag/v1.4.3) (2021-06-24)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Silence warning (#1286, @gurgeous)
|
||||||
|
* Always dup url_prefix in Connection#build_exclusive_url (#1288, @alexeyds)
|
||||||
|
|
||||||
|
## [v1.4.2](https://github.com/lostisland/faraday/releases/tag/v1.4.2) (2021-05-22)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
* Add proxy setting when url_prefix is changed (#1276, @ci)
|
||||||
|
* Default proxy scheme to http:// if necessary, fixes #1282 (#1283, @gurgeous)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
* Improve introduction page (#1273, @gurgeous)
|
||||||
|
* Docs: add more middleware examples (#1277, @gurgeous)
|
||||||
|
|
||||||
|
### Misc
|
||||||
|
* Use external `em_http` and `em_synchrony` adapters (#1274, @iMacTia)
|
||||||
|
|
||||||
|
## [v1.4.1](https://github.com/lostisland/faraday/releases/tag/v1.4.1) (2021-04-18)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix dependencies from external adapter gems (#1269, @iMacTia)
|
||||||
|
|
||||||
|
## [v1.4.0](https://github.com/lostisland/faraday/releases/tag/v1.4.0) (2021-04-16)
|
||||||
|
|
||||||
|
### Highlights
|
||||||
|
|
||||||
|
With this release, we continue the work of gradually moving out adapters into their own gems 🎉
|
||||||
|
Thanks to @MikeRogers0 for helping the Faraday team in progressing with this quest 👏
|
||||||
|
|
||||||
|
And thanks to @olleolleolle efforts, Faraday is becoming more inclusive than ever 🤗
|
||||||
|
Faraday's `master` branch has been renamed into `main`, we have an official policy on inclusive language and even a rubocop plugin to check for non-inclusive words ❤️!
|
||||||
|
Checkout the "Misc" section below for more details 🙌 !
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix NoMethodError undefined method 'coverage' (#1255, @Maroo-b)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
* Some docs on EventMachine adapters. (#1232, @damau)
|
||||||
|
* CONTRIBUTING: Fix grammar and layout (#1261, @olleolleolle)
|
||||||
|
|
||||||
|
### Misc
|
||||||
|
|
||||||
|
* Replacing Net::HTTP::Persistent with faraday-net_http_persistent (#1250, @MikeRogers0)
|
||||||
|
* CI: Configure the regenerated Coveralls token (#1256, @olleolleolle)
|
||||||
|
* Replace Excon adapter with Faraday::Excon gem, and fix autoloading issue with Faraday::NetHttpPersistent (#1257, @iMacTia)
|
||||||
|
* Drop CodeClimate (#1259, @olleolleolle)
|
||||||
|
* CI: Rename default branch to main (#1263, @olleolleolle)
|
||||||
|
* Drop RDoc support file .document (#1264, @olleolleolle, @iMacTia)
|
||||||
|
* CONTRIBUTING: add a policy on inclusive language (#1262, @olleolleolle)
|
||||||
|
* Add rubocop-inclusivity (#1267, @olleolleolle, @iMacTia)
|
||||||
|
|
||||||
|
## [v1.3.1](https://github.com/lostisland/faraday/releases/tag/v1.3.1) (2021-04-16)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Escape colon in path segment (#1237, @yarafan)
|
||||||
|
* Handle IPv6 address String on Faraday::Connection#proxy_from_env (#1252, @cosmo0920)
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
* Fix broken Rubydoc.info links (#1236, @nickcampbell18)
|
||||||
|
* Add httpx to list of external adapters (#1246, @HoneyryderChuck)
|
||||||
|
|
||||||
|
### Misc
|
||||||
|
|
||||||
|
* Refactor CI to remove duplicated line (#1230, @tricknotes)
|
||||||
|
* Gemspec: Pick a good ruby2_keywords release (#1241, @olleolleolle)
|
||||||
|
|
||||||
## [v1.3.0](https://github.com/lostisland/faraday/releases/tag/v1.3.0) (2020-12-31)
|
## [v1.3.0](https://github.com/lostisland/faraday/releases/tag/v1.3.0) (2020-12-31)
|
||||||
|
|
||||||
### Highlights
|
### Highlights
|
||||||
Faraday v1.3.0 is the first release to officially support Ruby 3.0 in the CI pipeline 🎉 🍾!
|
Faraday v1.3.0 is the first release to officially support Ruby 3.0 in the CI pipeline 🎉 🍾!
|
||||||
|
|
||||||
This is also the first release with a previously "included" adapter (Net::HTTP) being isolated into a [separate gem](https://github.com/lostisland/faraday-net_http) 🎊!
|
This is also the first release with a previously "included" adapter (Net::HTTP) being isolated into a [separate gem](https://github.com/lostisland/faraday-net_http) 🎊!
|
||||||
The new adapter is added to Faraday as a dependency for now, so that means full backwards-compatibility, but just to be safe be careful when upgrading!
|
The new adapter is added to Faraday as a dependency for now, so that means full backwards-compatibility, but just to be safe be careful when upgrading!
|
||||||
@ -94,7 +281,7 @@ Many thanks to the Faraday Team, @JanDintel and everyone who attended the [ROSS
|
|||||||
* Allows `parse` method to be private/protected in response middleware (#1123)
|
* Allows `parse` method to be private/protected in response middleware (#1123)
|
||||||
* Encode Spaces in Query Strings as '%20' Instead of '+' (#1125)
|
* Encode Spaces in Query Strings as '%20' Instead of '+' (#1125)
|
||||||
* Limits rack to v2.0.x (#1127)
|
* Limits rack to v2.0.x (#1127)
|
||||||
* Adapter Registry reads also use mutex (#1136)
|
* Adapter Registry reads also use mutex (#1136)
|
||||||
|
|
||||||
### Documentation
|
### Documentation
|
||||||
|
|
||||||
@ -104,7 +291,7 @@ Many thanks to the Faraday Team, @JanDintel and everyone who attended the [ROSS
|
|||||||
* Website: add search bar (#1116)
|
* Website: add search bar (#1116)
|
||||||
* Fix request/response mix-up in docs text (#1132)
|
* Fix request/response mix-up in docs text (#1132)
|
||||||
|
|
||||||
## v1.0
|
## [v1.0](https://github.com/lostisland/faraday/releases/tag/v1.0.0) (2020-01-22)
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
|
|
||||||
@ -148,6 +335,13 @@ Misc:
|
|||||||
* Describe clearing cached stubs #1045 (@viraptor)
|
* Describe clearing cached stubs #1045 (@viraptor)
|
||||||
* Add project metadata to the gemspec #1046 (@orien)
|
* Add project metadata to the gemspec #1046 (@orien)
|
||||||
|
|
||||||
|
## v0.17.4
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
|
||||||
|
* NetHttp adapter: wrap Errno::EADDRNOTAVAIL (#1114, @embs)
|
||||||
|
* Fix === for subclasses of deprecated classes (#1243, @mervync)
|
||||||
|
|
||||||
## v0.17.3
|
## v0.17.3
|
||||||
|
|
||||||
Fixes:
|
Fixes:
|
||||||
@ -323,7 +517,7 @@ Breaking changes:
|
|||||||
- Drop support for Ruby 1.8
|
- Drop support for Ruby 1.8
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Include wrapped exception/reponse in ClientErrors
|
- Include wrapped exception/response 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
|
||||||
|
41
Gemfile
@ -2,39 +2,28 @@
|
|||||||
|
|
||||||
source 'https://rubygems.org'
|
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
|
gem 'jruby-openssl', '~> 0.11.0', platforms: :jruby
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'pry'
|
gem 'bake-test-external'
|
||||||
gem 'rake'
|
|
||||||
end
|
|
||||||
|
|
||||||
group :lint, :development 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 'coveralls_reborn', require: false
|
||||||
gem 'em-http-request', '>= 1.1', require: 'em-http', platform: :ruby
|
gem 'pry'
|
||||||
gem 'em-synchrony', '>= 1.0.3', require: %w[em-synchrony em-synchrony/em-http], platform: :ruby
|
gem 'rack', '~> 3.0'
|
||||||
gem 'excon', '>= 0.27.4'
|
gem 'rake'
|
||||||
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', '~> 3.7'
|
||||||
gem 'rspec_junit_formatter', '~> 0.4'
|
gem 'rspec_junit_formatter', '~> 0.4'
|
||||||
gem 'simplecov'
|
gem 'simplecov'
|
||||||
gem 'typhoeus', '~> 1.4'
|
|
||||||
gem 'webmock', '~> 3.4'
|
gem 'webmock', '~> 3.4'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
group :development, :lint do
|
||||||
|
gem 'racc', '~> 1.7' # for RuboCop, on Ruby 3.3
|
||||||
|
gem 'rubocop'
|
||||||
|
gem 'rubocop-packaging', '~> 0.5'
|
||||||
|
gem 'rubocop-performance', '~> 1.0'
|
||||||
|
gem 'yard-junk'
|
||||||
|
end
|
||||||
|
|
||||||
gemspec
|
gemspec
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2009-2020 Rick Olson, Zack Hobson
|
Copyright (c) 2009-2023 Rick Olson, Zack Hobson
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
52
README.md
@ -1,25 +1,41 @@
|
|||||||
# [][website]
|
# [][website]
|
||||||
|
|
||||||
[](https://rubygems.org/gems/faraday)
|
[](https://rubygems.org/gems/faraday)
|
||||||
[](https://github.com/lostisland/faraday/actions?query=workflow%3ACI)
|
[](https://github.com/lostisland/faraday/actions?query=workflow%3ACI)
|
||||||
[](https://gitter.im/lostisland/faraday?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
[](https://github.com/lostisland/faraday/discussions)
|
||||||
|
|
||||||
|
Faraday is an HTTP client library abstraction layer that provides a common interface over many
|
||||||
|
adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.
|
||||||
|
Take a look at [Awesome Faraday][awesome] for a list of available adapters and middleware.
|
||||||
|
|
||||||
Faraday is an HTTP client library that provides a common interface over many
|
## Why use Faraday?
|
||||||
adapters (such as Net::HTTP) and embraces the concept of Rack middleware when
|
|
||||||
processing the request/response cycle.
|
Faraday gives you the power of Rack middleware for manipulating HTTP requests and responses,
|
||||||
|
making it easier to build sophisticated API clients or web service libraries that abstract away
|
||||||
|
the details of how HTTP requests are made.
|
||||||
|
|
||||||
|
Faraday comes with a lot of features out of the box, such as:
|
||||||
|
* Support for multiple adapters (Net::HTTP, Typhoeus, Patron, Excon, HTTPClient, and more)
|
||||||
|
* Persistent connections (keep-alive)
|
||||||
|
* Parallel requests
|
||||||
|
* Automatic response parsing (JSON, XML, YAML)
|
||||||
|
* Customization of the request/response cycle with middleware
|
||||||
|
* Support for streaming responses
|
||||||
|
* Support for uploading files
|
||||||
|
* And much more!
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
The best starting point is the [Faraday Website][website], with its introduction and explanation.
|
The best starting point is the [Faraday Website][website], with its introduction and explanation.
|
||||||
Need more details? See the [Faraday API Documentation][apidoc] to see how it works internally.
|
|
||||||
|
Need more details? See the [Faraday API Documentation][apidoc] to see how it works internally, or take a look at [Advanced techniques for calling HTTP APIs in Ruby](https://mattbrictson.com/blog/advanced-http-techniques-in-ruby) blog post from [@mattbrictson](https://github.com/mattbrictson) 🚀
|
||||||
|
|
||||||
## Supported Ruby versions
|
## Supported Ruby versions
|
||||||
|
|
||||||
This library aims to support and is [tested against][actions] the following Ruby
|
This library aims to support and is [tested against][actions] the currently officially supported Ruby
|
||||||
implementations:
|
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).
|
||||||
* Ruby 2.4+
|
Currently that means we support Ruby 3.0+
|
||||||
|
|
||||||
If something doesn't work on one of these Ruby versions, it's a bug.
|
If something doesn't work on one of these Ruby versions, it's a bug.
|
||||||
|
|
||||||
@ -41,13 +57,11 @@ Open the issues page and check for the `help wanted` label!
|
|||||||
But before you start coding, please read our [Contributing Guide][contributing]
|
But before you start coding, please read our [Contributing Guide][contributing]
|
||||||
|
|
||||||
## Copyright
|
## Copyright
|
||||||
© 2009 - 2020, the [Faraday Team][faraday_team]. Website and branding design by [Elena Lo Piccolo](https://elelopic.design).
|
|
||||||
|
|
||||||
[website]: https://lostisland.github.io/faraday
|
© 2009 - 2023, the Faraday Team. Website and branding design by [Elena Lo Piccolo](https://elelopic.design).
|
||||||
[faraday_team]: https://lostisland.github.io/faraday/team
|
|
||||||
[contributing]: https://github.com/lostisland/faraday/blob/master/.github/CONTRIBUTING.md
|
[awesome]: https://github.com/lostisland/awesome-faraday/#adapters
|
||||||
[apidoc]: https://www.rubydoc.info/github/lostisland/faraday
|
[website]: https://lostisland.github.io/faraday
|
||||||
[actions]: https://github.com/lostisland/faraday/actions
|
[contributing]: https://github.com/lostisland/faraday/blob/main/.github/CONTRIBUTING.md
|
||||||
[jruby]: http://jruby.org/
|
[apidoc]: https://www.rubydoc.info/github/lostisland/faraday
|
||||||
[rubinius]: http://rubini.us/
|
[actions]: https://github.com/lostisland/faraday/actions
|
||||||
[license]: LICENSE.md
|
|
||||||
|
7
Rakefile
@ -1,7 +1,12 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rspec/core/rake_task'
|
require 'rspec/core/rake_task'
|
||||||
|
require 'bundler'
|
||||||
|
|
||||||
RSpec::Core::RakeTask.new(:spec)
|
Bundler::GemHelper.install_tasks
|
||||||
|
|
||||||
|
RSpec::Core::RakeTask.new(:spec) do |task|
|
||||||
|
task.ruby_opts = %w[-W]
|
||||||
|
end
|
||||||
|
|
||||||
task default: :spec
|
task default: :spec
|
||||||
|
126
UPGRADING.md
@ -1,7 +1,133 @@
|
|||||||
## Faraday 2.0
|
## Faraday 2.0
|
||||||
|
|
||||||
|
### Adapters have moved!
|
||||||
|
|
||||||
|
With this release, we've officially moved all adapters, except for the `net_http` one, 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.
|
||||||
|
|
||||||
|
**NOTE: the `net_http` adapter was originally removed as well in version `2.0`, but quickly reintroduced in version `2.0.1`.
|
||||||
|
We strongly suggest you to skip version `2.0` and instead use version `2.0.1` or greater.**
|
||||||
|
|
||||||
|
#### 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 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, except for the `net_http` one, have already been moved out and released as separate gems.
|
||||||
|
They've then been re-added into Faraday's v1 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.
|
||||||
|
|
||||||
|
#### That's great! What should I change in my code immediately after upgrading?
|
||||||
|
|
||||||
|
* If you just use the default `net_http` adapter, then you don't need to do anything!
|
||||||
|
* Otherwise, add the corresponding adapter gem to your Gemfile (e.g. `faraday-net_http_persistent`). Then, simply require them after you require `faraday`.
|
||||||
|
```ruby
|
||||||
|
# Gemfile
|
||||||
|
gem 'faraday'
|
||||||
|
gem 'faraday-net_http_persistent'
|
||||||
|
|
||||||
|
# Code
|
||||||
|
require 'faraday'
|
||||||
|
require 'faraday/net_http_persistent'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Faraday Middleware Deprecation
|
||||||
|
|
||||||
|
In case you never used it, [Faraday Middleware](https://github.com/lostisland/faraday_middleware) is a handy collection of middleware that have so far been maintained by the Faraday core team.
|
||||||
|
With Faraday 2.0 focusing on becoming an ecosystem, rather than an out-of-the-box solution, it only made sense to take the same approach for middleware as we did for adapters. For this reason, `faraday_middleware` will not be updated to support Faraday 2.0.
|
||||||
|
Instead, we'll support the transition from centralised-repo collection of middleware to individual middleware gems, effectively replicating what we did with adapters. Each middleware will have its own repository and gem, and it will allow developers to only add the ones they require to their Gemfile.
|
||||||
|
|
||||||
|
So don't fret yet! We're doing our best to support our `faraday_middleware` users out there, so here are the steps we've taken to make this work:
|
||||||
|
* We've promoted the highly popular JSON middleware (both request encoding and response parsing) to a core middleware and it will now be shipped together with Faraday. We expect many `faraday_middleware` users will be able to just stop using the extra dependency thanks to this.
|
||||||
|
* We've created a [faraday-middleware-template](https://github.com/lostisland/faraday-middleware-template) repository that will make creating a new middleware gem simple and straightforward.
|
||||||
|
* We've added a middleware section to the [awesome-faraday](https://github.com/lostisland/awesome-faraday) repo, so you can easily find available middleware when you need it.
|
||||||
|
|
||||||
|
It will obviously take time for some of the middleware in `faraday_middleware` to make its way into a separate gem, so we appreciate this might be an upgrade obstacle for some. However this is part of an effort to focus the core team resources tackling the most requested features.
|
||||||
|
We'll be listening to the community and prioritize middleware that are most used, and will be welcoming contributors who want to become owners of the middleware when these become separate from the `faraday_middleware` repo.
|
||||||
|
|
||||||
|
### Bundled middleware moved out
|
||||||
|
|
||||||
|
Moving middleware into its own gem makes sense not only for `faraday_middleware`, but also for middleware bundled with Faraday.
|
||||||
|
As of v2.0, the `retry` and `multipart` middleware have been moved to separate `faraday-retry` and `faraday-multipart` gems.
|
||||||
|
These have been identified as good candidates due to their complexity and external dependencies.
|
||||||
|
Thanks to this change, we were able to make Faraday 2.0 completely dependency free 🎉 (the only exception being `ruby2_keywords`, which will be necessary only while we keep supporting Ruby 2.6).
|
||||||
|
|
||||||
|
#### So what should I do if I currently use the `retry` or `multipart` middleware?
|
||||||
|
|
||||||
|
Upgrading is pretty simple, because the middleware was simply moved out to external gems.
|
||||||
|
All you need to do is to add them to your gemfile (either `faraday-retry` or `faraday-multipart` and require them before usage:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Gemfile
|
||||||
|
gem 'faraday-multipart'
|
||||||
|
gem 'faraday-retry'
|
||||||
|
|
||||||
|
# Connection initializer
|
||||||
|
require 'faraday/retry'
|
||||||
|
require 'faraday/multipart'
|
||||||
|
|
||||||
|
conn = Faraday.new(url) do |f|
|
||||||
|
f.request :multipart
|
||||||
|
f.request :retry
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Autoloading and dependencies
|
||||||
|
|
||||||
|
Faraday has until now provided and relied on a complex dynamic dependencies system.
|
||||||
|
This would allow to reference classes and require dependencies only when needed (e.g. when running the first request) based
|
||||||
|
on the middleware/adapters used.
|
||||||
|
As part of Faraday v2.0, we've removed all external dependencies, which means we don't really need this anymore.
|
||||||
|
This change should not affect you directly, but if you're registering middleware then be aware of the new syntax:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# `name` here can be anything you want.
|
||||||
|
# `klass` is your custom middleware class.
|
||||||
|
# This method can also be called on `Faraday::Adapter`, `Faraday::Request` and `Faraday::Response`
|
||||||
|
Faraday::Middleware.register_middleware(name: klass)
|
||||||
|
```
|
||||||
|
|
||||||
|
The `register_middleware` method also previously allowed to provide a symbol, string, proc, or array
|
||||||
|
but this has been removed from the v2.0 release to simplify the interface.
|
||||||
|
(EDIT: symbol/string/proc have subsequently been reintroduced in v2.2, but not the array).
|
||||||
|
|
||||||
|
### Authentication helper methods in Connection have been removed
|
||||||
|
|
||||||
|
You were previously able to call `authorization`, `basic_auth` and `token_auth` against the `Connection` object, but this helper methods have now been dropped.
|
||||||
|
Instead, you should be using the equivalent middleware, as explained in [this page](https://lostisland.github.io/faraday/#/middleware/included/authentication).
|
||||||
|
|
||||||
|
For more details, see https://github.com/lostisland/faraday/pull/1306
|
||||||
|
|
||||||
|
### The `dependency` method in middlewares has been removed
|
||||||
|
|
||||||
|
In Faraday 1, a deferred require was used with the `dependency` method.
|
||||||
|
|
||||||
|
In Faraday 2, that method has been removed. In your middleware gems, use a regular `require` at the top of the file,
|
||||||
|
|
||||||
### Others
|
### Others
|
||||||
|
|
||||||
* Rename `Faraday::Request#method` to `#http_method`.
|
* Rename `Faraday::Request#method` to `#http_method`.
|
||||||
|
* Remove `Faraday::Response::Middleware`. You can now use the new `on_complete` callback provided by `Faraday::Middleware`.
|
||||||
|
* `Faraday.default_connection_options` will now be deep-merged into new connections to avoid overriding them (e.g. headers).
|
||||||
|
* `Faraday::Builder#build` method is not exposed through `Faraday::Connection` anymore and does not reset the handlers if called multiple times. This method should be used internally only.
|
||||||
|
|
||||||
## Faraday 1.0
|
## Faraday 1.0
|
||||||
|
|
||||||
|
15
bin/console
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env ruby
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'bundler/setup'
|
||||||
|
require 'faraday'
|
||||||
|
|
||||||
|
# You can add fixtures and/or initialization code here to make experimenting
|
||||||
|
# with your gem easier. You can also use a different console, if you like.
|
||||||
|
|
||||||
|
# (If you use this, don't forget to add pry to your Gemfile!)
|
||||||
|
# require "pry"
|
||||||
|
# Pry.start
|
||||||
|
|
||||||
|
require 'irb'
|
||||||
|
IRB.start(__FILE__)
|
7
bin/setup
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
set -vx
|
||||||
|
|
||||||
|
gem install bundler
|
||||||
|
bundle install --jobs 4
|
7
bin/test
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
IFS=$'\n\t'
|
||||||
|
set -vx
|
||||||
|
|
||||||
|
bundle exec rubocop -a --format progress
|
||||||
|
bundle exec rspec
|
3
config/external.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
faraday-net_http:
|
||||||
|
url: https://github.com/lostisland/faraday-net_http.git
|
||||||
|
command: bundle exec rspec
|
3
docs/.gitignore
vendored
@ -1,3 +0,0 @@
|
|||||||
_site
|
|
||||||
.sass-cache
|
|
||||||
.jekyll-metadata
|
|
0
docs/.nojekyll
Normal file
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
layout: default
|
|
||||||
---
|
|
||||||
|
|
||||||
<style type="text/css" media="screen">
|
|
||||||
.container {
|
|
||||||
margin: 10px auto;
|
|
||||||
max-width: 600px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
margin: 30px 0;
|
|
||||||
font-size: 4em;
|
|
||||||
line-height: 1;
|
|
||||||
letter-spacing: -1px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<h1>404</h1>
|
|
||||||
|
|
||||||
<p><strong>Page not found :(</strong></p>
|
|
||||||
<p>The requested page could not be found.</p>
|
|
||||||
</div>
|
|
34
docs/Gemfile
@ -1,34 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
|
||||||
|
|
||||||
# Hello! This is where you manage which Jekyll version is used to run.
|
|
||||||
# When you want to use a different version, change it below, save the
|
|
||||||
# file and run `bundle install`. Run Jekyll with `bundle exec`, like so:
|
|
||||||
#
|
|
||||||
# bundle exec jekyll serve
|
|
||||||
#
|
|
||||||
# This will help ensure the proper Jekyll version is running.
|
|
||||||
# Happy Jekylling!
|
|
||||||
# gem "jekyll", "~> 3.7.4"
|
|
||||||
|
|
||||||
# This is the default theme for new Jekyll sites.
|
|
||||||
# You may change this to anything you like.
|
|
||||||
# gem "minima", "~> 2.0"
|
|
||||||
# gem "jekyll-theme-type"
|
|
||||||
gem 'jekyll-remote-theme'
|
|
||||||
|
|
||||||
# If you want to use GitHub Pages, remove the "gem "jekyll"" above and
|
|
||||||
# uncomment the line below. To upgrade, run `bundle update github-pages`.
|
|
||||||
gem 'github-pages', group: :jekyll_plugins
|
|
||||||
|
|
||||||
# If you have any plugins, put them here!
|
|
||||||
group :jekyll_plugins do
|
|
||||||
gem 'jekyll-feed', '~> 0.6'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
|
||||||
gem 'tzinfo-data', platforms: %i[mingw mswin x64_mingw jruby]
|
|
||||||
|
|
||||||
# Performance-booster for watching directories on Windows
|
|
||||||
gem 'wdm', '~> 0.1.0' if Gem.win_platform?
|
|
@ -1,21 +1,21 @@
|
|||||||
# Faraday Website
|
# Faraday Docs
|
||||||
|
|
||||||
This is the root directory of the [Faraday Website][website].
|
Faraday Docs are powered by [Docsify](https://docsify.js.org/#/).
|
||||||
If you want to apply changes to it, please test it locally using `Jekyll`.
|
|
||||||
|
|
||||||
Here is how:
|
## Development
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
From the Faraday project root, run the following:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Navigate into the /docs folder
|
npm install # this will install the necessary dependencies
|
||||||
$ cd docs
|
|
||||||
|
|
||||||
# Install Jekyll dependencies, this bundle is different from Faraday's one.
|
|
||||||
$ bundle install
|
|
||||||
|
|
||||||
# Run the Jekyll server with the Faraday website
|
|
||||||
$ bundle exec jekyll serve
|
|
||||||
|
|
||||||
# The site will now be reachable at http://127.0.0.1:4000/faraday/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
[website]: https://lostisland.github.io/faraday
|
### Running the Docs Locally
|
||||||
|
|
||||||
|
To preview your changes locally, run the following:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run docs
|
||||||
|
```
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
# SITE CONFIGURATION
|
|
||||||
url: 'https://lostisland.github.io'
|
|
||||||
baseurl: '/faraday'
|
|
||||||
repository: 'lostisland/faraday'
|
|
||||||
|
|
||||||
# THEME-SPECIFIC CONFIGURATION
|
|
||||||
theme_settings:
|
|
||||||
title: Faraday
|
|
||||||
avatar: assets/img/logo.png
|
|
||||||
favicon: assets/img/favicon.png
|
|
||||||
# email: your-email@example.com
|
|
||||||
description: >-
|
|
||||||
Simple, but flexible HTTP client library, with support for multiple backends
|
|
||||||
footer_text: "© 2009 - 2020, the <a class=\"body-link\" href=\"/faraday/team\">Faraday Team</a>. Website and branding design by <a href=\"https://elelopic.design\" target=\"_blank\">Elena Lo Piccolo</a>."
|
|
||||||
|
|
||||||
# Icons
|
|
||||||
github: 'lostisland/faraday'
|
|
||||||
gitter: 'lostisland/faraday'
|
|
||||||
|
|
||||||
# Post navigation
|
|
||||||
post_navigation: true
|
|
||||||
site_navigation_sort: 'order'
|
|
||||||
|
|
||||||
# BUILD SETTINGS
|
|
||||||
markdown: kramdown
|
|
||||||
remote_theme: rohanchandra/type-theme
|
|
||||||
|
|
||||||
plugins:
|
|
||||||
- jekyll-feed
|
|
||||||
- jekyll-remote-theme
|
|
||||||
|
|
||||||
# GitHub settings
|
|
||||||
lsi: false
|
|
||||||
safe: true
|
|
||||||
#source: [your repo's top level directory]
|
|
||||||
incremental: false
|
|
||||||
highlighter: rouge
|
|
||||||
gist:
|
|
||||||
noscript: false
|
|
||||||
kramdown:
|
|
||||||
math_engine: mathjax
|
|
||||||
syntax_highlighter: rouge
|
|
||||||
|
|
||||||
# Exclude from processing.
|
|
||||||
# The following items will not be processed, by default. Create a custom list
|
|
||||||
# to override the default setting.
|
|
||||||
exclude:
|
|
||||||
- Gemfile
|
|
||||||
- Gemfile.lock
|
|
||||||
- README.md
|
|
||||||
- node_modules
|
|
||||||
- vendor/bundle/
|
|
||||||
- vendor/cache/
|
|
||||||
- vendor/gems/
|
|
||||||
- vendor/ruby/
|
|
@ -1,18 +0,0 @@
|
|||||||
|
|
||||||
<div class="docs-nav">
|
|
||||||
<p class="docs-nav-item">
|
|
||||||
{% if page.prev_link %}
|
|
||||||
<a href="{{page.prev_link}}"><i class="fa fa-angle-left"></i> {{ page.prev_name }}</a>
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
<p class="docs-nav-item">
|
|
||||||
{% if page.top_link %}
|
|
||||||
<a href="{{ page.top_link }}"><i class="fa fa-angle-up"></i> {{ page.top_name }}</a>
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
<p class="docs-nav-item">
|
|
||||||
{% if page.next_link %}
|
|
||||||
<a href="{{ page.next_link }}">{{ page.next_name }} <i class="fa fa-angle-right"></i></a>
|
|
||||||
{% endif %}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
@ -1,21 +0,0 @@
|
|||||||
<!-- Directly extracted from https://github.com/rohanchandra/type-theme/blob/c6ec5a69ff7dfe2df193be08515193c72bd4a55d/_includes/footer.html
|
|
||||||
for customization (DocSearch feature) -->
|
|
||||||
|
|
||||||
{% if site.theme_settings.katex and page.id %}
|
|
||||||
<script src="{{ "/assets/js/katex_init.js" | relative_url }}"></script>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if site.theme_settings.footer_text %}
|
|
||||||
<footer class="site-footer">
|
|
||||||
<p class="text">{{ site.theme_settings.footer_text }}</p>
|
|
||||||
</footer>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"></script>
|
|
||||||
<script type="text/javascript"> docsearch({
|
|
||||||
apiKey: '4b20e432aa42a515b1a465b1494df8bf',
|
|
||||||
indexName: 'lostisland_faraday',
|
|
||||||
inputSelector: '#search-box',
|
|
||||||
debug: false // Set debug to true if you want to inspect the dropdown
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,59 +0,0 @@
|
|||||||
<!-- This is extracted directly from https://github.com/rohanchandra/type-theme/blob/c6ec5a69ff7dfe2df193be08515193c72bd4a55d/_includes/header.html
|
|
||||||
for customization (DocSearch feature) -->
|
|
||||||
<header class="site-header">
|
|
||||||
<div class="branding">
|
|
||||||
{% if site.theme_settings.gravatar %}
|
|
||||||
<a href="{{ site.baseurl }}/">
|
|
||||||
<img class="avatar" src="https://secure.gravatar.com/avatar/{{ site.theme_settings.gravatar }}?s=100" alt=""/>
|
|
||||||
</a>
|
|
||||||
{% elsif site.theme_settings.avatar %}
|
|
||||||
<a href="{{ site.baseurl }}/">
|
|
||||||
<img class="avatar" src="{{ site.baseurl }}/{{ site.theme_settings.avatar }}" alt=""/>
|
|
||||||
</a>
|
|
||||||
{% endif %}
|
|
||||||
<h1 class="site-title">
|
|
||||||
<a href="{{ site.baseurl }}/">{{ site.theme_settings.title }}</a>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<nav class="site-nav">
|
|
||||||
<ul>
|
|
||||||
{% if site.theme_settings.site_navigation_sort %}
|
|
||||||
{% assign site_pages = site.pages | sort: site.theme_settings.site_navigation_sort %}
|
|
||||||
{% else %}
|
|
||||||
{% assign site_pages = site.pages %}
|
|
||||||
{% endif %}
|
|
||||||
{% for page in site_pages %}
|
|
||||||
{% if page.title and page.hide != true %}
|
|
||||||
<li>
|
|
||||||
<a class="page-link" href="{{ page.url | relative_url }}">
|
|
||||||
{{ page.title }}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
<!-- Social icons from Font Awesome, if enabled -->
|
|
||||||
{% include icons.html %}
|
|
||||||
|
|
||||||
<!-- Search bar -->
|
|
||||||
{% if site.theme_settings.search %}
|
|
||||||
<li>
|
|
||||||
<form action="{{ site.baseurl }}/search.html" method="get">
|
|
||||||
<input type="text" id="search-box" name="query" placeholder="Search" class="">
|
|
||||||
<button type="submit" class="">
|
|
||||||
<i class="fa fa-fw fa-search"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
<li>
|
|
||||||
<form id="search-form">
|
|
||||||
<input type="text" id="search-box" placeholder="Search">
|
|
||||||
<button type="submit">
|
|
||||||
<i class="fa fa-fw fa-search"></i>
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
</header>
|
|
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
layout: page
|
|
||||||
---
|
|
||||||
|
|
||||||
{{ content }}
|
|
||||||
|
|
||||||
{% include docs_nav.md %}
|
|
Before Width: | Height: | Size: 700 B After Width: | Height: | Size: 700 B |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
BIN
docs/_media/logo.png
Normal file
After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 225 KiB After Width: | Height: | Size: 225 KiB |
BIN
docs/_media/overview.png
Normal file
After Width: | Height: | Size: 225 KiB |
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 186 KiB |
Before Width: | Height: | Size: 358 KiB After Width: | Height: | Size: 358 KiB |
@ -1,25 +0,0 @@
|
|||||||
---
|
|
||||||
layout: post
|
|
||||||
title: "Welcome to Jekyll!"
|
|
||||||
date: 2019-03-12 10:25:23 +0000
|
|
||||||
categories: jekyll update
|
|
||||||
---
|
|
||||||
You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated.
|
|
||||||
|
|
||||||
To add new posts, simply add a file in the `_posts` directory that follows the convention `YYYY-MM-DD-name-of-post.ext` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works.
|
|
||||||
|
|
||||||
Jekyll also offers powerful support for code snippets:
|
|
||||||
|
|
||||||
{% highlight ruby %}
|
|
||||||
def print_hi(name)
|
|
||||||
puts "Hi, #{name}"
|
|
||||||
end
|
|
||||||
print_hi('Tom')
|
|
||||||
#=> prints 'Hi, Tom' to STDOUT.
|
|
||||||
{% endhighlight %}
|
|
||||||
|
|
||||||
Check out the [Jekyll docs][jekyll-docs] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll Talk][jekyll-talk].
|
|
||||||
|
|
||||||
[jekyll-docs]: https://jekyllrb.com/docs/home
|
|
||||||
[jekyll-gh]: https://github.com/jekyll/jekyll
|
|
||||||
[jekyll-talk]: https://talk.jekyllrb.com/
|
|
@ -1,9 +0,0 @@
|
|||||||
// Override theme variables.
|
|
||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css?family=Raleway:700');
|
|
||||||
|
|
||||||
$link-color: #EE4266;
|
|
||||||
$text-color: #3C3C3C;
|
|
||||||
$font-family-main: 'KohinoorTelugu-Regular', Helvetica, Arial, sans-serif;
|
|
||||||
$font-family-headings: 'Raleway', Helvetica, Arial, sans-serif;
|
|
||||||
$search-color: #EE4266;
|
|
@ -1,129 +0,0 @@
|
|||||||
// Custom Styles added on top of the theme.
|
|
||||||
|
|
||||||
.btn
|
|
||||||
display: inline-block
|
|
||||||
background-color: $link-color
|
|
||||||
padding: 5px 10px
|
|
||||||
box-shadow: 0 4px 10px 5px rgba(238, 66, 102, 0.30)
|
|
||||||
border-radius: 20px
|
|
||||||
width: 200px
|
|
||||||
color: #FFFFFF
|
|
||||||
letter-spacing: -0.41px
|
|
||||||
text-align: center
|
|
||||||
margin: 0 10px
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background-color: darken($link-color, 10%)
|
|
||||||
color: white
|
|
||||||
text-decoration: none
|
|
||||||
|
|
||||||
.text-center
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
.mt-60
|
|
||||||
margin-top: 60px
|
|
||||||
|
|
||||||
.hidden
|
|
||||||
display: none
|
|
||||||
|
|
||||||
.docs-nav
|
|
||||||
display: flex
|
|
||||||
margin-top: 40px
|
|
||||||
|
|
||||||
.docs-nav-item
|
|
||||||
flex: 1 1 0
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
pre.highlight
|
|
||||||
padding: 20px
|
|
||||||
background-color: #F6F6F6
|
|
||||||
border-radius: 4px
|
|
||||||
|
|
||||||
code
|
|
||||||
word-wrap: normal
|
|
||||||
overflow: scroll
|
|
||||||
|
|
||||||
code.highlighter-rouge
|
|
||||||
background-color: #EEE
|
|
||||||
padding: 0 5px
|
|
||||||
border-radius: 3px
|
|
||||||
|
|
||||||
.site-header .site-nav li
|
|
||||||
margin-right: 1.2em
|
|
||||||
|
|
||||||
h1, h2, h3, h4, h5, h6
|
|
||||||
font-weight: bold
|
|
||||||
|
|
||||||
.feature-image header
|
|
||||||
@media (max-width: 1000px)
|
|
||||||
padding: 7% 12.5%
|
|
||||||
@media (max-width: 576px)
|
|
||||||
padding: 4% 5% 1% 5%
|
|
||||||
|
|
||||||
#team-content
|
|
||||||
h3
|
|
||||||
margin: 30px 0
|
|
||||||
|
|
||||||
#contributors-list
|
|
||||||
text-align: justify
|
|
||||||
|
|
||||||
.team-tile
|
|
||||||
width: 200px
|
|
||||||
display: inline-block
|
|
||||||
margin: 0 20px
|
|
||||||
|
|
||||||
img
|
|
||||||
width: 100%
|
|
||||||
border-radius: 50%
|
|
||||||
|
|
||||||
footer
|
|
||||||
background-color: #f1f3f4
|
|
||||||
|
|
||||||
#active-maintainers-list, #historical-team-list
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
#loader
|
|
||||||
margin-top: 20%
|
|
||||||
margin-bottom: 20%
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
.lds-ring
|
|
||||||
display: inline-block
|
|
||||||
position: relative
|
|
||||||
width: 200px
|
|
||||||
height: 200px
|
|
||||||
|
|
||||||
.lds-ring div
|
|
||||||
box-sizing: border-box
|
|
||||||
display: block
|
|
||||||
position: absolute
|
|
||||||
width: 187px
|
|
||||||
height: 187px
|
|
||||||
margin: 6px
|
|
||||||
border: 12px solid $link-color
|
|
||||||
border-radius: 50%
|
|
||||||
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite
|
|
||||||
border-color: $link-color transparent transparent transparent
|
|
||||||
|
|
||||||
.lds-ring div:nth-child(1)
|
|
||||||
animation-delay: -0.45s
|
|
||||||
|
|
||||||
.lds-ring div:nth-child(2)
|
|
||||||
animation-delay: -0.3s
|
|
||||||
|
|
||||||
.lds-ring div:nth-child(3)
|
|
||||||
animation-delay: -0.15s
|
|
||||||
|
|
||||||
@keyframes lds-ring
|
|
||||||
0%
|
|
||||||
transform: rotate(0deg)
|
|
||||||
|
|
||||||
100%
|
|
||||||
transform: rotate(360deg)
|
|
||||||
|
|
||||||
#search-form
|
|
||||||
display: inline-block
|
|
||||||
// You need a minimal window size for the search to display well, should be ok, people are on desktop
|
|
||||||
// when needing search usually
|
|
||||||
@media screen and (max-width: 800px)
|
|
||||||
display: none
|
|
33
docs/_sidebar.md
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
* Getting Started
|
||||||
|
* [Quick Start](getting-started/quick-start.md)
|
||||||
|
* [The Env Object](getting-started/env-object.md)
|
||||||
|
* [Dealing with Errors](getting-started/errors.md)
|
||||||
|
* [Migrating from rest-client](getting-started/rest-client-migration.md)
|
||||||
|
* Customization
|
||||||
|
* [Configuration](customization/index.md)
|
||||||
|
* [Connection Options](customization/connection-options.md)
|
||||||
|
* [Request Options](customization/request-options.md)
|
||||||
|
* [SSL Options](customization/ssl-options.md)
|
||||||
|
* [Proxy Options](customization/proxy-options.md)
|
||||||
|
* Middleware
|
||||||
|
* [Overview](middleware/index.md)
|
||||||
|
* [Included](middleware/included/index.md)
|
||||||
|
* [Authentication](middleware/included/authentication.md)
|
||||||
|
* [URL Encoding](middleware/included/url-encoding.md)
|
||||||
|
* [JSON Encoding/Decoding](middleware/included/json.md)
|
||||||
|
* [Instrumentation](middleware/included/instrumentation.md)
|
||||||
|
* [Logging](middleware/included/logging.md)
|
||||||
|
* [Raising Errors](middleware/included/raising-errors.md)
|
||||||
|
* [Writing custom middleware](middleware/custom-middleware.md)
|
||||||
|
* Adapters
|
||||||
|
* [Overview](adapters/index.md)
|
||||||
|
* [Net::HTTP](adapters/net-http.md)
|
||||||
|
* [Test Adapter](adapters/test-adapter.md)
|
||||||
|
* Writing custom adapters
|
||||||
|
* [Overview](adapters/custom/index.md)
|
||||||
|
* [Parallel Requests](adapters/custom/parallel-requests.md)
|
||||||
|
* [Streaming Responses](adapters/custom/streaming.md)
|
||||||
|
* [Test your adapter](adapters/custom/testing.md)
|
||||||
|
* Advanced Features
|
||||||
|
* [Parallel Requests](advanced/parallel-requests.md)
|
||||||
|
* [Streaming Responses](advanced/streaming-responses.md)
|
161
docs/adapters/custom/index.md
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
# Writing custom adapters
|
||||||
|
|
||||||
|
!> A template for writing your own middleware is available in the [faraday-adapter-template](https://github.com/lostisland/faraday-adapter-template) repository.
|
||||||
|
|
||||||
|
Adapters have methods that can help you implement support for a new backend.
|
||||||
|
|
||||||
|
This example will use a fictional HTTP backend gem called `FlorpHttp`. It doesn't
|
||||||
|
exist. Its only function is to make this example more concrete.
|
||||||
|
|
||||||
|
## An Adapter _is_ a Middleware
|
||||||
|
|
||||||
|
When you subclass `Faraday::Adapter`, you get helpful methods defined and all you need to do is to
|
||||||
|
extend the `call` method (remember to call `super` inside it!):
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
module Faraday
|
||||||
|
class Adapter
|
||||||
|
class FlorpHttp < Faraday::Adapter
|
||||||
|
def call(env)
|
||||||
|
super
|
||||||
|
# Perform the request and call `save_response`
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, there are only two things which are actually mandatory for an adapter middleware to function:
|
||||||
|
|
||||||
|
- a `#call` implementation
|
||||||
|
- a call to `#save_response` inside `#call`, which will keep the Response around.
|
||||||
|
|
||||||
|
These are the only two things, the rest of this text is about methods which make the authoring easier.
|
||||||
|
|
||||||
|
Like any other middleware, the `env` parameter passed to `#call` is an instance of [Faraday::Env][env-object].
|
||||||
|
This object will contain all the information about the request, as well as the configuration of the connection.
|
||||||
|
Your adapter is expected to deal with SSL and Proxy settings, as well as any other configuration options.
|
||||||
|
|
||||||
|
## Connection options and configuration block
|
||||||
|
|
||||||
|
Users of your adapter have two main ways of configuring it:
|
||||||
|
* connection options: these can be passed to your adapter initializer and are automatically stored into an instance variable `@connection_options`.
|
||||||
|
* configuration block: this can also be provided to your adapter initializer and it's stored into an instance variable `@config_block`.
|
||||||
|
|
||||||
|
Both of these are automatically managed by `Faraday::Adapter#initialize`, so remember to call it with `super` if you create an `initialize` method in your adapter.
|
||||||
|
You can then use them in your adapter code as you wish, since they're pretty flexible.
|
||||||
|
|
||||||
|
Below is an example of how they can be used:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# You can use @connection_options and @config_block in your adapter code
|
||||||
|
class FlorpHttp < Faraday::Adapter
|
||||||
|
def call(env)
|
||||||
|
# `connection` internally calls `build_connection` and yields the result
|
||||||
|
connection do |conn|
|
||||||
|
# perform the request using configured `conn`
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_connection(env)
|
||||||
|
conn = FlorpHttp::Client.new(pool_size: @connection_options[:pool_size] || 10)
|
||||||
|
@config_block&.call(conn)
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Then your users can provide them when initializing the connection
|
||||||
|
Faraday.new(...) do |f|
|
||||||
|
# ...
|
||||||
|
# in this example, { pool_size: 5 } will be provided as `connection_options`
|
||||||
|
f.adapter :florp_http, pool_size: 5 do |client|
|
||||||
|
# this block is useful to set properties unique to HTTP clients that are not
|
||||||
|
# manageable through the Faraday API
|
||||||
|
client.some_fancy_florp_http_property = 10
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementing `#close`
|
||||||
|
|
||||||
|
Just like middleware, adapters can implement a `#close` method. It will be called when the connection is closed.
|
||||||
|
In this method, you should close any resources that you opened in `#initialize` or `#call`, like sockets or files.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class FlorpHttp < Faraday::Adapter
|
||||||
|
def close
|
||||||
|
@socket.close if @socket
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Helper methods
|
||||||
|
|
||||||
|
`Faraday::Adapter` provides some helper methods to make it easier to implement adapters.
|
||||||
|
|
||||||
|
### `#save_response`
|
||||||
|
|
||||||
|
The most important helper method and the only one you're expected to call from your `#call` method.
|
||||||
|
This method is responsible for, among other things, the following:
|
||||||
|
* Take the `env` object and save the response into it.
|
||||||
|
* Set the `:response` key in the `env` object.
|
||||||
|
* Parse headers using `Utils::Headers` and set the `:response_headers` key in the `env` object.
|
||||||
|
* Call `#finish` on the `Response` object, triggering the `#on_complete` callbacks in the middleware stack.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class FlorpHttp < Faraday::Adapter
|
||||||
|
def call(env)
|
||||||
|
super
|
||||||
|
# Perform the request using FlorpHttp.
|
||||||
|
# Returns a FlorpHttp::Response object.
|
||||||
|
response = FlorpHttp.perform_request(...)
|
||||||
|
|
||||||
|
save_response(env, response.status, response.body, response.headers, response.reason_phrase)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
`#save_response` also accepts a `finished` keyword argument, which defaults to `true`, but that you can set to false
|
||||||
|
if you don't want it to call `#finish` on the `Response` object.
|
||||||
|
|
||||||
|
### `#request_timeout`
|
||||||
|
|
||||||
|
Most HTTP libraries support different types of timeouts, like `:open_timeout`, `:read_timeout` and `:write_timeout`.
|
||||||
|
Faraday let you set individual values for each of these, as well as a more generic `:timeout` value on the request options.
|
||||||
|
|
||||||
|
This helper method knows about supported timeout types, and falls back to `:timeout` if they are not set.
|
||||||
|
You can use those when building the options you need for your backend's instantiation.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class FlorpHttp < Faraday::Adapter
|
||||||
|
def call(env)
|
||||||
|
super
|
||||||
|
# Perform the request using FlorpHttp.
|
||||||
|
# Returns a FlorpHttp::Response object.
|
||||||
|
response = FlorpHttp.perform_request(
|
||||||
|
# ... other options ...,
|
||||||
|
open_timeout: request_timeout(:open, env[:request]),
|
||||||
|
read_timeout: request_timeout(:read, env[:request]),
|
||||||
|
write_timeout: request_timeout(:write, env[:request])
|
||||||
|
)
|
||||||
|
|
||||||
|
# Call save_response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Register your adapter
|
||||||
|
|
||||||
|
Like middleware, you may register a nickname for your adapter.
|
||||||
|
People can then refer to your adapter with that name when initializing their connection.
|
||||||
|
You do that using `Faraday::Adapter.register_middleware`, like this:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class FlorpHttp < Faraday::Adapter
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
|
||||||
|
Faraday::Adapter.register_middleware(florp_http: FlorpHttp)
|
||||||
|
```
|
||||||
|
|
||||||
|
[env-object]: /getting-started/env-object.md
|
75
docs/adapters/custom/parallel-requests.md
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# Adding support for parallel requests
|
||||||
|
|
||||||
|
!> This is slightly more involved, and this section is not fully formed.
|
||||||
|
|
||||||
|
Vague example, excerpted from [the test suite about parallel requests](https://github.com/lostisland/faraday/blob/main/spec/support/shared_examples/request_method.rb#L179)
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
response_1 = nil
|
||||||
|
response_2 = nil
|
||||||
|
|
||||||
|
conn.in_parallel do
|
||||||
|
response_1 = conn.get('/about')
|
||||||
|
response_2 = conn.get('/products')
|
||||||
|
end
|
||||||
|
|
||||||
|
puts response_1.status
|
||||||
|
puts response_2.status
|
||||||
|
```
|
||||||
|
|
||||||
|
First, in your class definition, you can tell Faraday that your backend supports parallel operation:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class FlorpHttp < ::Faraday::Adapter
|
||||||
|
dependency do
|
||||||
|
require 'florp_http'
|
||||||
|
end
|
||||||
|
|
||||||
|
self.supports_parallel = true
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, implement a method which returns a ParallelManager:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class FlorpHttp < ::Faraday::Adapter
|
||||||
|
dependency do
|
||||||
|
require 'florp_http'
|
||||||
|
end
|
||||||
|
|
||||||
|
self.supports_parallel = true
|
||||||
|
|
||||||
|
def self.setup_parallel_manager(_options = nil)
|
||||||
|
FlorpParallelManager.new # NB: we will need to define this
|
||||||
|
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
|
||||||
|
|
||||||
|
class FlorpParallelManager
|
||||||
|
# The execute method will be passed the same block as `in_parallel`,
|
||||||
|
# so you can either collect the requests or just wrap them into a wrapper,
|
||||||
|
# depending on how your adapter works.
|
||||||
|
def execute(&block)
|
||||||
|
run_async(&block)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### A note on the old, deprecated interface
|
||||||
|
|
||||||
|
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).
|
79
docs/adapters/custom/streaming.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# Adding support for streaming
|
||||||
|
|
||||||
|
Faraday supports streaming responses, which means that the response body is not loaded into memory all at once,
|
||||||
|
but instead it is read in chunks. This can be particularly useful when dealing with large responses.
|
||||||
|
Not all HTTP clients support this, so it is not required for adapters to support it.
|
||||||
|
|
||||||
|
However, if you do want to support it in your adapter, you can do so by leveraging helpers provided by the env object.
|
||||||
|
Let's see an example implementation first with some comments, and then we'll explain it in more detail:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
module Faraday
|
||||||
|
class Adapter
|
||||||
|
class FlorpHttp < Faraday::Adapter
|
||||||
|
def call(env)
|
||||||
|
super
|
||||||
|
if env.stream_response? # check if the user wants to stream the response
|
||||||
|
# start a streaming response.
|
||||||
|
# on_data is a block that will let users consume the response body
|
||||||
|
http_response = env.stream_response do |&on_data|
|
||||||
|
# perform the request using FlorpHttp
|
||||||
|
# the block will be called for each chunk of data
|
||||||
|
FlorpHttp.perform_request(...) do |chunk|
|
||||||
|
on_data.call(chunk)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# the body is already consumed by the block
|
||||||
|
# so it's good practice to set it to nil
|
||||||
|
http_response.body = nil
|
||||||
|
else
|
||||||
|
# perform the request normally, no streaming.
|
||||||
|
http_response = FlorpHttp.perform_request(...)
|
||||||
|
end
|
||||||
|
save_response(env, http_response.status, http_response.body, http_response.headers, http_response.reason_phrase)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
### `#stream_response?`
|
||||||
|
|
||||||
|
The first helper method we use is `#stream_response?`. This method is provided by the env object and it returns true
|
||||||
|
if the user wants to stream the response. This is controlled by the presence of an `on_data` callback in the request options.
|
||||||
|
|
||||||
|
### `#stream_response`
|
||||||
|
|
||||||
|
The second helper is `#stream_response`. This method is also provided by the env object and it takes a block.
|
||||||
|
The block will be called with a single argument, which is a callback that the user can use to consume the response body.
|
||||||
|
All your adapter needs to do, is to call this callback with each chunk of data that you receive from the server.
|
||||||
|
|
||||||
|
The `on_data` callback will internally call the callback provided by the user, so you don't need to worry about that.
|
||||||
|
It will also keep track of the number of bytes that have been read, and pass that information to the user's callback.
|
||||||
|
|
||||||
|
To see how this all works together, let's see an example of how a user would use this feature:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# A buffer to store the streamed data
|
||||||
|
streamed = []
|
||||||
|
|
||||||
|
conn = Faraday.new('http://httpbingo.org')
|
||||||
|
conn.get('/stream/100') do |req|
|
||||||
|
# Set a callback which will receive tuples of chunk Strings,
|
||||||
|
# the sum of characters received so far, and the response environment.
|
||||||
|
# The latter will allow access to the response status, headers and reason, as well as the request info.
|
||||||
|
req.options.on_data = proc do |chunk, overall_received_bytes, env|
|
||||||
|
puts "Received #{overall_received_bytes} characters"
|
||||||
|
streamed << chunk
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Joins all response chunks together
|
||||||
|
streamed.join
|
||||||
|
```
|
||||||
|
|
||||||
|
For more details on the user experience, check the [Streaming Responses] page.
|
||||||
|
|
||||||
|
[Streaming Responses]: /advanced/streaming-responses.md
|
60
docs/adapters/custom/testing.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Test your custom adapter
|
||||||
|
|
||||||
|
Faraday puts a lot of expectations on adapters, but it also provides you with useful tools to test your adapter
|
||||||
|
against those expectations. This guide will walk you through the process of testing your adapter.
|
||||||
|
|
||||||
|
## The adapter test suite
|
||||||
|
|
||||||
|
Faraday provides a test suite that you can use to test your adapter.
|
||||||
|
The test suite is located in the `spec/external_adapters/faraday_specs_setup.rb`.
|
||||||
|
|
||||||
|
All you need to do is to `require 'faraday_specs_setup'` in your adapter's `spec_helper.rb` file.
|
||||||
|
This will load the `an adapter` shared example group that you can use to test your adapter.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'faraday_specs_setup'
|
||||||
|
|
||||||
|
RSpec.describe Faraday::Adapter::FlorpHttp do
|
||||||
|
it_behaves_like 'an adapter'
|
||||||
|
|
||||||
|
# You can then add any other test specific to this adapter here...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing optional features
|
||||||
|
|
||||||
|
By default, `an adapter` will test your adapter against the required behaviour for an adapter.
|
||||||
|
However, there are some optional "features" that your adapter can implement, like parallel requests or streaming.
|
||||||
|
|
||||||
|
If your adapter implements any of those optional features, you can test it against those extra expectations
|
||||||
|
by calling the `features` method:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
RSpec.describe Faraday::Adapter::MyAdapter do
|
||||||
|
# Since not all adapters support all the features Faraday has to offer, you can use
|
||||||
|
# the `features` method to turn on only the ones you know you can support.
|
||||||
|
features :request_body_on_query_methods,
|
||||||
|
:compression,
|
||||||
|
:streaming
|
||||||
|
|
||||||
|
# Runs the tests provide by Faraday, according to the features specified above.
|
||||||
|
it_behaves_like 'an adapter'
|
||||||
|
|
||||||
|
# You can then add any other test specific to this adapter here...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available features
|
||||||
|
|
||||||
|
| Feature | Description |
|
||||||
|
|----------------------------------|----------------------------------------------------------------------------------------------------------|
|
||||||
|
| `:compression` | Tests that your adapter can handle `gzip` and `deflate` compressions. |
|
||||||
|
| `:local_socket_binding` | Tests that your adapter supports binding to a local socket via the `:bind` request option. |
|
||||||
|
| `:parallel` | Tests that your adapter supports parallel requests. See [Parallel requests][parallel] for more details. |
|
||||||
|
| `:reason_phrase_parse` | Tests that your adapter supports parsing the `reason_phrase` from the response. |
|
||||||
|
| `:request_body_on_query_methods` | Tests that your adapter supports sending a request body on `GET`, `HEAD`, `DELETE` and `TRACE` requests. |
|
||||||
|
| `:streaming` | Tests that your adapter supports streaming responses. See [Streaming][streaming] for more details. |
|
||||||
|
| `:trace_method` | Tests your adapter against the `TRACE` HTTP method. |
|
||||||
|
|
||||||
|
[streaming]: /adapters/custom/streaming.md
|
||||||
|
[parallel]: /adapters/custom/parallel-requests.md
|
@ -1,72 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "EM-HTTP Adapter"
|
|
||||||
permalink: /adapters/em-http
|
|
||||||
hide: true
|
|
||||||
top_name: Adapters
|
|
||||||
top_link: ./
|
|
||||||
---
|
|
||||||
|
|
||||||
This Adapter uses [EventMachine](https://github.com/eventmachine/eventmachine/) and the tie in [em-http-request][src]
|
|
||||||
|
|
||||||
It can be used to make parallel requests using EventMachine.
|
|
||||||
|
|
||||||
The major difference between this and EMSynchrony is that it does not use fibers.
|
|
||||||
|
|
||||||
**Error handling and responses have a slightly different behaviour and structure in some cases. Please run thorough testing scenarios, including connection failures and SSL failures**
|
|
||||||
|
|
||||||
You will need to add em-http-request to your Gemfile:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Gemfile
|
|
||||||
gem 'em-http-request'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Base request
|
|
||||||
```ruby
|
|
||||||
require 'faraday'
|
|
||||||
require 'em-http-request'
|
|
||||||
|
|
||||||
conn = Faraday.new(...) do |f|
|
|
||||||
# no custom options available
|
|
||||||
f.adapter :em_http
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Parallel Requests
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
require 'faraday'
|
|
||||||
require 'em-http-request'
|
|
||||||
|
|
||||||
urls = Array.new(5) { 'http://127.0.0.1:3000' }
|
|
||||||
|
|
||||||
conn = Faraday::Connection.new do |builder|
|
|
||||||
builder.adapter :em_http
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
conn.in_parallel do
|
|
||||||
puts "Parallel manager: #{conn.parallel_manager}"
|
|
||||||
|
|
||||||
@responses = urls.map do |url|
|
|
||||||
conn.get(url)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Gather responses outside of block
|
|
||||||
puts @responses.map(&:status).join(', ')
|
|
||||||
puts @responses.map(&:status).compact.count
|
|
||||||
```
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
* [Gem RDoc][rdoc]
|
|
||||||
* [Gem source][src]
|
|
||||||
* [Adapter RDoc][adapter_rdoc]
|
|
||||||
* [EM-Synchrony Adapter](./em-synchrony.md)
|
|
||||||
|
|
||||||
[rdoc]: https://www.rubydoc.info/gems/em-http-request
|
|
||||||
[src]: https://github.com/igrigorik/em-http-request#readme
|
|
||||||
[adapter_rdoc]: https://www.rubydoc.info/github/lostisland/faraday/Faraday/Adapter/EMHttp
|
|
@ -1,78 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "EM-Synchrony Adapter"
|
|
||||||
permalink: /adapters/em-synchrony
|
|
||||||
hide: true
|
|
||||||
top_name: Adapters
|
|
||||||
top_link: ./
|
|
||||||
---
|
|
||||||
|
|
||||||
This Adapter uses [EventMachine](https://github.com/eventmachine/eventmachine/) and the tie in [em-http-request](https://www.rubydoc.info/gems/em-http-request) in conjunction with [em-synchrony][rdoc]
|
|
||||||
|
|
||||||
It can be used to make parallel requests using EventMachine.
|
|
||||||
|
|
||||||
The key difference between this and EM-Http is that it uses fibers. For more information see igrigorik's blog posts on the matter:
|
|
||||||
|
|
||||||
- [fibers-cooperative-scheduling-in-ruby](https://www.igvita.com/2009/05/13/fibers-cooperative-scheduling-in-ruby/)
|
|
||||||
- [untangling-evented-code-with-ruby-fibers](https://www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers)
|
|
||||||
|
|
||||||
**Error handling and responses have a slightly different behaviour and structure in some cases. Please run thorough testing scenarios, including connection failures and SSL failures**
|
|
||||||
|
|
||||||
You will need to add em-http-request and em-synchrony to your Gemfile:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Gemfile
|
|
||||||
gem 'em-http-request'
|
|
||||||
gem 'em-synchrony'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Base request
|
|
||||||
```ruby
|
|
||||||
require 'faraday'
|
|
||||||
require 'em-http-request'
|
|
||||||
require 'em-synchrony'
|
|
||||||
|
|
||||||
conn = Faraday.new(...) do |f|
|
|
||||||
# no custom options available
|
|
||||||
f.adapter :em_synchrony
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Parallel Requests
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
require 'faraday'
|
|
||||||
require 'em-http-request'
|
|
||||||
require 'em-synchrony'
|
|
||||||
|
|
||||||
urls = Array.new(5) { 'http://127.0.0.1:3000' }
|
|
||||||
|
|
||||||
conn = Faraday::Connection.new do |builder|
|
|
||||||
builder.adapter :em_synchrony
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
conn.in_parallel do
|
|
||||||
puts "Parallel manager: #{conn.parallel_manager}"
|
|
||||||
|
|
||||||
@responses = urls.map do |url|
|
|
||||||
conn.get(url)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Gather responses outside of block
|
|
||||||
puts @responses.map(&:status).join(', ')
|
|
||||||
puts @responses.map(&:status).compact.count
|
|
||||||
```
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
* [Gem RDoc][rdoc]
|
|
||||||
* [Gem source][src]
|
|
||||||
* [Adapter RDoc][adapter_rdoc]
|
|
||||||
* [EM-HTTP Adapter](./em-http.md)
|
|
||||||
|
|
||||||
[rdoc]: https://www.rubydoc.info/gems/em-synchrony
|
|
||||||
[src]: https://github.com/igrigorik/em-synchrony
|
|
||||||
[adapter_rdoc]: https://www.rubydoc.info/github/lostisland/faraday/Faraday/Adapter/EMSynchrony
|
|
@ -1,27 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Excon Adapter"
|
|
||||||
permalink: /adapters/excon
|
|
||||||
hide: true
|
|
||||||
top_name: Adapters
|
|
||||||
top_link: ./
|
|
||||||
---
|
|
||||||
|
|
||||||
This Adapter uses the [excon][rdoc] gem to make HTTP requests.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
conn = Faraday.new(...) do |f|
|
|
||||||
# no custom options available
|
|
||||||
f.adapter :excon
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
* [Gem RDoc][rdoc]
|
|
||||||
* [Gem source][src]
|
|
||||||
* [Adapter RDoc][adapter_rdoc]
|
|
||||||
|
|
||||||
[rdoc]: https://www.rubydoc.info/gems/excon
|
|
||||||
[src]: https://github.com/excon/excon
|
|
||||||
[adapter_rdoc]: https://www.rubydoc.info/github/lostisland/faraday/Faraday/Adapter/Excon
|
|
@ -1,30 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "HTTPClient Adapter"
|
|
||||||
permalink: /adapters/httpclient
|
|
||||||
hide: true
|
|
||||||
top_name: Adapters
|
|
||||||
top_link: ./
|
|
||||||
---
|
|
||||||
|
|
||||||
This Adapter uses the [httpclient][rdoc] gem to make HTTP requests.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
conn = Faraday.new(...) do |f|
|
|
||||||
f.adapter :httpclient do |client|
|
|
||||||
# yields HTTPClient
|
|
||||||
client.keep_alive_timeout = 20
|
|
||||||
client.ssl_config.timeout = 25
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
* [Gem RDoc][rdoc]
|
|
||||||
* [Gem source][src]
|
|
||||||
* [Adapter RDoc][adapter_rdoc]
|
|
||||||
|
|
||||||
[rdoc]: https://www.rubydoc.info/gems/httpclient
|
|
||||||
[src]: https://github.com/nahi/httpclient
|
|
||||||
[adapter_rdoc]: https://www.rubydoc.info/github/lostisland/faraday/Faraday/Adapter/HTTPClient
|
|
@ -1,186 +1,46 @@
|
|||||||
---
|
# Adapters
|
||||||
layout: documentation
|
|
||||||
title: "Adapters"
|
|
||||||
permalink: /adapters/
|
|
||||||
order: 2
|
|
||||||
---
|
|
||||||
|
|
||||||
The Faraday Adapter interface determines how a Faraday request is turned into
|
The Faraday Adapter interface determines how a Faraday request is turned into
|
||||||
a Faraday response object. Adapters are typically implemented with common Ruby
|
a Faraday response object. Adapters are typically implemented with common Ruby
|
||||||
HTTP clients, but can have custom implementations. Adapters can be configured
|
HTTP clients, but can have custom implementations. Adapters can be configured
|
||||||
either globally or per Faraday Connection through the configuration block.
|
either globally or per Faraday Connection through the configuration block.
|
||||||
|
|
||||||
{: .mt-60}
|
For example, consider using `httpclient` as an adapter. Note that [faraday-httpclient](https://github.com/lostisland/faraday-httpclient) must be installed beforehand.
|
||||||
## Built-in adapters
|
|
||||||
|
|
||||||
Faraday includes these adapters (but not the HTTP client libraries):
|
If you want to configure it globally, do the following:
|
||||||
|
|
||||||
* [Net::HTTP][net_http] _(this is the default adapter)_
|
```ruby
|
||||||
* [Net::HTTP::Persistent][persistent]
|
require 'faraday'
|
||||||
* [Excon][excon]
|
require 'faraday/httpclient'
|
||||||
* [Patron][patron]
|
|
||||||
* [EM-Synchrony][em-synchrony]
|
Faraday.default_adapter = :httpclient
|
||||||
* [EM-Http][em-http]
|
```
|
||||||
* [HTTPClient][httpclient]
|
|
||||||
|
If you want to configure it per Faraday Connection, do the following:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'faraday'
|
||||||
|
require 'faraday/httpclient'
|
||||||
|
|
||||||
|
conn = Faraday.new do |f|
|
||||||
|
f.adapter :httpclient
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fantastic adapters and where to find them
|
||||||
|
|
||||||
|
Except for the default [Net::HTTP][net_http] adapter and the [Test Adapter][testing] adapter, which is for _test purposes only_,
|
||||||
|
adapters are distributed separately from Faraday and need to be manually installed.
|
||||||
|
They are usually available as gems, or bundled with HTTP clients.
|
||||||
|
|
||||||
While most adapters use a common Ruby HTTP client library, adapters can also
|
While most adapters use a common Ruby HTTP client library, adapters can also
|
||||||
have completely custom implementations.
|
have completely custom implementations.
|
||||||
|
|
||||||
* [Test Adapter][testing]
|
If you're just getting started you can find a list of featured adapters in [Awesome Faraday][awesome].
|
||||||
* Rack Adapter (link TBD)
|
Anyone can create a Faraday adapter and distribute it. If you're interested learning more, check how to [build your own][build_adapters]!
|
||||||
|
|
||||||
## External adapters
|
|
||||||
|
|
||||||
Adapters are slowly being moved into their own gems, or bundled with HTTP clients.
|
[testing]: /adapters/test-adapter.md
|
||||||
Please refer to their documentation for usage examples.
|
[net_http]: /adapters/net-http.md
|
||||||
|
[awesome]: https://github.com/lostisland/awesome-faraday/#adapters
|
||||||
* [Typhoeus][typhoeus]
|
[build_adapters]: /adapters/custom/index.md
|
||||||
* [HTTP.rb][faraday-http]
|
|
||||||
* [httpx][httpx]
|
|
||||||
|
|
||||||
## Ad-hoc adapters customization
|
|
||||||
|
|
||||||
Faraday is intended to be a generic interface between your code and the adapter.
|
|
||||||
However, sometimes you need to access a feature specific to one of the adapters that is not covered in Faraday's interface.
|
|
||||||
When that happens, you can pass a block when specifying the adapter to customize it.
|
|
||||||
The block parameter will change based on the adapter you're using. See each adapter page for more details.
|
|
||||||
|
|
||||||
## Write your own adapter
|
|
||||||
|
|
||||||
Adapters have methods that can help you implement support for a new backend.
|
|
||||||
|
|
||||||
This example will use a fictional HTTP backend gem called `FlorpHttp`. It doesn't
|
|
||||||
exist. Its only function is to make this example more concrete.
|
|
||||||
|
|
||||||
### An Adapter _is_ a Middleware
|
|
||||||
|
|
||||||
When you subclass `::Faraday::Adapter`, you get helpful methods defined:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class FlorpHttp < ::Faraday::Adapter
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, there are only two things which are actually mandatory for an adapter middleware to function:
|
|
||||||
|
|
||||||
- a `#call` implementation
|
|
||||||
- a call to `#save_response` inside `#call`, which will keep the Response around.
|
|
||||||
|
|
||||||
These are the only two things.
|
|
||||||
|
|
||||||
The rest of this text is about methods which make the authoring easier.
|
|
||||||
|
|
||||||
### Helpful method: `#build_connection`
|
|
||||||
|
|
||||||
Faraday abstracts all your backend's concrete stuff behind its user-facing API.
|
|
||||||
You take care of setting up the connection from the supplied parameters.
|
|
||||||
|
|
||||||
Example from the excon adapter: it gets an `Env` and reads its information
|
|
||||||
to instantiate an `Excon` object:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class FlorpHttp < ::Faraday::Adapter
|
|
||||||
def build_connection(env)
|
|
||||||
opts = opts_from_env(env)
|
|
||||||
::Excon.new(env[:url].to_s, opts.merge(@connection_options))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
The `env` contains stuff like:
|
|
||||||
|
|
||||||
- `env[:ssl]`
|
|
||||||
- `env[:request]`
|
|
||||||
|
|
||||||
There are helper to fetch timeouts: `#request_timeout(type, options)` knows
|
|
||||||
about supported timeout types, and falls back to `:timeout` if they are not set.
|
|
||||||
You can use those when building the options you need for your backend's instantiation.
|
|
||||||
|
|
||||||
So, use the information provided in `env` to instantiate your backend's connection class.
|
|
||||||
Return that instance. Now, Faraday knows how to create and reuse that connection.
|
|
||||||
|
|
||||||
### Nickname for your adapter: `.register_middleware`
|
|
||||||
|
|
||||||
You may register a nickname for your adapter. People can then refer to your adapter with that name.
|
|
||||||
You do that using `.register_middleware`, like this:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class FlorpHttp < ::Faraday::Adapter
|
|
||||||
register_middleware(
|
|
||||||
File.expand_path('adapter', __dir__),
|
|
||||||
florp_http: [ :FlorpHttp, 'florp_http' ]
|
|
||||||
)
|
|
||||||
# ...
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Does your backend support parallel operation?
|
|
||||||
|
|
||||||
:warning: This is slightly more involved, and this section is not fully formed.
|
|
||||||
|
|
||||||
Vague example, excerpted from [the test suite about parallel requests](https://github.com/lostisland/faraday/blob/master/spec/support/shared_examples/request_method.rb#L179)
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
response_1 = nil
|
|
||||||
response_2 = nil
|
|
||||||
|
|
||||||
conn.in_parallel do
|
|
||||||
response_1 = conn.get('/about')
|
|
||||||
response_2 = conn.get('/products')
|
|
||||||
end
|
|
||||||
|
|
||||||
puts response_1.status
|
|
||||||
puts response_2.status
|
|
||||||
```
|
|
||||||
|
|
||||||
First, in your class definition, you can tell Faraday that your backend supports parallel operation:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class FlorpHttp < ::Faraday::Adapter
|
|
||||||
dependency do
|
|
||||||
require 'florp_http'
|
|
||||||
end
|
|
||||||
|
|
||||||
self.supports_parallel = true
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, implement a method which returns a ParallelManager:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
class FlorpHttp < ::Faraday::Adapter
|
|
||||||
dependency do
|
|
||||||
require 'florp_http'
|
|
||||||
end
|
|
||||||
|
|
||||||
self.supports_parallel = true
|
|
||||||
|
|
||||||
def self.setup_parallel_manager(_options = nil)
|
|
||||||
FlorpParallelManager.new # NB: we will need to define this
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class FlorpParallelManager
|
|
||||||
def add(request, method, *args, &block)
|
|
||||||
# Collect the requests
|
|
||||||
end
|
|
||||||
|
|
||||||
def run
|
|
||||||
# Process the requests
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Compare to the finished example [em-synchrony](https://github.com/lostisland/faraday/blob/master/lib/faraday/adapter/em_synchrony.rb) and its [ParallelManager implementation](https://github.com/lostisland/faraday/blob/master/lib/faraday/adapter/em_synchrony/parallel_manager.rb).
|
|
||||||
|
|
||||||
[net_http]: ./net-http
|
|
||||||
[persistent]: ./net-http-persistent
|
|
||||||
[excon]: ./excon
|
|
||||||
[patron]: ./patron
|
|
||||||
[em-synchrony]: ./em-synchrony
|
|
||||||
[em-http]: ./em-http
|
|
||||||
[httpclient]: ./httpclient
|
|
||||||
[typhoeus]: https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb
|
|
||||||
[faraday-http]: https://github.com/lostisland/faraday-http
|
|
||||||
[testing]: ./testing
|
|
||||||
[httpx]: https://honeyryderchuck.gitlab.io/httpx/wiki/Faraday-Adapter
|
|
||||||
|
12
docs/adapters/net-http.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Net::HTTP Adapter
|
||||||
|
|
||||||
|
Faraday's Net::HTTP adapter is the default adapter. It uses the `Net::HTTP`
|
||||||
|
library that ships with Ruby's standard library.
|
||||||
|
Unless you have a specific reason to use a different adapter, this is probably
|
||||||
|
the adapter you want to use.
|
||||||
|
|
||||||
|
With the release of Faraday 2.0, the Net::HTTP adapter has been moved into a [separate gem][faraday-net_http],
|
||||||
|
but it has also been added as a dependency of Faraday.
|
||||||
|
That means you can use it without having to install it or require it explicitly.
|
||||||
|
|
||||||
|
[faraday-net_http]: https://github.com/lostisland/faraday-net_http
|
@ -1,31 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Net::HTTP Adapter"
|
|
||||||
permalink: /adapters/net-http
|
|
||||||
hide: true
|
|
||||||
top_name: Adapters
|
|
||||||
top_link: ./
|
|
||||||
---
|
|
||||||
|
|
||||||
This Adapter uses the [`Net::HTTP`][rdoc] client from the Ruby standard library to make
|
|
||||||
HTTP requests.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
conn = Faraday.new(...) do |f|
|
|
||||||
f.adapter :net_http do |http|
|
|
||||||
# yields Net::HTTP
|
|
||||||
http.idle_timeout = 100
|
|
||||||
http.verify_callback = lambda do |preverify, cert_store|
|
|
||||||
# do something here...
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
* [Net::HTTP RDoc][rdoc]
|
|
||||||
* [Adapter RDoc][adapter_rdoc]
|
|
||||||
|
|
||||||
[rdoc]: http://ruby-doc.org/stdlib/libdoc/net/http/rdoc/Net/HTTP.html
|
|
||||||
[adapter_rdoc]: https://www.rubydoc.info/github/lostisland/faraday/Faraday/Adapter/NetHttp
|
|
@ -1,29 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Net::HTTP::Persistent Adapter"
|
|
||||||
permalink: /adapters/net-http-persistent
|
|
||||||
hide: true
|
|
||||||
top_name: Adapters
|
|
||||||
top_link: ./
|
|
||||||
---
|
|
||||||
|
|
||||||
This Adapter uses the [net-http-persistent][rdoc] gem to make HTTP requests.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
conn = Faraday.new(...) do |f|
|
|
||||||
f.adapter :net_http_persistent, pool_size: 5 do |http|
|
|
||||||
# yields Net::HTTP::Persistent
|
|
||||||
http.idle_timeout = 100
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
* [Gem RDoc][rdoc]
|
|
||||||
* [Gem source][src]
|
|
||||||
* [Adapter RDoc][adapter_rdoc]
|
|
||||||
|
|
||||||
[rdoc]: https://www.rubydoc.info/gems/net-http-persistent
|
|
||||||
[src]: https://github.com/drbrain/net-http-persistent
|
|
||||||
[adapter_rdoc]: https://www.rubydoc.info/github/lostisland/faraday/Faraday/Adapter/NetHttpPersistent
|
|
@ -1,29 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Patron Adapter"
|
|
||||||
permalink: /adapters/patron
|
|
||||||
hide: true
|
|
||||||
top_name: Adapters
|
|
||||||
top_link: ./
|
|
||||||
---
|
|
||||||
|
|
||||||
This Adapter uses the [patron][rdoc] gem to make HTTP requests.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
conn = Faraday.new(...) do |f|
|
|
||||||
f.adapter :patron do |session|
|
|
||||||
# yields Patron::Session
|
|
||||||
session.max_redirects = 10
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
* [Gem RDoc][rdoc]
|
|
||||||
* [Gem source][src]
|
|
||||||
* [Adapter RDoc][adapter_rdoc]
|
|
||||||
|
|
||||||
[rdoc]: https://www.rubydoc.info/gems/patron
|
|
||||||
[src]: https://github.com/toland/patron
|
|
||||||
[adapter_rdoc]: https://www.rubydoc.info/github/lostisland/faraday/Faraday/Adapter/Patron
|
|
@ -1,11 +1,4 @@
|
|||||||
---
|
# Test Adapter
|
||||||
layout: documentation
|
|
||||||
title: "Testing"
|
|
||||||
permalink: /adapters/testing
|
|
||||||
hide: true
|
|
||||||
top_name: Adapters
|
|
||||||
top_link: ./
|
|
||||||
---
|
|
||||||
|
|
||||||
The built-in Faraday Test adapter lets you define stubbed HTTP requests. This can
|
The built-in Faraday Test adapter lets you define stubbed HTTP requests. This can
|
||||||
be used to mock out network services in an application's unit tests.
|
be used to mock out network services in an application's unit tests.
|
||||||
@ -33,7 +26,7 @@ conn = Faraday.new do |builder|
|
|||||||
|
|
||||||
# test exceptions too
|
# test exceptions too
|
||||||
stub.get('/boom') do
|
stub.get('/boom') do
|
||||||
raise Faraday::ConnectionFailed, nil
|
raise Faraday::ConnectionFailed
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -64,6 +57,14 @@ initialized. This is useful for testing.
|
|||||||
stubs.get('/uni') { |env| [ 200, {}, 'urchin' ]}
|
stubs.get('/uni') { |env| [ 200, {}, 'urchin' ]}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also stub the request body with a string or a proc.
|
||||||
|
It would be useful to pass a proc if it's OK only to check the parts of the request body are passed.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
stubs.post('/kohada', 'where=sea&temperature=24') { |env| [ 200, {}, 'spotted gizzard shad' ]}
|
||||||
|
stubs.post('/anago', -> (request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'Wakamoto' } }) { |env| [200, {}, 'conger eel'] }
|
||||||
|
```
|
||||||
|
|
||||||
If you want to stub requests that exactly match a path, parameters, and headers,
|
If you want to stub requests that exactly match a path, parameters, and headers,
|
||||||
`strict_mode` would be useful.
|
`strict_mode` would be useful.
|
||||||
|
|
||||||
@ -112,5 +113,5 @@ Faraday.default_connection = nil
|
|||||||
Working [RSpec] and [test/unit] examples for a fictional JSON API client are
|
Working [RSpec] and [test/unit] examples for a fictional JSON API client are
|
||||||
available.
|
available.
|
||||||
|
|
||||||
[RSpec]: https://github.com/lostisland/faraday/blob/master/examples/client_spec.rb
|
[RSpec]: https://github.com/lostisland/faraday/blob/main/examples/client_spec.rb
|
||||||
[test/unit]: https://github.com/lostisland/faraday/blob/master/examples/client_test.rb
|
[test/unit]: https://github.com/lostisland/faraday/blob/main/examples/client_test.rb
|
58
docs/advanced/parallel-requests.md
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Parallel Requests
|
||||||
|
|
||||||
|
Some adapters support running requests in parallel.
|
||||||
|
This can be achieved using the `#in_parallel` method on the connection object.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Install the Typhoeus adapter with `gem install faraday-typhoeus` first.
|
||||||
|
require 'faraday/typhoeus'
|
||||||
|
|
||||||
|
conn = Faraday.new('http://httpbingo.org') do |faraday|
|
||||||
|
faraday.adapter :typhoeus
|
||||||
|
end
|
||||||
|
|
||||||
|
now = Time.now
|
||||||
|
|
||||||
|
conn.in_parallel do
|
||||||
|
conn.get('/delay/3')
|
||||||
|
conn.get('/delay/3')
|
||||||
|
end
|
||||||
|
|
||||||
|
# This should take about 3 seconds, not 6.
|
||||||
|
puts "Time taken: #{Time.now - now}"
|
||||||
|
```
|
||||||
|
|
||||||
|
## A note on Async
|
||||||
|
|
||||||
|
You might have heard about [Async] and its native integration with Ruby 3.0.
|
||||||
|
The good news is that you can already use Async with Faraday (thanks to the [async-http-faraday] gem)
|
||||||
|
and this does not require the use of `#in_parallel` to run parallel requests.
|
||||||
|
Instead, you only need to wrap your Faraday code into an Async block:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Install the Async adapter with `gem install async-http-faraday` first.
|
||||||
|
require 'async/http/faraday'
|
||||||
|
|
||||||
|
conn = Faraday.new('http://httpbingo.org') do |faraday|
|
||||||
|
faraday.adapter :async_http
|
||||||
|
end
|
||||||
|
|
||||||
|
now = Time.now
|
||||||
|
|
||||||
|
# NOTE: This is not limited to a single connection anymore!
|
||||||
|
# You can run parallel requests spanning multiple connections.
|
||||||
|
Async do
|
||||||
|
Async { conn.get('/delay/3') }
|
||||||
|
Async { conn.get('/delay/3') }
|
||||||
|
end
|
||||||
|
|
||||||
|
# This should take about 3 seconds, not 6.
|
||||||
|
puts "Time taken: #{Time.now - now}"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
The big advantage of using Async is that you can now run parallel requests *spanning multiple connections*,
|
||||||
|
whereas the `#in_parallel` method only works for requests that are made through the same connection.
|
||||||
|
|
||||||
|
[Async]: https://github.com/socketry/async
|
||||||
|
[async-http-faraday]: https://github.com/socketry/async-http-faraday
|
35
docs/advanced/streaming-responses.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Streaming Responses
|
||||||
|
|
||||||
|
Sometimes you might need to receive a streaming response.
|
||||||
|
You can do this with the `on_data` request option.
|
||||||
|
|
||||||
|
The `on_data` callback is a receives tuples of chunk Strings, and the total
|
||||||
|
of received bytes so far.
|
||||||
|
|
||||||
|
This example implements such a callback:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# A buffer to store the streamed data
|
||||||
|
streamed = []
|
||||||
|
|
||||||
|
conn = Faraday.new('http://httpbingo.org')
|
||||||
|
conn.get('/stream/100') do |req|
|
||||||
|
# Set a callback which will receive tuples of chunk Strings,
|
||||||
|
# the sum of characters received so far, and the response environment.
|
||||||
|
# The latter will allow access to the response status, headers and reason, as well as the request info.
|
||||||
|
req.options.on_data = Proc.new do |chunk, overall_received_bytes, env|
|
||||||
|
puts "Received #{overall_received_bytes} characters"
|
||||||
|
streamed << chunk
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Joins all response chunks together
|
||||||
|
streamed.join
|
||||||
|
```
|
||||||
|
|
||||||
|
The `on_data` streaming is currently only supported by some adapters.
|
||||||
|
To see which ones, please refer to [Awesome Faraday][awesome] comparative table or check the adapter documentation.
|
||||||
|
Moreover, the `env` parameter was only recently added, which means some adapters may only have partial support
|
||||||
|
(i.e. only `chunk` and `overall_received_bytes` will be passed to your block).
|
||||||
|
|
||||||
|
[awesome]: https://github.com/lostisland/awesome-faraday/#adapters
|
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
---
|
|
||||||
|
|
||||||
@import "variables";
|
|
||||||
@import "type-theme";
|
|
||||||
@import "faraday";
|
|
||||||
@import "https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css"
|
|
@ -1,52 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg width="1920px" height="1024px" viewBox="0 0 1920 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
||||||
<!-- Generator: Sketch 55.1 (78136) - https://sketchapp.com -->
|
|
||||||
<title>Background and Stripes</title>
|
|
||||||
<desc>Created with Sketch.</desc>
|
|
||||||
<defs>
|
|
||||||
<linearGradient x1="0%" y1="50%" x2="113.817376%" y2="50%" id="linearGradient-1">
|
|
||||||
<stop stop-color="#F59D2A" offset="0%"></stop>
|
|
||||||
<stop stop-color="#F59A2C" offset="1%"></stop>
|
|
||||||
<stop stop-color="#EE6A4B" offset="21%"></stop>
|
|
||||||
<stop stop-color="#EA4D5F" offset="36%"></stop>
|
|
||||||
<stop stop-color="#E94266" offset="45%"></stop>
|
|
||||||
<stop stop-color="#D54F76" offset="54%"></stop>
|
|
||||||
<stop stop-color="#A0719F" offset="74%"></stop>
|
|
||||||
<stop stop-color="#52A3DB" offset="100%"></stop>
|
|
||||||
</linearGradient>
|
|
||||||
<path d="M1497.08184,1023 L1413.76949,1023 L1970,467.082708 L1970,550.348143 L1747.2718,772.950924 L1497.08184,1023 Z M1237.28591,1023 L1153.97357,1023 L1970,207.433075 L1970,290.698512 L1308.46073,951.865267 L1237.28591,1023 Z M977.1947,1023 L893.882349,1023 L1917.45873,-5.68434189e-13 L1970,-2.27373675e-13 L1970,30.7537554 L977.1947,1023 Z M716.514745,1023 L633.202393,1023 L1656.77878,-2.27373675e-13 L1740.09113,-2.01923225e-13 L716.514745,1023 Z M455.834791,1023 L372.522439,1023 L1396.09882,-2.27373675e-13 L1479.41117,-1.73066477e-13 L455.834791,1023 Z M198.100371,1023 L114.787781,1023 L1138.36416,-3.41060513e-13 L1221.67675,-1.44535795e-13 L198.100371,1023 Z" id="path-2"></path>
|
|
||||||
<filter x="-4.5%" y="-4.5%" width="109.0%" height="109.0%" filterUnits="objectBoundingBox" id="filter-4">
|
|
||||||
<feGaussianBlur stdDeviation="0" in="SourceGraphic"></feGaussianBlur>
|
|
||||||
</filter>
|
|
||||||
<filter x="-2.9%" y="-2.9%" width="105.8%" height="105.8%" filterUnits="objectBoundingBox" id="filter-5">
|
|
||||||
<feGaussianBlur stdDeviation="0" in="SourceGraphic"></feGaussianBlur>
|
|
||||||
</filter>
|
|
||||||
<filter x="-2.1%" y="-2.1%" width="104.3%" height="104.3%" filterUnits="objectBoundingBox" id="filter-6">
|
|
||||||
<feGaussianBlur stdDeviation="0" in="SourceGraphic"></feGaussianBlur>
|
|
||||||
</filter>
|
|
||||||
<filter x="-2.1%" y="-2.1%" width="104.0%" height="104.1%" filterUnits="objectBoundingBox" id="filter-7">
|
|
||||||
<feGaussianBlur stdDeviation="0" in="SourceGraphic"></feGaussianBlur>
|
|
||||||
</filter>
|
|
||||||
<filter x="-2.0%" y="-2.1%" width="104.0%" height="104.1%" filterUnits="objectBoundingBox" id="filter-8">
|
|
||||||
<feGaussianBlur stdDeviation="0" in="SourceGraphic"></feGaussianBlur>
|
|
||||||
</filter>
|
|
||||||
<filter x="-2.0%" y="-2.1%" width="104.0%" height="104.1%" filterUnits="objectBoundingBox" id="filter-9">
|
|
||||||
<feGaussianBlur stdDeviation="0" in="SourceGraphic"></feGaussianBlur>
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
<g id="Background-and-Stripes" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
||||||
<rect id="Rectangle" fill="#EDEBEB" fill-rule="nonzero" x="0" y="0" width="1922" height="1023"></rect>
|
|
||||||
<mask id="mask-3" fill="white">
|
|
||||||
<use xlink:href="#path-2"></use>
|
|
||||||
</mask>
|
|
||||||
<use id="Combined-Shape" fill-opacity="0.5" fill="url(#linearGradient-1)" fill-rule="nonzero" xlink:href="#path-2"></use>
|
|
||||||
<g id="Line" transform="translate(148.000000, 0.000000)" stroke="#FFFFFF" stroke-linecap="square" stroke-width="30">
|
|
||||||
<path d="M1303.02503,1024 L1772,555.025033" filter="url(#filter-4)"></path>
|
|
||||||
<path d="M1045.02503,1024 L1772,297.025033" filter="url(#filter-5)"></path>
|
|
||||||
<path d="M786.025033,1024 L1772,38.0250327" filter="url(#filter-6)"></path>
|
|
||||||
<path d="M522.025037,1024 L1546.02504,0" filter="url(#filter-7)"></path>
|
|
||||||
<path d="M261.55292,1024 L1285.55292,0" filter="url(#filter-8)"></path>
|
|
||||||
<path d="M0.552920204,1024 L1024.55292,0" filter="url(#filter-9)"></path>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 28 KiB |
Before Width: | Height: | Size: 876 B |
@ -1,49 +0,0 @@
|
|||||||
function teamTile(member) {
|
|
||||||
console.log(member);
|
|
||||||
return '<div class="team-tile">' +
|
|
||||||
'<a href="' + member.author.html_url + '">' +
|
|
||||||
'<img src="' + member.author.avatar_url + '" />' +
|
|
||||||
'<p class="team-tile-login">' +
|
|
||||||
member.author.login +
|
|
||||||
'</p>' +
|
|
||||||
'</a>' +
|
|
||||||
'</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchTeam(json, team, div) {
|
|
||||||
let el = document.querySelector(div);
|
|
||||||
el.innerHTML = team.map(function (m) {
|
|
||||||
let index = json.findIndex(function(e) {
|
|
||||||
return e.author.login === m
|
|
||||||
});
|
|
||||||
return teamTile(json.splice(index, 1)[0]);
|
|
||||||
}).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
function fetchContributors(json) {
|
|
||||||
let el = document.querySelector('#contributors-list');
|
|
||||||
el.innerHTML = json.reverse().map(function (c) {
|
|
||||||
return '<span class="contributor"><a href="' + c.author.html_url + '">' + c.author.login + '</a></span>';
|
|
||||||
}).join('<b> · </b>');
|
|
||||||
}
|
|
||||||
|
|
||||||
function hideLoader() {
|
|
||||||
let el = document.querySelector('#loader');
|
|
||||||
el.classList.add('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
function showTeam() {
|
|
||||||
let el = document.querySelector('#team-content');
|
|
||||||
el.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch('https://api.github.com/repos/lostisland/faraday/stats/contributors')
|
|
||||||
.then(function (response) {
|
|
||||||
response.json().then(function (json) {
|
|
||||||
fetchTeam(json, ['technoweenie', 'iMacTia', 'olleolleolle'], '#active-maintainers-list');
|
|
||||||
fetchTeam(json, ['mislav', 'sferik'], '#historical-team-list');
|
|
||||||
fetchContributors(json);
|
|
||||||
hideLoader();
|
|
||||||
showTeam();
|
|
||||||
});
|
|
||||||
});
|
|
48
docs/customization/connection-options.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Connection Options
|
||||||
|
|
||||||
|
When initializing a new Faraday connection with `Faraday.new`, you can pass a hash of options to customize the connection.
|
||||||
|
All these options are optional.
|
||||||
|
|
||||||
|
| Option | Type | Default | Description |
|
||||||
|
|---------------------|-------------------|-----------------|---------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `:request` | Hash | nil | Hash of request options. Will be use to build [RequestOptions]. |
|
||||||
|
| `:proxy` | URI, String, Hash | nil | Proxy options, either as a URL or as a Hash of [ProxyOptions]. |
|
||||||
|
| `:ssl` | Hash | nil | Hash of SSL options. Will be use to build [SSLOptions]. |
|
||||||
|
| `:url` | URI, String | nil | URI or String base URL. This can also be passed as positional argument. |
|
||||||
|
| `:parallel_manager` | | nil | Default parallel manager to use. This is normally set by the adapter, but you have the option to override it. |
|
||||||
|
| `:params` | Hash | nil | URI query unencoded key/value pairs. |
|
||||||
|
| `:headers` | Hash | nil | Hash of unencoded HTTP header key/value pairs. |
|
||||||
|
| `:builder_class` | Class | RackBuilder | A custom class to use as the middleware stack builder. |
|
||||||
|
| `:builder` | Object | Rackbuilder.new | An instance of a custom class to use as the middleware stack builder. |
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
options = {
|
||||||
|
request: {
|
||||||
|
open_timeout: 5,
|
||||||
|
timeout: 5
|
||||||
|
},
|
||||||
|
proxy: {
|
||||||
|
uri: 'https://proxy.com',
|
||||||
|
user: 'proxy_user',
|
||||||
|
password: 'proxy_password'
|
||||||
|
},
|
||||||
|
ssl: {
|
||||||
|
ca_file: '/path/to/ca_file',
|
||||||
|
ca_path: '/path/to/ca_path',
|
||||||
|
verify: true
|
||||||
|
},
|
||||||
|
url: 'https://example.com',
|
||||||
|
params: { foo: 'bar' },
|
||||||
|
headers: { 'X-Api-Key' => 'secret', 'X-Api-Version' => '2' }
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = Faraday.new(options) do |faraday|
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
[RequestOptions]: /customization/request-options.md
|
||||||
|
[ProxyOptions]: /customization/proxy-options.md
|
||||||
|
[SSLOptions]: /customization/ssl-options.md
|
@ -1,28 +1,23 @@
|
|||||||
---
|
# Configuration
|
||||||
layout: documentation
|
|
||||||
title: "Customizing the Request"
|
|
||||||
permalink: /usage/customize
|
|
||||||
hide: true
|
|
||||||
top_name: Usage
|
|
||||||
top_link: ./
|
|
||||||
next_name: Streaming Responses
|
|
||||||
next_link: ./streaming
|
|
||||||
---
|
|
||||||
|
|
||||||
|
Faraday is highly configurable and allows you to customize the way requests are made.
|
||||||
|
This applies to both the connection and the request, but can also cover things like SSL and proxy settings.
|
||||||
|
|
||||||
|
Below are some examples of how to customize Faraday requests.
|
||||||
Configuration can be set up with the connection and/or adjusted per request.
|
Configuration can be set up with the connection and/or adjusted per request.
|
||||||
|
|
||||||
As connection options:
|
As connection options:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
conn = Faraday.new('http://sushi.com', request: { timeout: 5 })
|
conn = Faraday.new('http://httpbingo.org', request: { timeout: 5 })
|
||||||
conn.get('/search')
|
conn.get('/ip')
|
||||||
```
|
```
|
||||||
|
|
||||||
Or as per-request options:
|
Or as per-request options:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
conn.get do |req|
|
conn.get do |req|
|
||||||
req.url '/search'
|
req.url '/ip'
|
||||||
req.options.timeout = 5
|
req.options.timeout = 5
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
@ -32,7 +27,7 @@ This will be available in the `env` on all middleware.
|
|||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
conn.get do |req|
|
conn.get do |req|
|
||||||
req.url '/search'
|
req.url '/get'
|
||||||
req.options.context = {
|
req.options.context = {
|
||||||
foo: 'foo',
|
foo: 'foo',
|
||||||
bar: 'bar'
|
bar: 'bar'
|
||||||
@ -77,6 +72,20 @@ serializes POST bodies.
|
|||||||
|
|
||||||
The default encoder is `Faraday::NestedParamsEncoder`.
|
The default encoder is `Faraday::NestedParamsEncoder`.
|
||||||
|
|
||||||
|
### Order of parameters
|
||||||
|
|
||||||
|
By default, parameters are sorted by name while being serialized.
|
||||||
|
Since this is really useful to provide better cache management and most servers don't really care about parameters order, this is the default behaviour.
|
||||||
|
However you might find yourself dealing with a server that requires parameters to be in a specific order.
|
||||||
|
When that happens, you can configure the encoder to skip sorting them.
|
||||||
|
This configuration is supported by both the default `Faraday::NestedParamsEncoder` and `Faraday::FlatParamsEncoder`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday::NestedParamsEncoder.sort_params = false
|
||||||
|
# or
|
||||||
|
Faraday::FlatParamsEncoder.sort_params = false
|
||||||
|
```
|
||||||
|
|
||||||
## Proxy
|
## Proxy
|
||||||
|
|
||||||
Faraday will try to automatically infer the proxy settings from your system using [`URI#find_proxy`][ruby-find-proxy].
|
Faraday will try to automatically infer the proxy settings from your system using [`URI#find_proxy`][ruby-find-proxy].
|
30
docs/customization/proxy-options.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Proxy Options
|
||||||
|
|
||||||
|
Proxy options can be provided to the connection constructor or set on a per-request basis via [RequestOptions](/customization/request-options.md).
|
||||||
|
All these options are optional.
|
||||||
|
|
||||||
|
| Option | Type | Default | Description |
|
||||||
|
|-------------|-------------|---------|-----------------|
|
||||||
|
| `:uri` | URI, String | nil | Proxy URL. |
|
||||||
|
| `:user` | String | nil | Proxy user. |
|
||||||
|
| `:password` | String | nil | Proxy password. |
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Proxy options can be passed to the connection constructor and will be applied to all requests.
|
||||||
|
proxy_options = {
|
||||||
|
uri: 'http://proxy.example.com:8080',
|
||||||
|
user: 'username',
|
||||||
|
password: 'password'
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = Faraday.new(proxy: proxy_options) do |faraday|
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
|
||||||
|
# You can then override them on a per-request basis.
|
||||||
|
conn.get('/foo') do |req|
|
||||||
|
req.options.proxy.update(uri: 'http://proxy2.example.com:8080')
|
||||||
|
end
|
||||||
|
```
|
39
docs/customization/request-options.md
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Request Options
|
||||||
|
|
||||||
|
Request options can be provided to the connection constructor or set on a per-request basis.
|
||||||
|
All these options are optional.
|
||||||
|
|
||||||
|
| Option | Type | Default | Description |
|
||||||
|
|-------------------|-------------------|----------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||||
|
| `:params_encoder` | Class | `Faraday::Utils.nested_params_encoder` (`NestedParamsEncoder`) | A custom class to use as the params encoder. |
|
||||||
|
| `:proxy` | URI, String, Hash | nil | Proxy options, either as a URL or as a Hash of [ProxyOptions]. |
|
||||||
|
| `:bind` | Hash | nil | Hash of bind options. Requires the `:host` and `:port` keys. |
|
||||||
|
| `:timeout` | Integer, Float | nil (adapter default) | The max number of seconds to wait for the request to complete. |
|
||||||
|
| `:open_timeout` | Integer, Float | nil (adapter default) | The max number of seconds to wait for the connection to be established. |
|
||||||
|
| `:read_timeout` | Integer, Float | nil (adapter default) | The max number of seconds to wait for one block to be read. |
|
||||||
|
| `:write_timeout` | Integer, Float | nil (adapter default) | The max number of seconds to wait for one block to be written. |
|
||||||
|
| `:boundary` | String | nil | The boundary string for multipart requests. |
|
||||||
|
| `:context` | Hash | nil | Arbitrary data that you can pass to your request. |
|
||||||
|
| `:on_data` | Proc | nil | A callback that will be called when data is received. See [Streaming] |
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Request options can be passed to the connection constructor and will be applied to all requests.
|
||||||
|
request_options = {
|
||||||
|
params_encoder: Faraday::FlatParamsEncoder,
|
||||||
|
timeout: 5
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = Faraday.new(request: request_options) do |faraday|
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
|
||||||
|
# You can then override them on a per-request basis.
|
||||||
|
conn.get('/foo') do |req|
|
||||||
|
req.options.timeout = 10
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
[ProxyOptions]: /customization/proxy-options.md
|
||||||
|
[SSLOptions]: /advanced/streaming-responses.md
|
35
docs/customization/ssl-options.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# SSL Options
|
||||||
|
|
||||||
|
Faraday supports a number of SSL options, which can be provided while initializing the connection.
|
||||||
|
|
||||||
|
| Option | Type | Default | Description |
|
||||||
|
|--------------------|----------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `:verify` | Boolean | true | Verify SSL certificate. 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_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)). |
|
||||||
|
| `:cert_store` | OpenSSL::X509::Store | nil | OpenSSL certificate store. |
|
||||||
|
| `:client_cert` | OpenSSL::X509::Certificate | nil | Client certificate. |
|
||||||
|
| `:client_key` | OpenSSL::PKey::RSA, OpenSSL::PKey::DSA | nil | Client private key. |
|
||||||
|
| `:certificate` | OpenSSL::X509::Certificate | nil | Certificate (Excon only). |
|
||||||
|
| `:private_key` | OpenSSL::PKey::RSA | nil | Private key (Excon only). |
|
||||||
|
| `: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)). |
|
||||||
|
| `: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)). |
|
||||||
|
| `: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
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
ssl_options = {
|
||||||
|
ca_file: '/path/to/ca_file',
|
||||||
|
min_version: :TLS1_2
|
||||||
|
}
|
||||||
|
|
||||||
|
conn = Faraday.new(ssl: options) do |faraday|
|
||||||
|
# ...
|
||||||
|
end
|
||||||
|
```
|
51
docs/getting-started/env-object.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# The Env Object
|
||||||
|
|
||||||
|
Inspired by Rack, Faraday uses an `env` object to pass data between middleware.
|
||||||
|
This object is initialized at the beginning of the request and passed down the middleware stack.
|
||||||
|
The adapter is then responsible to run the HTTP request and set the `response` property on the `env` object,
|
||||||
|
which is then passed back up the middleware stack.
|
||||||
|
|
||||||
|
You can read more about how the `env` object is used in the [Middleware - How it works](/middleware/index?id=how-it-works) section.
|
||||||
|
|
||||||
|
Because of its nature, the `env` object is a complex structure that holds a lot of information and can
|
||||||
|
therefore be a bit intimidating at first. This page will try to explain the different properties of the `env` object.
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
Please also note that these properties are not all available at the same time: while configuration
|
||||||
|
and request properties are available at the beginning of the request, response properties are only
|
||||||
|
available after the request has been performed (i.e. in the `on_complete` callback of middleware).
|
||||||
|
|
||||||
|
|
||||||
|
| Property | Type | Request | Response | Description |
|
||||||
|
|---------------------|----------------------------|:------------------:|:------------------:|-----------------------------|
|
||||||
|
| `:method` | `Symbol` | :heavy_check_mark: | :heavy_check_mark: | The HTTP method to use. |
|
||||||
|
| `:request_body` | `String` | :heavy_check_mark: | :heavy_check_mark: | The request body. |
|
||||||
|
| `:url` | `URI` | :heavy_check_mark: | :heavy_check_mark: | The request URL. |
|
||||||
|
| `:request` | `Faraday::RequestOptions` | :heavy_check_mark: | :heavy_check_mark: | The request options. |
|
||||||
|
| `:request_headers` | `Faraday::Utils::Headers` | :heavy_check_mark: | :heavy_check_mark: | The request headers. |
|
||||||
|
| `:ssl` | `Faraday::SSLOptions` | :heavy_check_mark: | :heavy_check_mark: | The SSL options. |
|
||||||
|
| `:parallel_manager` | `Faraday::ParallelManager` | :heavy_check_mark: | :heavy_check_mark: | The parallel manager. |
|
||||||
|
| `:params` | `Hash` | :heavy_check_mark: | :heavy_check_mark: | The request params. |
|
||||||
|
| `:response` | `Faraday::Response` | :x: | :heavy_check_mark: | The response. |
|
||||||
|
| `:response_headers` | `Faraday::Utils::Headers` | :x: | :heavy_check_mark: | The response headers. |
|
||||||
|
| `:status` | `Integer` | :x: | :heavy_check_mark: | The response status code. |
|
||||||
|
| `:reason_phrase` | `String` | :x: | :heavy_check_mark: | The response reason phrase. |
|
||||||
|
| `:response_body` | `String` | :x: | :heavy_check_mark: | The response body. |
|
||||||
|
|
||||||
|
## Helpers
|
||||||
|
|
||||||
|
The `env` object also provides some helper methods to make it easier to work with the properties.
|
||||||
|
|
||||||
|
| Method | Description |
|
||||||
|
|-------------------------|--------------------------------------------------------------------------------------------------|
|
||||||
|
| `#body`/`#current_body` | Returns the request or response body, based on the presence of `#status`. |
|
||||||
|
| `#success?` | Returns `true` if the response status is in the 2xx range. |
|
||||||
|
| `#needs_body?` | Returns `true` if there's no body yet, and the method is in the set of `Env::MethodsWithBodies`. |
|
||||||
|
| `#clear_body` | Clears the body, if it's present. That includes resetting the `Content-Length` header. |
|
||||||
|
| `#parse_body?` | Returns `true` unless the status indicates otherwise (e.g. 204, 304). |
|
||||||
|
| `#parallel?` | Returns `true` if a parallel manager is available. |
|
||||||
|
| `#stream_response?` | Returns `true` if the `on_data` streaming callback has been provided. |
|
||||||
|
| `#stream_response` | Helper method to implement streaming in adapters. See [Support streaming in your adapter]. |
|
||||||
|
|
||||||
|
[Support streaming in your adapter]: /adapters/custom/streaming.md
|
17
docs/getting-started/errors.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Dealing with Errors
|
||||||
|
|
||||||
|
As an abstraction layer between the user and the underlying HTTP library,
|
||||||
|
it's important that Faraday provides a consistent interface for dealing with errors.
|
||||||
|
This is especially important when dealing with multiple adapters, as each adapter may raise different errors.
|
||||||
|
|
||||||
|
Below is a list of errors that Faraday may raise, and that you should be prepared to handle.
|
||||||
|
|
||||||
|
| Error | Description |
|
||||||
|
|-----------------------------|--------------------------------------------------------------------------------|
|
||||||
|
| `Faraday::Error` | Base class for all Faraday errors, also used for generic or unexpected errors. |
|
||||||
|
| `Faraday::ConnectionFailed` | Raised when the connection to the remote server failed. |
|
||||||
|
| `Faraday::TimeoutError` | Raised when the connection to the remote server timed out. |
|
||||||
|
| `Faraday::SSLError` | Raised when the connection to the remote server failed due to an SSL error. |
|
||||||
|
|
||||||
|
If you add the `raise_error` middleware, Faraday will also raise additional errors for 4xx and 5xx responses.
|
||||||
|
You can find the full list of errors in the [raise_error middleware](/middleware/included/raising-errors) page.
|
266
docs/getting-started/quick-start.md
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
# Quick Start
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Add this line to your application’s `Gemfile`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
gem 'faraday'
|
||||||
|
```
|
||||||
|
|
||||||
|
And then execute:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ bundle
|
||||||
|
```
|
||||||
|
|
||||||
|
Or install it yourself as:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ gem install faraday
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Quick requests
|
||||||
|
|
||||||
|
Let's fetch the home page for the wonderful [httpbingo.org](https://httpbingo.org) service.
|
||||||
|
|
||||||
|
You can make a simple `GET` request using `Faraday.get`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
response = Faraday.get('http://httpbingo.org')
|
||||||
|
```
|
||||||
|
|
||||||
|
This returns a `Faraday::Response` object with the response status, headers, and body.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
response.status
|
||||||
|
# => 200
|
||||||
|
|
||||||
|
response.headers
|
||||||
|
# => {"server"=>"Fly/c375678 (2021-04-23)", "content-type"=> ...
|
||||||
|
|
||||||
|
response.body
|
||||||
|
# => "<!DOCTYPE html><html> ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Faraday Connection
|
||||||
|
|
||||||
|
The recommended way to use Faraday, especially when integrating to 3rd party services and APIs, is to create
|
||||||
|
a `Faraday::Connection`. The connection initializer allows you to set:
|
||||||
|
|
||||||
|
- default request headers & query parameters
|
||||||
|
- network settings like proxy or timeout
|
||||||
|
- common URL base path
|
||||||
|
- Faraday adapter & middleware (see below)
|
||||||
|
|
||||||
|
Create a `Faraday::Connection` by calling `Faraday.new`. You can then call each HTTP verb
|
||||||
|
(`get`, `post`, ...) on your `Faraday::Connection` to perform a request:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new(
|
||||||
|
url: 'http://httpbingo.org',
|
||||||
|
params: {param: '1'},
|
||||||
|
headers: {'Content-Type' => 'application/json'}
|
||||||
|
)
|
||||||
|
|
||||||
|
response = conn.post('/post') do |req|
|
||||||
|
req.params['limit'] = 100
|
||||||
|
req.body = {query: 'chunky bacon'}.to_json
|
||||||
|
end
|
||||||
|
# => POST http://httpbingo.org/post?param=1&limit=100
|
||||||
|
```
|
||||||
|
|
||||||
|
### GET, HEAD, DELETE, TRACE
|
||||||
|
|
||||||
|
Faraday supports the following HTTP verbs that typically don't include a request body:
|
||||||
|
|
||||||
|
- `get(url, params = nil, headers = nil)`
|
||||||
|
- `head(url, params = nil, headers = nil)`
|
||||||
|
- `delete(url, params = nil, headers = nil)`
|
||||||
|
- `trace(url, params = nil, headers = nil)`
|
||||||
|
|
||||||
|
You can specify URI query parameters and HTTP headers when making a request.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
response = conn.get('get', { boom: 'zap' }, { 'User-Agent' => 'myapp' })
|
||||||
|
# => GET http://httpbingo.org/get?boom=zap
|
||||||
|
```
|
||||||
|
|
||||||
|
### POST, PUT, PATCH
|
||||||
|
|
||||||
|
Faraday also supports HTTP verbs with bodies. Instead of query parameters, these
|
||||||
|
accept a request body:
|
||||||
|
|
||||||
|
- `post(url, body = nil, headers = nil)`
|
||||||
|
- `put(url, body = nil, headers = nil)`
|
||||||
|
- `patch(url, body = nil, headers = nil)`
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# POST 'application/x-www-form-urlencoded' content
|
||||||
|
response = conn.post('post', 'boom=zap')
|
||||||
|
|
||||||
|
# POST JSON content
|
||||||
|
response = conn.post('post', '{"boom": "zap"}',
|
||||||
|
"Content-Type" => "application/json")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Posting Forms
|
||||||
|
|
||||||
|
Faraday will automatically convert key/value hashes into proper form bodies
|
||||||
|
thanks to the `url_encoded` middleware included in the default connection.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# POST 'application/x-www-form-urlencoded' content
|
||||||
|
response = conn.post('post', boom: 'zap')
|
||||||
|
# => POST 'boom=zap' to http://httpbingo.org/post
|
||||||
|
```
|
||||||
|
|
||||||
|
### Detailed HTTP Requests
|
||||||
|
|
||||||
|
Faraday supports a longer style for making requests. This is handy if you need
|
||||||
|
to change many of the defaults, or if the details of the HTTP request change
|
||||||
|
according to method arguments. Each of the HTTP verb helpers can yield a
|
||||||
|
`Faraday::Request` that can be modified before being sent.
|
||||||
|
|
||||||
|
This example shows a hypothetical search endpoint that accepts a JSON request
|
||||||
|
body as the actual search query.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
response = conn.post('post') do |req|
|
||||||
|
req.params['limit'] = 100
|
||||||
|
req.headers['Content-Type'] = 'application/json'
|
||||||
|
req.body = {query: 'chunky bacon'}.to_json
|
||||||
|
end
|
||||||
|
# => POST http://httpbingo.org/post?limit=100
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using Middleware
|
||||||
|
|
||||||
|
Configuring your connection or request with predefined headers and parameters is a good start,
|
||||||
|
but the real power of Faraday comes from its middleware stack.
|
||||||
|
Middleware are classes that allow you to hook into the request/response cycle and modify the request.
|
||||||
|
They can help you with things like:
|
||||||
|
* adding authentication headers
|
||||||
|
* parsing JSON responses
|
||||||
|
* logging requests and responses
|
||||||
|
* raise errors on 4xx and 5xx responses
|
||||||
|
* and much more!
|
||||||
|
|
||||||
|
For example, let's say you want to call an API that:
|
||||||
|
* requires an authentication token in the `Authorization` header
|
||||||
|
* expects JSON request bodies
|
||||||
|
* returns JSON responses
|
||||||
|
|
||||||
|
and on top of that, you want to automatically raise errors on 4xx and 5xx responses,
|
||||||
|
as well as log all requests and responses.
|
||||||
|
|
||||||
|
You can easily achieve all of the above by adding the necessary middleware to your connection:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
|
||||||
|
# Calls MyAuthStorage.get_auth_token on each request to get the auth token
|
||||||
|
# and sets it in the Authorization header with Bearer scheme.
|
||||||
|
builder.request :authorization, 'Bearer', -> { MyAuthStorage.get_auth_token }
|
||||||
|
|
||||||
|
# Sets the Content-Type header to application/json on each request.
|
||||||
|
# Also, if the request body is a Hash, it will automatically be encoded as JSON.
|
||||||
|
builder.request :json
|
||||||
|
|
||||||
|
# Parses JSON response bodies.
|
||||||
|
# If the response body is not valid JSON, it will raise a Faraday::ParsingError.
|
||||||
|
builder.response :json
|
||||||
|
|
||||||
|
# Raises an error on 4xx and 5xx responses.
|
||||||
|
builder.response :raise_error
|
||||||
|
|
||||||
|
# Logs requests and responses.
|
||||||
|
# By default, it only logs the request method and URL, and the request/response headers.
|
||||||
|
builder.response :logger
|
||||||
|
end
|
||||||
|
|
||||||
|
# A simple example implementation for MyAuthStorage
|
||||||
|
class MyAuthStorage
|
||||||
|
def self.get_auth_token
|
||||||
|
rand(36 ** 8).to_s(36)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
The connection can now be used to make requests.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
begin
|
||||||
|
response = conn.post('post', { payload: 'this ruby hash will become JSON' })
|
||||||
|
rescue Faraday::Error => e
|
||||||
|
# You can handle errors here (4xx/5xx responses, timeouts, etc.)
|
||||||
|
puts e.response[:status]
|
||||||
|
puts e.response[:body]
|
||||||
|
end
|
||||||
|
|
||||||
|
# At this point, you can assume the request was successful
|
||||||
|
puts response.body
|
||||||
|
|
||||||
|
# I, [2023-06-30T14:27:11.776511 #35368] INFO -- request: POST http://httpbingo.org/post
|
||||||
|
# I, [2023-06-30T14:27:11.776646 #35368] INFO -- request: User-Agent: "Faraday v2.7.8"
|
||||||
|
# Authorization: "Bearer wibzjgyh"
|
||||||
|
# Content-Type: "application/json"
|
||||||
|
# I, [2023-06-30T14:27:12.063897 #35368] INFO -- response: Status 200
|
||||||
|
# I, [2023-06-30T14:27:12.064260 #35368] INFO -- response: access-control-allow-credentials: "true"
|
||||||
|
# access-control-allow-origin: "*"
|
||||||
|
# content-type: "application/json; encoding=utf-8"
|
||||||
|
# date: "Fri, 30 Jun 2023 13:27:12 GMT"
|
||||||
|
# content-encoding: "gzip"
|
||||||
|
# transfer-encoding: "chunked"
|
||||||
|
# server: "Fly/a0b91024 (2023-06-13)"
|
||||||
|
# via: "1.1 fly.io"
|
||||||
|
# fly-request-id: "01H467RYRHA0YK4TQSZ7HS8ZFT-lhr"
|
||||||
|
# cf-team: "19ae1592b8000003bbaedcf400000001"
|
||||||
|
```
|
||||||
|
|
||||||
|
Faraday ships with a number of useful middleware, and you can also write your own.
|
||||||
|
To learn more about middleware, please check the [Middleware] section.
|
||||||
|
|
||||||
|
### Swapping Adapters
|
||||||
|
|
||||||
|
Faraday does not make HTTP requests itself, but instead relies on a Faraday adapter to do so.
|
||||||
|
By default, it will use the `Net::HTTP` adapter, which is part of the Ruby standard library.
|
||||||
|
Although `Net::HTTP` is the only adapter that ships with Faraday, there are [many other adapters
|
||||||
|
available as separate gems](https://github.com/lostisland/awesome-faraday#adapters).
|
||||||
|
|
||||||
|
Once you have installed an adapter, you can use it by passing the `adapter` option to `Faraday.new`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
|
||||||
|
builder.adapter :async_http
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
To learn more about adapters, including how to write your own, please check the [Adapters] section.
|
||||||
|
|
||||||
|
### Default Connection, Default Adapter
|
||||||
|
|
||||||
|
Remember how we said that Faraday will automatically encode key/value hash
|
||||||
|
bodies into form bodies? Internally, the top level shortcut methods
|
||||||
|
`Faraday.get`, `post`, etc. use a simple default `Faraday::Connection`. The only
|
||||||
|
middleware used for the default connection is `:url_encoded`, which encodes
|
||||||
|
those form hashes, and the `default_adapter`.
|
||||||
|
|
||||||
|
You can change the default adapter or connection. Be careful because they're set globally.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.default_adapter = :async_http # defaults to :net_http
|
||||||
|
|
||||||
|
# The default connection has only `:url_encoded` middleware.
|
||||||
|
# Note that if you create your own connection with middleware, it won't encode
|
||||||
|
# form bodies unless you too include the :url_encoded middleware!
|
||||||
|
Faraday.default_connection = Faraday.new do |conn|
|
||||||
|
conn.request :url_encoded
|
||||||
|
conn.response :logger
|
||||||
|
conn.adapter Faraday.default_adapter
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
[Adapters]: /adapters/index.md
|
||||||
|
[Middleware]: /middleware/index.md
|
225
docs/getting-started/rest-client-migration.md
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
# 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
|
||||||
|
```
|
121
docs/index.html
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="icon" type="image/x-icon" href="_media/favicon.png">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||||
|
|
||||||
|
<!-- HTML Meta Tags -->
|
||||||
|
<title>Faraday Docs</title>
|
||||||
|
<meta name="description" content="Faraday is an HTTP client library abstraction layer that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.">
|
||||||
|
|
||||||
|
<!-- OpenGraph Meta Tags -->
|
||||||
|
<meta property="og:url" content="https://lostisland.github.io/faraday/#/">
|
||||||
|
<meta property="og:type" content="website">
|
||||||
|
<meta property="og:title" content="Faraday Docs">
|
||||||
|
<meta property="og:description" content="Faraday is an HTTP client library abstraction layer that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.">
|
||||||
|
<meta property="og:image" content="https://lostisland.github.io/faraday/_media/repo-card.png">
|
||||||
|
|
||||||
|
<!-- Twitter Meta Tags -->
|
||||||
|
<meta name="twitter:card" content="summary_large_image">
|
||||||
|
<meta property="twitter:domain" content="lostisland.github.io">
|
||||||
|
<meta property="twitter:url" content="https://lostisland.github.io/faraday/#/">
|
||||||
|
<meta name="twitter:title" content="Faraday Docs">
|
||||||
|
<meta name="twitter:description" content="Faraday is an HTTP client library abstraction layer that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.">
|
||||||
|
<meta name="twitter:image" content="https://lostisland.github.io/faraday/_media/repo-card.png">
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
|
||||||
|
title="docsify-darklight-theme"
|
||||||
|
type="text/css"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="stylesheet"
|
||||||
|
href="//cdn.jsdelivr.net/npm/prism-themes/themes/prism-material-light.min.css"
|
||||||
|
id="prism-theme"
|
||||||
|
type="text/css"
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- Docsify plugins -->
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/docsify-edit-on-github"></script>
|
||||||
|
|
||||||
|
<!-- Docsify config -->
|
||||||
|
<script>
|
||||||
|
window.$docsify = {
|
||||||
|
name: 'Faraday',
|
||||||
|
repo: 'lostisland/faraday',
|
||||||
|
logo: '_media/home-logo.svg',
|
||||||
|
homepage: 'index.md',
|
||||||
|
search: true,
|
||||||
|
loadSidebar: true,
|
||||||
|
subMaxLevel: 4,
|
||||||
|
auto2top: true,
|
||||||
|
darklightTheme: {
|
||||||
|
dark: {
|
||||||
|
accent: '#EE4266',
|
||||||
|
prismTheme: 'prism-material-dark'
|
||||||
|
},
|
||||||
|
light: {
|
||||||
|
accent: '#EE4266',
|
||||||
|
prismTheme: 'prism-material-light'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
EditOnGithubPlugin.create(
|
||||||
|
'https://github.com/lostisland/faraday/blob/main/docs/',
|
||||||
|
null,
|
||||||
|
'Edit this page on GitHub'
|
||||||
|
),
|
||||||
|
function pageFooter(hook, _vm) {
|
||||||
|
var footer = [
|
||||||
|
'<hr/>',
|
||||||
|
'<footer>',
|
||||||
|
'<span>© 2009 - 2023, the Faraday Team. </span>',
|
||||||
|
'<span>Website and branding design by <a href="https://elelopic.design" target="_blank" rel="noopener">Elena Lo Piccolo</a>.</span>',
|
||||||
|
'</footer>',
|
||||||
|
].join('');
|
||||||
|
|
||||||
|
hook.afterEach(function (html) {
|
||||||
|
return html + footer;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
function prismThemeSwitcher(hook, _vm) {
|
||||||
|
// Switch Prism theme based on docsify-darklight-theme setting
|
||||||
|
let lightTheme = '//cdn.jsdelivr.net/npm/prism-themes/themes/prism-one-light.min.css';
|
||||||
|
let darkTheme = '//cdn.jsdelivr.net/npm/prism-themes/themes/prism-one-dark.min.css';
|
||||||
|
|
||||||
|
let switchTheme = () => {
|
||||||
|
console.log('Theme changed');
|
||||||
|
let theme = localStorage.getItem('DARK_LIGHT_THEME')
|
||||||
|
let link = document.getElementById('prism-theme');
|
||||||
|
link.setAttribute('href', theme === 'dark' ? darkTheme : lightTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
hook.ready(() => {
|
||||||
|
document.getElementById('main').addEventListener('click', switchTheme);
|
||||||
|
switchTheme();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<!-- Docsify v4 -->
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
||||||
|
<!-- Docsify Darklight Theme -->
|
||||||
|
<script
|
||||||
|
src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"
|
||||||
|
type="text/javascript">
|
||||||
|
</script>
|
||||||
|
<!-- Prism Ruby highlight -->
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/prismjs@v1.x/components/prism-ruby.min.js"></script>
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/prismjs@v1.x/plugins/autoloader/prism-autoloader.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Other Plugins -->
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||||
|
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,51 +1,28 @@
|
|||||||
---
|
# 
|
||||||
# You don't need to edit this file, it's empty on purpose.
|
|
||||||
# Edit theme's home layout instead if you wanna make some changes
|
|
||||||
# See: https://jekyllrb.com/docs/themes/#overriding-theme-defaults
|
|
||||||
layout: page
|
|
||||||
title: Homepage
|
|
||||||
feature-title: <img src="assets/img/home-logo.svg">
|
|
||||||
feature-img: "assets/img/featured-bg.svg"
|
|
||||||
hide: true
|
|
||||||
---
|
|
||||||
|
|
||||||
Faraday is an HTTP client library that provides a common interface over many adapters (such as Net::HTTP)
|
Faraday is an HTTP client library abstraction layer that provides a common interface over many
|
||||||
and embraces the concept of Rack middleware when processing the request/response cycle.
|
adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.
|
||||||
|
|
||||||
{: .text-center}
|
## Why use Faraday?
|
||||||
[<i class="fab fa-fw fa-github"> </i> Fork on GitHub][github]{: .btn}
|
|
||||||
[<i class="fab fa-fw fa-gitter"> </i> Chat with us][gitter]{: .btn}
|
|
||||||
|
|
||||||
{: .mt-60}
|
Faraday gives you the power of Rack middleware for manipulating HTTP requests and responses,
|
||||||
## Installation
|
making it easier to build sophisticated API clients or web service libraries that abstract away
|
||||||
|
the details of how HTTP requests are made.
|
||||||
|
|
||||||
Add this line to your application's Gemfile:
|
Faraday comes with a lot of features out of the box, such as:
|
||||||
|
* Support for multiple adapters (Net::HTTP, Typhoeus, Patron, Excon, HTTPClient, and more)
|
||||||
|
* Persistent connections (keep-alive)
|
||||||
|
* Parallel requests
|
||||||
|
* Automatic response parsing (JSON, XML, YAML)
|
||||||
|
* Customization of the request/response cycle with middleware
|
||||||
|
* Support for streaming responses
|
||||||
|
* Support for uploading files
|
||||||
|
* And much more!
|
||||||
|
|
||||||
```ruby
|
## Who uses Faraday?
|
||||||
gem 'faraday'
|
|
||||||
```
|
|
||||||
|
|
||||||
And then execute:
|
Faraday is used by many popular Ruby libraries, such as:
|
||||||
|
* [Signet](https://github.com/googleapis/signet)
|
||||||
```bash
|
* [Octokit](https://github.com/octokit/octokit.rb)
|
||||||
$ bundle
|
* [Oauth2](https://bestgems.org/gems/oauth2)
|
||||||
```
|
* [Elasticsearch](https://github.com/elastic/elasticsearch-ruby)
|
||||||
|
|
||||||
Or install it yourself as:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gem install faraday
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also install the [`faraday_middleware`][faraday_middleware]
|
|
||||||
extension gem to access a collection of useful Faraday middleware.
|
|
||||||
|
|
||||||
{: .mt-60}
|
|
||||||
|
|
||||||
{: .text-center}
|
|
||||||
[<i class="fas fa-fw fa-book-open"> </i> Read the docs][usage]{: .btn}
|
|
||||||
|
|
||||||
[github]: https://github.com/lostisland/faraday
|
|
||||||
[gitter]: https://gitter.im/lostisland/faraday
|
|
||||||
[faraday_middleware]: https://github.com/lostisland/faraday_middleware
|
|
||||||
[usage]: ./usage
|
|
||||||
|
84
docs/middleware/custom-middleware.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# Writing custom middleware
|
||||||
|
|
||||||
|
!> A template for writing your own middleware is available in the [faraday-middleware-template](https://github.com/lostisland/faraday-middleware-template) repository.
|
||||||
|
|
||||||
|
The recommended way to write middleware is to make your middleware subclass `Faraday::Middleware`.
|
||||||
|
`Faraday::Middleware` simply expects your subclass to implement two methods: `#on_request(env)` and `#on_complete(env)`.
|
||||||
|
* `#on_request` is called when the request is being built and is given the `env` representing the request.
|
||||||
|
* `#on_complete` is called after the response has been received (that's right, it already supports parallel mode!) and receives the `env` of the response.
|
||||||
|
|
||||||
|
For both `env` parameters, please refer to the [Env Object](getting-started/env-object.md) page.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class MyMiddleware < Faraday::Middleware
|
||||||
|
def on_request(env)
|
||||||
|
# do something with the request
|
||||||
|
# env[:request_headers].merge!(...)
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_complete(env)
|
||||||
|
# do something with the response
|
||||||
|
# env[:response_headers].merge!(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Having more control
|
||||||
|
|
||||||
|
For the majority of middleware, it's not necessary to override the `#call` method. You can instead use `#on_request` and `#on_complete`.
|
||||||
|
|
||||||
|
However, in some cases you may need to wrap the call in a block, or work around it somehow (think of a begin-rescue, for example).
|
||||||
|
When that happens, then you can override `#call`. When you do so, remember to call either `app.call(env)` or `super` to avoid breaking the middleware stack call!
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
def call(request_env)
|
||||||
|
# do something with the request
|
||||||
|
# request_env[:request_headers].merge!(...)
|
||||||
|
|
||||||
|
@app.call(request_env).on_complete do |response_env|
|
||||||
|
# do something with the response
|
||||||
|
# response_env[:response_headers].merge!(...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
It's important to do all processing of the response only in the `#on_complete`
|
||||||
|
block. This enables middleware to work in parallel mode where requests are
|
||||||
|
asynchronous.
|
||||||
|
|
||||||
|
The `request_env` and `response_env` are both [Env Objects](getting-started/env-object.md) but note the amount of
|
||||||
|
information available in each one will differ based on the request/response lifecycle.
|
||||||
|
|
||||||
|
## Accepting configuration options
|
||||||
|
|
||||||
|
`Faraday::Middleware` also allows your middleware to accept configuration options.
|
||||||
|
These are passed in when the middleware is added to the stack, and can be accessed via the `options` getter.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class MyMiddleware < Faraday::Middleware
|
||||||
|
def on_request(_env)
|
||||||
|
# access the foo option
|
||||||
|
puts options[:foo]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||||
|
faraday.use MyMiddleware, foo: 'bar'
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## Registering your middleware
|
||||||
|
|
||||||
|
Users can use your middleware using the class directly, but you can also register it with Faraday so that
|
||||||
|
it can be used with the `use`, `request` or `response` methods as well.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Register for `use`
|
||||||
|
Faraday::Middleware.register_middleware(my_middleware: MyMiddleware)
|
||||||
|
|
||||||
|
# Register for `request`
|
||||||
|
Faraday::Request.register_middleware(my_middleware: MyMiddleware)
|
||||||
|
|
||||||
|
# Register for `response`
|
||||||
|
Faraday::Response.register_middleware(my_middleware: MyMiddleware)
|
||||||
|
```
|
@ -1,59 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Writing Middleware"
|
|
||||||
permalink: /middleware/custom
|
|
||||||
hide: true
|
|
||||||
top_name: Middleware
|
|
||||||
top_link: ./
|
|
||||||
prev_name: Available Middleware
|
|
||||||
prev_link: ./list
|
|
||||||
---
|
|
||||||
|
|
||||||
Middleware are classes that implement a `#call` instance method. They hook into the request/response cycle.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
def call(request_env)
|
|
||||||
# do something with the request
|
|
||||||
# request_env[:request_headers].merge!(...)
|
|
||||||
|
|
||||||
@app.call(request_env).on_complete do |response_env|
|
|
||||||
# do something with the response
|
|
||||||
# response_env[:response_headers].merge!(...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
It's important to do all processing of the response only in the `#on_complete`
|
|
||||||
block. This enables middleware to work in parallel mode where requests are
|
|
||||||
asynchronous.
|
|
||||||
|
|
||||||
The `env` is a hash with symbol keys that contains info about the request and,
|
|
||||||
later, response. Some keys are:
|
|
||||||
|
|
||||||
```
|
|
||||||
# request phase
|
|
||||||
:method - :get, :post, ...
|
|
||||||
:url - URI for the current request; also contains GET parameters
|
|
||||||
:body - POST parameters for :post/:put requests
|
|
||||||
:request_headers
|
|
||||||
|
|
||||||
# response phase
|
|
||||||
:status - HTTP response status code, such as 200
|
|
||||||
:body - the response body
|
|
||||||
:response_headers
|
|
||||||
```
|
|
||||||
|
|
||||||
### Faraday::Middleware
|
|
||||||
|
|
||||||
There's an easier way to write middleware, and it's also the recommended one: make your middleware subclass `Faraday::Middleware`.
|
|
||||||
`Faraday::Middleware` already implements the `#call` method for you and looks for two methods in your subclass: `#on_request(env)` and `#on_complete(env)`.
|
|
||||||
`#on_request` is called when the request is being built and is given the `env` representing the request.
|
|
||||||
|
|
||||||
`#on_complete` is called after the response has been received (that's right, it already supports parallel mode!) and receives the `env` of the response.
|
|
||||||
|
|
||||||
### Do I need to override `#call`?
|
|
||||||
|
|
||||||
For the majority of middleware, it's not necessary to override the `#call` method. You can instead use `#on_request` and `#on_complete`.
|
|
||||||
|
|
||||||
However, in some cases you may need to wrap the call in a block, or work around it somehow (think of a begin-rescue, for example).
|
|
||||||
When that happens, then you can override `#call`. When you do so, remember to call either `app.call(env)` or `super` to avoid breaking the middleware stack call!
|
|
65
docs/middleware/included/authentication.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Authentication
|
||||||
|
|
||||||
|
The `Faraday::Request::Authorization` middleware allows you to automatically add an `Authorization` header
|
||||||
|
to your requests. It also features a handy helper to manage Basic authentication.
|
||||||
|
**Please note the way you use this middleware in Faraday 1.x is different**,
|
||||||
|
examples are available at the bottom of this page.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.new(...) do |conn|
|
||||||
|
conn.request :authorization, 'Bearer', 'authentication-token'
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### With a proc
|
||||||
|
|
||||||
|
You can also provide a proc, which will be evaluated on each request:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.new(...) do |conn|
|
||||||
|
conn.request :authorization, 'Bearer', -> { MyAuthStorage.get_auth_token }
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
If the proc takes an argument, it will receive the forwarded `env` (see [The Env Object](getting-started/env-object.md)):
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.new(...) do |conn|
|
||||||
|
conn.request :authorization, 'Bearer', ->(env) { MyAuthStorage.get_auth_token(env) }
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Basic Authentication
|
||||||
|
|
||||||
|
The middleware will automatically Base64 encode your Basic username and password:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.new(...) do |conn|
|
||||||
|
conn.request :authorization, :basic, 'username', 'password'
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
### Faraday 1.x usage
|
||||||
|
|
||||||
|
In Faraday 1.x, the way you use this middleware is slightly different:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# Basic Auth request
|
||||||
|
# Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
|
||||||
|
Faraday.new(...) do |conn|
|
||||||
|
conn.request :basic_auth, 'username', 'password'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Token Auth request
|
||||||
|
# `options` are automatically converted into `key=value` format
|
||||||
|
# Authorization: Token authentication-token <options>
|
||||||
|
Faraday.new(...) do |conn|
|
||||||
|
conn.request :token_auth, 'authentication-token', **options
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generic Auth Request
|
||||||
|
# Authorization: Bearer authentication-token
|
||||||
|
Faraday.new(...) do |conn|
|
||||||
|
conn.request :authorization, 'Bearer', 'authentication-token'
|
||||||
|
end
|
||||||
|
```
|
@ -1,13 +1,4 @@
|
|||||||
---
|
# Included middleware
|
||||||
layout: documentation
|
|
||||||
title: "Available Middleware"
|
|
||||||
permalink: /middleware/list
|
|
||||||
hide: true
|
|
||||||
top_name: Middleware
|
|
||||||
top_link: ./
|
|
||||||
next_name: Writing Middleware
|
|
||||||
next_link: ./custom
|
|
||||||
---
|
|
||||||
|
|
||||||
Faraday ships with some useful middleware that you can use to customize your request/response lifecycle.
|
Faraday ships with some useful middleware that you can use to customize your request/response lifecycle.
|
||||||
Middleware are separated into two macro-categories: **Request Middleware** and **Response Middleware**.
|
Middleware are separated into two macro-categories: **Request Middleware** and **Response Middleware**.
|
||||||
@ -21,14 +12,9 @@ parsing the response body, logging useful info or checking the response status.
|
|||||||
middleware set Header values or transform the request body based on the
|
middleware set Header values or transform the request body based on the
|
||||||
content type.
|
content type.
|
||||||
|
|
||||||
* [`BasicAuthentication`][authentication] sets the `Authorization` header to the `user:password`
|
* [`Authorization`][authentication] allows you to automatically add an Authorization header to your requests.
|
||||||
base64 representation.
|
|
||||||
* [`TokenAuthentication`][authentication] sets the `Authorization` header to the specified token.
|
|
||||||
* [`Multipart`][multipart] converts a `Faraday::Request#body` hash of key/value pairs into a
|
|
||||||
multipart form request.
|
|
||||||
* [`UrlEncoded`][url_encoded] converts a `Faraday::Request#body` hash of key/value pairs into a url-encoded request body.
|
* [`UrlEncoded`][url_encoded] converts a `Faraday::Request#body` hash of key/value pairs into a url-encoded request body.
|
||||||
* [`Retry`][retry] automatically retries requests that fail due to intermittent client
|
* [`Json Request`][json-request] converts a `Faraday::Request#body` hash of key/value pairs into a JSON request body.
|
||||||
or server errors (such as network hiccups).
|
|
||||||
* [`Instrumentation`][instrumentation] allows to instrument requests using different tools.
|
* [`Instrumentation`][instrumentation] allows to instrument requests using different tools.
|
||||||
|
|
||||||
|
|
||||||
@ -37,14 +23,15 @@ or server errors (such as network hiccups).
|
|||||||
**Response middleware** receives the response from the adapter and can modify its details
|
**Response middleware** receives the response from the adapter and can modify its details
|
||||||
before returning it.
|
before returning it.
|
||||||
|
|
||||||
|
* [`Json Response`][json-response] parses response body into a hash of key/value pairs.
|
||||||
* [`Logger`][logger] logs both the request and the response body and headers.
|
* [`Logger`][logger] logs both the request and the response body and headers.
|
||||||
* [`RaiseError`][raise_error] checks the response HTTP code and raises an exception if it is a 4xx or 5xx code.
|
* [`RaiseError`][raise_error] checks the response HTTP code and raises an exception if it is a 4xx or 5xx code.
|
||||||
|
|
||||||
|
|
||||||
[authentication]: ./authentication
|
[authentication]: middleware/included/authentication.md
|
||||||
[multipart]: ./multipart
|
[url_encoded]: middleware/included/url-encoding
|
||||||
[url_encoded]: ./url-encoded
|
[json-request]: middleware/included/json#json-requests
|
||||||
[retry]: ./retry
|
[instrumentation]: middleware/included/instrumentation
|
||||||
[instrumentation]: ./instrumentation
|
[json-response]: middleware/included/json#json-responses
|
||||||
[logger]: ./logger
|
[logger]: middleware/included/logging
|
||||||
[raise_error]: ./raise-error
|
[raise_error]: middleware/included/raising-errors
|
@ -1,15 +1,4 @@
|
|||||||
---
|
# Instrumentation
|
||||||
layout: documentation
|
|
||||||
title: "Instrumentation Middleware"
|
|
||||||
permalink: /middleware/instrumentation
|
|
||||||
hide: true
|
|
||||||
prev_name: Retry Middleware
|
|
||||||
prev_link: ./retry
|
|
||||||
next_name: Logger Middleware
|
|
||||||
next_link: ./logger
|
|
||||||
top_name: Back to Middleware
|
|
||||||
top_link: ./list
|
|
||||||
---
|
|
||||||
|
|
||||||
The `Instrumentation` middleware allows to instrument requests using different tools.
|
The `Instrumentation` middleware allows to instrument requests using different tools.
|
||||||
Options for this middleware include the instrumentation `name` and the `instrumenter` you want to use.
|
Options for this middleware include the instrumentation `name` and the `instrumenter` you want to use.
|
81
docs/middleware/included/json.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# JSON Encoding/Decoding
|
||||||
|
|
||||||
|
## JSON Requests
|
||||||
|
|
||||||
|
The `JSON` request middleware converts a `Faraday::Request#body` hash of key/value pairs into a JSON request body.
|
||||||
|
The middleware also automatically sets the `Content-Type` header to `application/json`,
|
||||||
|
processes only requests with matching Content-Type or those without a type and
|
||||||
|
doesn't try to encode bodies that already are in string form.
|
||||||
|
|
||||||
|
### Example Usage
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new(...) do |f|
|
||||||
|
f.request :json
|
||||||
|
...
|
||||||
|
end
|
||||||
|
|
||||||
|
conn.post('/', { a: 1, b: 2 })
|
||||||
|
# POST with
|
||||||
|
# Content-Type: application/json
|
||||||
|
# Body: {"a":1,"b":2}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using custom JSON encoders
|
||||||
|
|
||||||
|
By default, middleware utilizes Ruby's `json` to generate JSON strings.
|
||||||
|
|
||||||
|
Other encoders can be used by specifying `encoder` option for the middleware:
|
||||||
|
* a module/class which implements `dump`
|
||||||
|
* a module/class-method pair to be used
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'oj'
|
||||||
|
|
||||||
|
Faraday.new(...) do |f|
|
||||||
|
f.request :json, encoder: Oj
|
||||||
|
end
|
||||||
|
|
||||||
|
Faraday.new(...) do |f|
|
||||||
|
f.request :json, encoder: [Oj, :dump]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
## JSON Responses
|
||||||
|
|
||||||
|
The `JSON` response middleware parses response body into a hash of key/value pairs.
|
||||||
|
The behaviour can be customized with the following options:
|
||||||
|
* **parser_options:** options that will be sent to the JSON.parse method. Defaults to {}.
|
||||||
|
* **content_type:** Single value or Array of response content-types that should be processed. Can be either strings or Regex. Defaults to `/\bjson$/`.
|
||||||
|
* **preserve_raw:** If set to true, the original un-parsed response will be stored in the `response.env[:raw_body]` property. Defaults to `false`.
|
||||||
|
|
||||||
|
### Example Usage
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new('http://httpbingo.org') do |f|
|
||||||
|
f.response :json, **options
|
||||||
|
end
|
||||||
|
|
||||||
|
conn.get('json').body
|
||||||
|
# => {"slideshow"=>{"author"=>"Yours Truly", "date"=>"date of publication", "slides"=>[{"title"=>"Wake up to WonderWidgets!", "type"=>"all"}, {"items"=>["Why <em>WonderWidgets</em> are great", "Who <em>buys</em> WonderWidgets"], "title"=>"Overview", "type"=>"all"}], "title"=>"Sample Slide Show"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using custom JSON decoders
|
||||||
|
|
||||||
|
By default, middleware utilizes Ruby's `json` to parse JSON strings.
|
||||||
|
|
||||||
|
Other decoders can be used by specifying `decoder` parser option for the middleware:
|
||||||
|
* a module/class which implements `load`
|
||||||
|
* a module/class-method pair to be used
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'oj'
|
||||||
|
|
||||||
|
Faraday.new(...) do |f|
|
||||||
|
f.response :json, parser_options: { decoder: Oj }
|
||||||
|
end
|
||||||
|
|
||||||
|
Faraday.new(...) do |f|
|
||||||
|
f.response :json, parser_options: { decoder: [Oj, :load] }
|
||||||
|
end
|
||||||
|
```
|
@ -1,15 +1,4 @@
|
|||||||
---
|
# Logging
|
||||||
layout: documentation
|
|
||||||
title: "Logger Middleware"
|
|
||||||
permalink: /middleware/logger
|
|
||||||
hide: true
|
|
||||||
prev_name: Instrumentation Middleware
|
|
||||||
prev_link: ./instrumentation
|
|
||||||
next_name: RaiseError Middleware
|
|
||||||
next_link: ./raise-error
|
|
||||||
top_name: Back to Middleware
|
|
||||||
top_link: ./list
|
|
||||||
---
|
|
||||||
|
|
||||||
The `Logger` middleware logs both the request and the response body and headers.
|
The `Logger` middleware logs both the request and the response body and headers.
|
||||||
It is highly customizable and allows to mask confidential information if necessary.
|
It is highly customizable and allows to mask confidential information if necessary.
|
||||||
@ -17,12 +6,12 @@ It is highly customizable and allows to mask confidential information if necessa
|
|||||||
### Basic Usage
|
### Basic Usage
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
conn = Faraday.new(url: 'http://sushi.com') do |faraday|
|
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||||
faraday.response :logger # log requests and responses to $stdout
|
faraday.response :logger # log requests and responses to $stdout
|
||||||
end
|
end
|
||||||
|
|
||||||
conn.get
|
conn.get
|
||||||
# => INFO -- request: GET http://sushi.com/
|
# => INFO -- request: GET http://httpbingo.org/
|
||||||
# => DEBUG -- request: User-Agent: "Faraday v1.0.0"
|
# => DEBUG -- request: User-Agent: "Faraday v1.0.0"
|
||||||
# => INFO -- response: Status 301
|
# => INFO -- response: Status 301
|
||||||
# => DEBUG -- response: date: "Sun, 19 May 2019 16:05:40 GMT"
|
# => DEBUG -- response: date: "Sun, 19 May 2019 16:05:40 GMT"
|
||||||
@ -34,7 +23,7 @@ By default, the `Logger` middleware uses the Ruby `Logger.new($stdout)`.
|
|||||||
You can customize it to use any logger you want by providing it when you add the middleware to the stack:
|
You can customize it to use any logger you want by providing it when you add the middleware to the stack:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
conn = Faraday.new(url: 'http://sushi.com') do |faraday|
|
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||||
faraday.response :logger, MyLogger.new($stdout)
|
faraday.response :logger, MyLogger.new($stdout)
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
@ -42,12 +31,21 @@ end
|
|||||||
### Include and exclude headers/bodies
|
### Include and exclude headers/bodies
|
||||||
|
|
||||||
By default, the `logger` middleware logs only headers for security reasons, however, you can configure it
|
By default, the `logger` middleware logs only headers for security reasons, however, you can configure it
|
||||||
to log bodies as well, or disable headers logging if you need to. To do so, simply provide a configuration hash
|
to log bodies and errors as well, or disable headers logging if you need to.
|
||||||
when you add the middleware to the stack:
|
To do so, simply provide a configuration hash when you add the middleware to the stack:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
conn = Faraday.new(url: 'http://sushi.com') do |faraday|
|
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||||
faraday.response :logger, nil, { headers: true, bodies: true }
|
faraday.response :logger, nil, { headers: true, bodies: true, errors: true }
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also configure the `logger` middleware with a little more complex settings
|
||||||
|
like "do not log the request bodies, but log the response bodies".
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||||
|
faraday.response :logger, nil, { bodies: { request: false, response: true } }
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -58,14 +56,14 @@ Please note this only works with the default formatter.
|
|||||||
You can filter sensitive information from Faraday logs using a regex matcher:
|
You can filter sensitive information from Faraday logs using a regex matcher:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
conn = Faraday.new(url: 'http://sushi.com') do |faraday|
|
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||||
faraday.response :logger do | logger |
|
faraday.response :logger do | logger |
|
||||||
logger.filter(/(api_key=)(\w+)/, '\1[REMOVED]')
|
logger.filter(/(api_key=)([^&]+)/, '\1[REMOVED]')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
conn.get('/', api_key: 'secret')
|
conn.get('/', api_key: 'secret')
|
||||||
# => INFO -- request: GET http://sushi.com/?api_key=[REMOVED]
|
# => INFO -- request: GET http://httpbingo.org/?api_key=[REMOVED]
|
||||||
# => DEBUG -- request: User-Agent: "Faraday v1.0.0"
|
# => DEBUG -- request: User-Agent: "Faraday v1.0.0"
|
||||||
# => INFO -- response: Status 301
|
# => INFO -- response: Status 301
|
||||||
# => DEBUG -- response: date: "Sun, 19 May 2019 16:12:36 GMT"
|
# => DEBUG -- response: date: "Sun, 19 May 2019 16:12:36 GMT"
|
||||||
@ -77,16 +75,18 @@ By default, the `logger` middleware logs on the `info` log level. It is possible
|
|||||||
the severity by providing the `log_level` option:
|
the severity by providing the `log_level` option:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
conn = Faraday.new(url: 'http://sushi.com') do |faraday|
|
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||||
faraday.response :logger, nil, { bodies: true, log_level: :debug }
|
faraday.response :logger, nil, { bodies: true, log_level: :debug }
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
### Customize the formatter
|
### Customize the formatter
|
||||||
|
|
||||||
You can also provide a custom formatter to control how requests and responses are logged.
|
You can also provide a custom formatter to control how requests, responses and errors are logged.
|
||||||
Any custom formatter MUST implement the `request` and `response` method, with one argument which
|
Any custom formatter MUST implement the `request` and `response` method, with one argument which
|
||||||
will be passed being the Faraday environment.
|
will be passed being the Faraday environment.
|
||||||
|
Any custom formatter CAN implement the `exception` method,
|
||||||
|
with one argument which will be passed being the exception (StandardError).
|
||||||
If you make your formatter inheriting from `Faraday::Logging::Formatter`,
|
If you make your formatter inheriting from `Faraday::Logging::Formatter`,
|
||||||
then the methods `debug`, `info`, `warn`, `error` and `fatal` are automatically delegated to the logger.
|
then the methods `debug`, `info`, `warn`, `error` and `fatal` are automatically delegated to the logger.
|
||||||
|
|
||||||
@ -101,9 +101,14 @@ class MyFormatter < Faraday::Logging::Formatter
|
|||||||
# Build a custom message using `env`
|
# Build a custom message using `env`
|
||||||
info('Response') { 'Response Received' }
|
info('Response') { 'Response Received' }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def exception(exc)
|
||||||
|
# Build a custom message using `exc`
|
||||||
|
info('Error') { 'Error Raised' }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
conn = Faraday.new(url: 'http://sushi.com/api_key=s3cr3t') do |faraday|
|
conn = Faraday.new(url: 'http://httpbingo.org/api_key=s3cr3t') do |faraday|
|
||||||
faraday.response :logger, nil, formatter: MyFormatter
|
faraday.response :logger, nil, formatter: MyFormatter
|
||||||
end
|
end
|
||||||
```
|
```
|
90
docs/middleware/included/raising-errors.md
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
# Raising Errors
|
||||||
|
|
||||||
|
The `RaiseError` middleware raises a `Faraday::Error` exception if an HTTP
|
||||||
|
response returns with a 4xx or 5xx status code.
|
||||||
|
This greatly increases the ease of use of Faraday, as you don't have to check
|
||||||
|
the response status code manually.
|
||||||
|
These errors add to the list of default errors [raised by Faraday](getting-started/errors.md).
|
||||||
|
|
||||||
|
All exceptions are initialized with a hash containing the response `status`, `headers`, and `body`.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||||
|
faraday.response :raise_error # raise Faraday::Error on status code 4xx or 5xx
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
conn.get('/wrong-url') # => Assume this raises a 404 response
|
||||||
|
rescue Faraday::ResourceNotFound => e
|
||||||
|
e.response_status #=> 404
|
||||||
|
e.response_headers #=> { ... }
|
||||||
|
e.response_body #=> "..."
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Specific exceptions are raised based on the HTTP Status code of the response.
|
||||||
|
|
||||||
|
## 4xx Errors
|
||||||
|
|
||||||
|
An HTTP status in the 400-499 range typically represents an error
|
||||||
|
by the client. They raise error classes inheriting from `Faraday::ClientError`.
|
||||||
|
|
||||||
|
| Status Code | Exception Class |
|
||||||
|
|---------------------------------------------------------------------|-------------------------------------|
|
||||||
|
| [400](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400) | `Faraday::BadRequestError` |
|
||||||
|
| [401](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) | `Faraday::UnauthorizedError` |
|
||||||
|
| [403](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403) | `Faraday::ForbiddenError` |
|
||||||
|
| [404](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404) | `Faraday::ResourceNotFound` |
|
||||||
|
| [407](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/407) | `Faraday::ProxyAuthError` |
|
||||||
|
| [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) | `Faraday::RequestTimeoutError` |
|
||||||
|
| [409](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409) | `Faraday::ConflictError` |
|
||||||
|
| [422](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422) | `Faraday::UnprocessableEntityError` |
|
||||||
|
| [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) | `Faraday::TooManyRequestsError` |
|
||||||
|
| 4xx (any other) | `Faraday::ClientError` |
|
||||||
|
|
||||||
|
## 5xx Errors
|
||||||
|
|
||||||
|
An HTTP status in the 500-599 range represents a server error, and raises a
|
||||||
|
`Faraday::ServerError` exception.
|
||||||
|
|
||||||
|
It's important to note that this exception is only returned if we receive a response and the
|
||||||
|
HTTP status in such response is in the 500-599 range.
|
||||||
|
Other kind of errors normally attributed to errors in the 5xx range (such as timeouts, failure to connect, etc...)
|
||||||
|
are raised as specific exceptions inheriting from `Faraday::Error`.
|
||||||
|
See [Faraday Errors](getting-started/errors.md) for more information on these.
|
||||||
|
|
||||||
|
### Missing HTTP status
|
||||||
|
|
||||||
|
The HTTP response status may be nil due to a malformed HTTP response from the
|
||||||
|
server, or a bug in the underlying HTTP library. This is considered a server error
|
||||||
|
and raised as `Faraday::NilStatusError`, which inherits from `Faraday::ServerError`.
|
||||||
|
|
||||||
|
## Middleware Options
|
||||||
|
|
||||||
|
The behavior of this middleware can be customized with the following options:
|
||||||
|
|
||||||
|
| Option | Default | Description |
|
||||||
|
|----------------------|---------|-------------|
|
||||||
|
| **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
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||||
|
faraday.response :raise_error, include_request: true, allowed_statuses: [404]
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
conn.get('/wrong-url') # => Assume this raises a 404 response
|
||||||
|
conn.get('/protected-url') # => Assume this raises a 401 response
|
||||||
|
rescue Faraday::UnauthorizedError => e
|
||||||
|
e.response[:status] # => 401
|
||||||
|
e.response[:headers] # => { ... }
|
||||||
|
e.response[:body] # => "..."
|
||||||
|
e.response[:request][:url_path] # => "/protected-url"
|
||||||
|
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.
|
@ -1,19 +1,8 @@
|
|||||||
---
|
# URL Encoding
|
||||||
layout: documentation
|
|
||||||
title: "UrlEncoded Middleware"
|
|
||||||
permalink: /middleware/url-encoded
|
|
||||||
hide: true
|
|
||||||
prev_name: Multipart Middleware
|
|
||||||
prev_link: ./multipart
|
|
||||||
next_name: Retry Middleware
|
|
||||||
next_link: ./retry
|
|
||||||
top_name: Back to Middleware
|
|
||||||
top_link: ./list
|
|
||||||
---
|
|
||||||
|
|
||||||
The `UrlEncoded` middleware converts a `Faraday::Request#body` hash of key/value pairs into a url-encoded request body.
|
The `UrlEncoded` middleware converts a `Faraday::Request#body` hash of key/value pairs into a url-encoded request body.
|
||||||
The middleware also automatically sets the `Content-Type` header to `application/x-www-form-urlencoded`.
|
The middleware also automatically sets the `Content-Type` header to `application/x-www-form-urlencoded`.
|
||||||
The way parameters are serialized can be [customized][customize].
|
The way parameters are serialized can be customized in the [Request Options](customization/request-options.md).
|
||||||
|
|
||||||
|
|
||||||
### Example Usage
|
### Example Usage
|
||||||
@ -39,4 +28,4 @@ conn.post('/', { a: [1, 3], b: { c: 2, d: 4} })
|
|||||||
# Body: a%5B%5D=1&a%5B%5D=3&b%5Bc%5D=2&b%5Bd%5D=4
|
# Body: a%5B%5D=1&a%5B%5D=3&b%5Bc%5D=2&b%5Bd%5D=4
|
||||||
```
|
```
|
||||||
|
|
||||||
[customize]: ../usage/customize/#changing-how-parameters-are-serialized
|
[customize]: ../usage/customize#changing-how-parameters-are-serialized
|
@ -1,11 +1,4 @@
|
|||||||
---
|
# Middleware
|
||||||
layout: documentation
|
|
||||||
title: "Middleware"
|
|
||||||
permalink: /middleware/
|
|
||||||
next_name: Available Middleware
|
|
||||||
next_link: ./list
|
|
||||||
order: 3
|
|
||||||
---
|
|
||||||
|
|
||||||
Under the hood, Faraday uses a Rack-inspired middleware stack for making
|
Under the hood, Faraday uses a Rack-inspired middleware stack for making
|
||||||
requests. Much of Faraday's power is unlocked with custom middleware. Some
|
requests. Much of Faraday's power is unlocked with custom middleware. Some
|
||||||
@ -19,19 +12,18 @@ Here are some of the features that middleware can provide:
|
|||||||
- following redirects
|
- following redirects
|
||||||
- JSON encoding/decoding
|
- JSON encoding/decoding
|
||||||
- logging
|
- logging
|
||||||
- retrying
|
|
||||||
|
|
||||||
To use these great features, create a `Faraday::Connection` with `Faraday.new`
|
To use these great features, create a `Faraday::Connection` with `Faraday.new`
|
||||||
and add the correct middleware in a block. For example:
|
and add the correct middleware in a block. For example:
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
require 'faraday_middleware'
|
require 'faraday'
|
||||||
|
|
||||||
conn = Faraday.new do |f|
|
conn = Faraday.new do |f|
|
||||||
f.request :json # encode req bodies as JSON
|
f.request :json # encode req bodies as JSON
|
||||||
f.request :retry # retry transient failures
|
f.response :logger # logs request and responses
|
||||||
f.response :follow_redirects # follow redirects
|
|
||||||
f.response :json # decode response bodies as JSON
|
f.response :json # decode response bodies as JSON
|
||||||
|
f.adapter :net_http # Use the Net::HTTP adapter
|
||||||
end
|
end
|
||||||
response = conn.get("http://httpbingo.org/get")
|
response = conn.get("http://httpbingo.org/get")
|
||||||
```
|
```
|
||||||
@ -45,10 +37,44 @@ run, Faraday will return a `Faraday::Response` to the end user.
|
|||||||
|
|
||||||
The order in which middleware is stacked is important. Like with Rack, the first
|
The order in which middleware is stacked is important. Like with Rack, the first
|
||||||
middleware on the list wraps all others, while the last middleware is the
|
middleware on the list wraps all others, while the last middleware is the
|
||||||
innermost one. If you want to use a custom [adapter](../adapters), it must
|
innermost one. If you want to use a custom [adapter](adapters/index.md), it must
|
||||||
therefore be last.
|
therefore be last.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
This is what makes things like the "retry middleware" possible.
|
||||||
|
It doesn't really matter if the middleware was registered as a request or a response one, the only thing that matter is how they're added to the stack.
|
||||||
|
|
||||||
|
Say you have the following:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.new(...) do |conn|
|
||||||
|
conn.request :authorization
|
||||||
|
conn.response :json
|
||||||
|
conn.response :parse_dates
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
This will result into a middleware stack like this:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
authorization do
|
||||||
|
# authorization request hook
|
||||||
|
json do
|
||||||
|
# json request hook
|
||||||
|
parse_dates do
|
||||||
|
# parse_dates request hook
|
||||||
|
response = adapter.perform(request)
|
||||||
|
# parse_dates response hook
|
||||||
|
end
|
||||||
|
# json response hook
|
||||||
|
end
|
||||||
|
# authorization response hook
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, you can see that `parse_dates` is the LAST middleware processing the request, and the FIRST middleware processing the response.
|
||||||
|
This is why it's important for the adapter to always be at the end of the middleware list.
|
||||||
|
|
||||||
### Using Middleware
|
### Using Middleware
|
||||||
|
|
||||||
@ -64,8 +90,8 @@ For example, the `Faraday::Request::UrlEncoded` middleware registers itself in
|
|||||||
# Faraday::Response and Faraday::Adapter registries
|
# Faraday::Response and Faraday::Adapter registries
|
||||||
conn = Faraday.new do |f|
|
conn = Faraday.new do |f|
|
||||||
f.request :url_encoded
|
f.request :url_encoded
|
||||||
f.response :follow_redirects
|
f.response :logger
|
||||||
f.adapter :httpclient
|
f.adapter :net_http
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -75,8 +101,8 @@ or:
|
|||||||
# identical, but add the class directly instead of using lookups
|
# identical, but add the class directly instead of using lookups
|
||||||
conn = Faraday.new do |f|
|
conn = Faraday.new do |f|
|
||||||
f.use Faraday::Request::UrlEncoded
|
f.use Faraday::Request::UrlEncoded
|
||||||
f.use FaradayMiddleware::FollowRedirects
|
f.use Faraday::Response::Logger
|
||||||
f.use Faraday::Adapter::HTTPClient
|
f.use Faraday::Adapter::NetHttp
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -84,19 +110,67 @@ This is also the place to pass options. For example:
|
|||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
conn = Faraday.new do |f|
|
conn = Faraday.new do |f|
|
||||||
f.request :retry, max: 10
|
f.request :logger, bodies: true
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### DEFAULT_OPTIONS
|
||||||
|
|
||||||
|
`DEFAULT_OPTIONS` improve the flexibility and customizability of new and existing middleware. Class-level `DEFAULT_OPTIONS` and the ability to set these defaults at the application level compliment existing functionality in which options can be passed into middleware on a per-instance basis.
|
||||||
|
|
||||||
|
#### Using DEFAULT_OPTIONS
|
||||||
|
|
||||||
|
Using `RaiseError` as an example, you can see that `DEFAULT_OPTIONS` have been defined at the top of the class:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
DEFAULT_OPTIONS = { include_request: true }.freeze
|
||||||
|
```
|
||||||
|
|
||||||
|
These options will be set at the class level upon instantiation and referenced as needed within the class. From our same example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
def response_values(env)
|
||||||
|
...
|
||||||
|
return response unless options[:include_request]
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
If the default value provides the desired functionality, no further consideration is needed.
|
||||||
|
|
||||||
|
#### Setting Alternative Options per Application
|
||||||
|
|
||||||
|
In the case where it is desirable to change the default option for all instances within an application, it can be done by configuring the options in a `/config/initializers` file. For example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
# config/initializers/faraday_config.rb
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Faraday.new do |f|
|
||||||
|
...
|
||||||
|
f.response :raise_error, include_request: true
|
||||||
|
...
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Available Middleware
|
### Available Middleware
|
||||||
|
|
||||||
|
The following pages provide detailed configuration for the middleware that ships with Faraday:
|
||||||
|
* [Authentication](middleware/included/authentication.md)
|
||||||
|
* [URL Encoding](middleware/included/url-encoding.md)
|
||||||
|
* [JSON Encoding/Decoding](middleware/included/json.md)
|
||||||
|
* [Instrumentation](middleware/included/instrumentation.md)
|
||||||
|
* [Logging](middleware/included/logging.md)
|
||||||
|
* [Raising Errors](middleware/included/raising-errors.md)
|
||||||
|
|
||||||
The [Awesome Faraday](https://github.com/lostisland/awesome-faraday/) project
|
The [Awesome Faraday](https://github.com/lostisland/awesome-faraday/) project
|
||||||
has a complete list of useful, well-maintained Faraday middleware. Middleware is
|
has a complete list of useful, well-maintained Faraday middleware. Middleware is
|
||||||
often provided by external gems, like the
|
often provided by external gems, like the
|
||||||
[faraday-middleware](https://github.com/lostisland/faraday_middleware) gem.
|
[faraday-retry](https://github.com/lostisland/faraday-retry) gem.
|
||||||
|
|
||||||
We also have [great documentation](list) for the middleware that ships with
|
|
||||||
Faraday.
|
|
||||||
|
|
||||||
### Detailed Example
|
### Detailed Example
|
||||||
|
|
||||||
@ -104,32 +178,23 @@ Here's a more realistic example:
|
|||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
Faraday.new(...) do |conn|
|
Faraday.new(...) do |conn|
|
||||||
# POST/PUT params encoders:
|
# POST/PUT params encoder
|
||||||
conn.request :multipart
|
|
||||||
conn.request :url_encoded
|
conn.request :url_encoded
|
||||||
|
|
||||||
# Last middleware must be the adapter:
|
# Logging of requests/responses
|
||||||
conn.adapter :typhoeus
|
conn.response :logger
|
||||||
|
|
||||||
|
# Last middleware must be the adapter
|
||||||
|
conn.adapter :net_http
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
This request middleware setup affects POST/PUT requests in the following way:
|
This request middleware setup affects POST/PUT requests in the following way:
|
||||||
|
|
||||||
1. `Request::Multipart` checks for files in the payload, otherwise leaves
|
1. `Request::UrlEncoded` encodes as "application/x-www-form-urlencoded" if not
|
||||||
everything untouched;
|
already encoded or of another type.
|
||||||
2. `Request::UrlEncoded` encodes as "application/x-www-form-urlencoded" if not
|
2. `Response::Logger` logs request and response headers, can be configured to log bodies as well.
|
||||||
already encoded or of another type
|
|
||||||
|
|
||||||
Swapping middleware means giving the other priority. Specifying the
|
Swapping middleware means giving the other priority. Specifying the
|
||||||
"Content-Type" for the request is explicitly stating which middleware should
|
"Content-Type" for the request is explicitly stating which middleware should
|
||||||
process it.
|
process it.
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# uploading a file:
|
|
||||||
payload[:profile_pic] = Faraday::FilePart.new('/path/to/avatar.jpg', 'image/jpeg')
|
|
||||||
|
|
||||||
# "Multipart" middleware detects files and encodes with "multipart/form-data":
|
|
||||||
conn.put '/profile', payload
|
|
||||||
```
|
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Authentication Middleware"
|
|
||||||
permalink: /middleware/authentication
|
|
||||||
hide: true
|
|
||||||
next_name: Multipart Middleware
|
|
||||||
next_link: ./multipart
|
|
||||||
top_name: Back to Middleware
|
|
||||||
top_link: ./list
|
|
||||||
---
|
|
||||||
|
|
||||||
The `Faraday::Request::Authorization` middleware allows you to automatically add an `Authorization` header
|
|
||||||
to your requests. It also features 2 specialised sub-classes that provide useful extra features for Basic Authentication
|
|
||||||
and Token Authentication requests.
|
|
||||||
|
|
||||||
### Any Authentication
|
|
||||||
|
|
||||||
The generic `Authorization` middleware allows you to add any type of Authorization header.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Faraday.new(...) do |conn|
|
|
||||||
conn.request :authorization, 'Bearer', 'authentication-token'
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Basic Authentication
|
|
||||||
|
|
||||||
`BasicAuthentication` adds a 'Basic' type Authorization header to a Faraday request.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Faraday.new(...) do |conn|
|
|
||||||
conn.request :basic_auth, 'username', 'password'
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
### Token Authentication
|
|
||||||
|
|
||||||
`TokenAuthentication` adds a 'Token' type Authorization header to a Faraday request.
|
|
||||||
You can optionally provide a hash of `options` that will be appended to the token.
|
|
||||||
This is not used anymore in modern web and have been replaced by Bearer tokens.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
Faraday.new(...) do |conn|
|
|
||||||
conn.request :token_auth, 'authentication-token', **options
|
|
||||||
end
|
|
||||||
```
|
|
@ -1,68 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Multipart Middleware"
|
|
||||||
permalink: /middleware/multipart
|
|
||||||
hide: true
|
|
||||||
prev_name: Authentication Middleware
|
|
||||||
prev_link: ./authentication
|
|
||||||
next_name: UrlEncoded Middleware
|
|
||||||
next_link: ./url-encoded
|
|
||||||
top_name: Back to Middleware
|
|
||||||
top_link: ./list
|
|
||||||
---
|
|
||||||
|
|
||||||
The `Multipart` middleware converts a `Faraday::Request#body` Hash of key/value
|
|
||||||
pairs into a multipart form request, but only under these conditions:
|
|
||||||
|
|
||||||
* The request's Content-Type is "multipart/form-data"
|
|
||||||
* Content-Type is unspecified, AND one of the values in the Body responds to
|
|
||||||
`#content_type`.
|
|
||||||
|
|
||||||
Faraday contains a couple helper classes for multipart values:
|
|
||||||
|
|
||||||
* `Faraday::FilePart` wraps binary file data with a Content-Type. The file data
|
|
||||||
can be specified with a String path to a local file, or an IO object.
|
|
||||||
* `Faraday::ParamPart` wraps a String value with a Content-Type, and optionally
|
|
||||||
a Content-ID.
|
|
||||||
|
|
||||||
Note: `Faraday::ParamPart` was added in Faraday v0.16.0. Before that,
|
|
||||||
`Faraday::FilePart` was called `Faraday::UploadIO`.
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
conn = Faraday.new(...) do |f|
|
|
||||||
f.request :multipart
|
|
||||||
...
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Payload can be a mix of POST data and multipart values.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# regular POST form value
|
|
||||||
payload = { string: 'value' }
|
|
||||||
|
|
||||||
# filename for this value is File.basename(__FILE__)
|
|
||||||
payload[:file] = Faraday::FilePart.new(__FILE__, 'text/x-ruby')
|
|
||||||
|
|
||||||
# specify filename because IO object doesn't know it
|
|
||||||
payload[:file_with_name] = Faraday::FilePart.new(File.open(__FILE__),
|
|
||||||
'text/x-ruby',
|
|
||||||
File.basename(__FILE__))
|
|
||||||
|
|
||||||
# Sets a custom Content-Disposition:
|
|
||||||
# nil filename still defaults to File.basename(__FILE__)
|
|
||||||
payload[:file_with_header] = Faraday::FilePart.new(__FILE__,
|
|
||||||
'text/x-ruby', nil,
|
|
||||||
'Content-Disposition' => 'form-data; foo=1')
|
|
||||||
|
|
||||||
# Upload raw json with content type
|
|
||||||
payload[:raw_data] = Faraday::ParamPart.new({a: 1}.to_json, 'application/json')
|
|
||||||
|
|
||||||
# optionally sets Content-ID too
|
|
||||||
payload[:raw_with_id] = Faraday::ParamPart.new({a: 1}.to_json, 'application/json',
|
|
||||||
'foo-123')
|
|
||||||
|
|
||||||
conn.post('/', payload)
|
|
||||||
```
|
|
@ -1,126 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Retry Middleware"
|
|
||||||
permalink: /middleware/retry
|
|
||||||
hide: true
|
|
||||||
prev_name: UrlEncoded Middleware
|
|
||||||
prev_link: ./url-encoded
|
|
||||||
next_name: Instrumentation Middleware
|
|
||||||
next_link: ./instrumentation
|
|
||||||
top_name: Back to Middleware
|
|
||||||
top_link: ./list
|
|
||||||
---
|
|
||||||
|
|
||||||
The `Retry` middleware automatically retries requests that fail due to intermittent client
|
|
||||||
or server errors (such as network hiccups).
|
|
||||||
By default, it retries 2 times and handles only timeout exceptions.
|
|
||||||
It can be configured with an arbitrary number of retries, a list of exceptions to handle,
|
|
||||||
a retry interval, a percentage of randomness to add to the retry interval, and a backoff factor.
|
|
||||||
The middleware can also handle the [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header automatically when configured with the right status codes (see below for an example).
|
|
||||||
|
|
||||||
### Example Usage
|
|
||||||
|
|
||||||
This example will result in a first interval that is random between 0.05 and 0.075
|
|
||||||
and a second interval that is random between 0.1 and 0.125.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
retry_options = {
|
|
||||||
max: 2,
|
|
||||||
interval: 0.05,
|
|
||||||
interval_randomness: 0.5,
|
|
||||||
backoff_factor: 2
|
|
||||||
}
|
|
||||||
|
|
||||||
conn = Faraday.new(...) do |f|
|
|
||||||
f.request :retry, retry_options
|
|
||||||
...
|
|
||||||
end
|
|
||||||
|
|
||||||
conn.get('/')
|
|
||||||
```
|
|
||||||
|
|
||||||
### Control when the middleware will retry requests
|
|
||||||
|
|
||||||
By default, the `Retry` middleware will only retry idempotent methods and the most common network-related exceptions.
|
|
||||||
You can change this behaviour by providing the right option when adding the middleware to your connection.
|
|
||||||
|
|
||||||
#### Specify which methods will be retried
|
|
||||||
|
|
||||||
You can provide a `methods` option with a list of HTTP methods.
|
|
||||||
This will replace the default list of HTTP methods: `delete`, `get`, `head`, `options`, `put`.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
retry_options = {
|
|
||||||
methods: %i[get post]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Specify which exceptions should trigger a retry
|
|
||||||
|
|
||||||
You can provide an `exceptions` option with a list of exceptions that will replace
|
|
||||||
the default list of network-related exceptions: `Errno::ETIMEDOUT`, `Timeout::Error`, `Faraday::TimeoutError`.
|
|
||||||
This can be particularly useful when combined with the [RaiseError][raise_error] middleware.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
retry_options = {
|
|
||||||
exceptions: [Faraday::ResourceNotFound, Faraday::UnauthorizedError]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Specify on which response statuses to retry
|
|
||||||
|
|
||||||
By default the `Retry` middleware will only retry the request if one of the expected exceptions arise.
|
|
||||||
However, you can specify a list of HTTP statuses you'd like to be retried. When you do so, the middleware will
|
|
||||||
check the response `status` code and will retry the request if included in the list.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
retry_options = {
|
|
||||||
retry_statuses: [401, 409]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Automatically handle the `Retry-After` header
|
|
||||||
|
|
||||||
Some APIs, like the [Slack API](https://api.slack.com/docs/rate-limits), will inform you when you reach their API limits by replying with a response status code of `429` and a response header of `Retry-After` containing a time in seconds. You should then only retry querying after the amount of time provided by the `Retry-After` header, otherwise you won't get a response.
|
|
||||||
|
|
||||||
You can automatically handle this and have Faraday pause and retry for the right amount of time by including the `429` status code in the retry statuses list:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
retry_options = {
|
|
||||||
retry_statuses: [429]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Specify a custom retry logic
|
|
||||||
|
|
||||||
You can also specify a custom retry logic with the `retry_if` option.
|
|
||||||
This option accepts a block that will receive the `env` object and the exception raised
|
|
||||||
and should decide if the code should retry still the action or not independent of the retry count.
|
|
||||||
This would be useful if the exception produced is non-recoverable or if the the HTTP method called is not idempotent.
|
|
||||||
|
|
||||||
**NOTE:** this option will only be used for methods that are not included in the `methods` option.
|
|
||||||
If you want this to apply to all HTTP methods, pass `methods: []` as an additional option.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# Retries the request if response contains { success: false }
|
|
||||||
retry_options = {
|
|
||||||
retry_if: -> (env, _exc) { env.body[:success] == 'false' }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Call a block on every retry
|
|
||||||
|
|
||||||
You can specify a block through the `retry_block` option that will be called before every retry.
|
|
||||||
There are many different applications for this feature, spacing from instrumentation to monitoring.
|
|
||||||
Request environment, middleware options, current number of retries and the exception is passed to the block as parameters.
|
|
||||||
For example, you might want to keep track of the response statuses:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
response_statuses = []
|
|
||||||
retry_options = {
|
|
||||||
retry_block: -> (env, options, retries, exc) { response_statuses << env.status }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
[raise_error]: ../middleware/raise-error
|
|
@ -1,49 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Raise Error Middleware"
|
|
||||||
permalink: /middleware/raise-error
|
|
||||||
hide: true
|
|
||||||
prev_name: Logger Middleware
|
|
||||||
prev_link: ./logger
|
|
||||||
top_name: Back to Middleware
|
|
||||||
top_link: ./list
|
|
||||||
---
|
|
||||||
|
|
||||||
The `RaiseError` middleware raises a `Faraday::Error` exception if an HTTP
|
|
||||||
response returns with a 4xx or 5xx status code. All exceptions are initialized
|
|
||||||
providing the response `status`, `headers`, and `body`.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
begin
|
|
||||||
conn.get('/wrong-url') # => Assume this raises a 404 response
|
|
||||||
rescue Faraday::ResourceNotFound => e
|
|
||||||
e.response[:status] #=> 404
|
|
||||||
e.response[:headers] #=> { ... }
|
|
||||||
e.response[:body] #=> "..."
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
Specific exceptions are raised based on the HTTP Status code, according to the list below:
|
|
||||||
|
|
||||||
An HTTP status in the 400-499 range typically represents an error
|
|
||||||
by the client. They raise error classes inheriting from `Faraday::ClientError`.
|
|
||||||
|
|
||||||
* 400 => `Faraday::BadRequestError`
|
|
||||||
* 401 => `Faraday::UnauthorizedError`
|
|
||||||
* 403 => `Faraday::ForbiddenError`
|
|
||||||
* 404 => `Faraday::ResourceNotFound`
|
|
||||||
* 407 => `Faraday::ProxyAuthError`
|
|
||||||
* 409 => `Faraday::ConflictError`
|
|
||||||
* 422 => `Faraday::UnprocessableEntityError`
|
|
||||||
* 4xx => `Faraday::ClientError`
|
|
||||||
|
|
||||||
An HTTP status in the 500-599 range represents a server error, and raises a
|
|
||||||
`Faraday::ServerError` exception.
|
|
||||||
|
|
||||||
* 5xx => `Faraday::ServerError`
|
|
||||||
|
|
||||||
The HTTP response status may be nil due to a malformed HTTP response from the
|
|
||||||
server, or a bug in the underlying HTTP library. It inherits from
|
|
||||||
`Faraday::ServerError`.
|
|
||||||
|
|
||||||
* nil => `Faraday::NilStatusError`
|
|
26
docs/team.md
@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
layout: page
|
|
||||||
title: Team
|
|
||||||
permalink: /team/
|
|
||||||
order: 4
|
|
||||||
---
|
|
||||||
|
|
||||||
<div id="loader">
|
|
||||||
<div class="lds-ring"><div></div><div></div><div></div><div></div></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="team-content" class="hidden">
|
|
||||||
<h3 class="text-center">Active Maintainers</h3>
|
|
||||||
<div id="active-maintainers-list"></div>
|
|
||||||
|
|
||||||
<h3 class="text-center">Historical team</h3>
|
|
||||||
<div id="historical-team-list"></div>
|
|
||||||
|
|
||||||
<h3 class="text-center">All the contributors</h3>
|
|
||||||
<div id="contributors-list"></div>
|
|
||||||
|
|
||||||
<h3 class="text-center">And some extra help</h3>
|
|
||||||
<p class="text-center">Website and branding design: <a href="https://elelopic.design" target="_blank">Elena Lo Piccolo</a></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="{{ site.baseurl }}/assets/js/team.js"></script>
|
|
@ -1,182 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Usage"
|
|
||||||
permalink: /usage/
|
|
||||||
next_name: Customizing the Request
|
|
||||||
next_link: ./customize
|
|
||||||
order: 1
|
|
||||||
---
|
|
||||||
|
|
||||||
Let's fetch the home page for the wonderful
|
|
||||||
[httpbingo.org](https://httpbingo.org) service. Make a simple `GET` request by
|
|
||||||
requiring the Faraday gem and using `Faraday.get`:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
require 'faraday'
|
|
||||||
|
|
||||||
response = Faraday.get 'http://httpbingo.org'
|
|
||||||
```
|
|
||||||
|
|
||||||
This returns a `Faraday::Response` object with the response status, headers, and
|
|
||||||
body.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
response.status
|
|
||||||
# => 200
|
|
||||||
|
|
||||||
response.headers
|
|
||||||
# => {"server"=>"Fly/c375678 (2021-04-23)", "content-type"=> ...
|
|
||||||
|
|
||||||
response.body
|
|
||||||
# => "<!DOCTYPE html><html> ...
|
|
||||||
```
|
|
||||||
|
|
||||||
### GET
|
|
||||||
|
|
||||||
Faraday supports the following HTTP verbs that typically don't include a request
|
|
||||||
body:
|
|
||||||
|
|
||||||
- `get(url, params = nil, headers = nil)`
|
|
||||||
- `head(url, params = nil, headers = nil)`
|
|
||||||
- `delete(url, params = nil, headers = nil)`
|
|
||||||
- `trace(url, params = nil, headers = nil)`
|
|
||||||
|
|
||||||
You can specify URI query parameters and HTTP headers when making a request.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
url = 'http://httpbingo.org/get'
|
|
||||||
response = Faraday.get(url, {boom: 'zap'}, {'User-Agent' => 'myapp'})
|
|
||||||
# => GET http://httpbingo.org/get?boom=zap
|
|
||||||
```
|
|
||||||
|
|
||||||
### POST
|
|
||||||
|
|
||||||
Faraday also supports HTTP verbs with bodies. Instead of query parameters, these
|
|
||||||
accept a request body:
|
|
||||||
|
|
||||||
- `post(url, body = nil, headers = nil)`
|
|
||||||
- `put(url, body = nil, headers = nil)`
|
|
||||||
- `patch(url, body = nil, headers = nil)`
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
url = 'http://httpbingo.org/post'
|
|
||||||
|
|
||||||
# POST 'application/x-www-form-urlencoded' content
|
|
||||||
response = Faraday.post(url, "boom=zap")
|
|
||||||
|
|
||||||
# POST JSON content
|
|
||||||
response = Faraday.post(url, '{"boom": "zap"}',
|
|
||||||
"Content-Type" => "application/json")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Posting Forms
|
|
||||||
|
|
||||||
Faraday will automatically convert key/value hashes into proper form bodies.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# POST 'application/x-www-form-urlencoded' content
|
|
||||||
url = 'http://httpbingo.org/post'
|
|
||||||
response = Faraday.post(url, boom: 'zap')
|
|
||||||
# => POST 'boom=zap' to http://httpbingo.org/post
|
|
||||||
```
|
|
||||||
|
|
||||||
Faraday can also [upload files][multipart].
|
|
||||||
|
|
||||||
### Detailed HTTP Requests
|
|
||||||
|
|
||||||
Faraday supports a longer style for making requests. This is handy if you need
|
|
||||||
to change many of the defaults, or if the details of the HTTP request change
|
|
||||||
according to method arguments. Each of the HTTP verb helpers can yield a
|
|
||||||
`Faraday::Request` that can be modified before being sent.
|
|
||||||
|
|
||||||
This example shows a hypothetical search endpoint that accepts a JSON request
|
|
||||||
body as the actual search query.
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
response = Faraday.post('http://httpbingo.org/post') do |req|
|
|
||||||
req.params['limit'] = 100
|
|
||||||
req.headers['Content-Type'] = 'application/json'
|
|
||||||
req.body = {query: 'chunky bacon'}.to_json
|
|
||||||
end
|
|
||||||
# => POST http://httpbingo.org/post?limit=100
|
|
||||||
```
|
|
||||||
|
|
||||||
### Customizing Faraday::Connection
|
|
||||||
|
|
||||||
You may want to create a `Faraday::Connection` to setup a common config for
|
|
||||||
multiple requests. The connection object can be configured with things like:
|
|
||||||
|
|
||||||
- default request headers & query parameters
|
|
||||||
- network settings like proxy or timeout
|
|
||||||
- common URL base path
|
|
||||||
- Faraday adapter & middleware (see below)
|
|
||||||
|
|
||||||
Create a `Faraday::Connection` by calling `Faraday.new`. The HTTP verbs
|
|
||||||
described above (`get`, `post`, ...) are `Faraday::Connection` methods:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
conn = Faraday.new(
|
|
||||||
url: 'http://httpbingo.org',
|
|
||||||
params: {param: '1'},
|
|
||||||
headers: {'Content-Type' => 'application/json'}
|
|
||||||
)
|
|
||||||
|
|
||||||
response = conn.post('/post') do |req|
|
|
||||||
req.params['limit'] = 100
|
|
||||||
req.body = {query: 'chunky bacon'}.to_json
|
|
||||||
end
|
|
||||||
# => POST http://httpbingo.org/post?param=1&limit=100
|
|
||||||
```
|
|
||||||
|
|
||||||
### Adapters
|
|
||||||
|
|
||||||
Adapters are responsible for actually executing HTTP requests. The default
|
|
||||||
adapter uses Ruby's `Net::HTTP`, but there are many different adapters
|
|
||||||
available. You might want to use Faraday with the Typhoeus adapter, for example.
|
|
||||||
[Learn more about Adapters](../adapters).
|
|
||||||
|
|
||||||
### Middleware
|
|
||||||
|
|
||||||
Under the hood, Faraday uses a Rack-inspired middleware stack for making
|
|
||||||
requests. Much of Faraday's power is unlocked with custom middleware. Some
|
|
||||||
middleware is included with Faraday, and others are in external gems. [Learn
|
|
||||||
more about Middleware](../middleware).
|
|
||||||
|
|
||||||
Here are some of the features that middleware can provide:
|
|
||||||
|
|
||||||
- authentication
|
|
||||||
- caching responses on disk or in memory
|
|
||||||
- cookies
|
|
||||||
- following redirects
|
|
||||||
- JSON encoding/decoding
|
|
||||||
- logging
|
|
||||||
- retrying
|
|
||||||
|
|
||||||
To use these great features, create a `Faraday::Connection` with `Faraday.new`
|
|
||||||
and add the correct middleware in a block. For example:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
require 'faraday_middleware'
|
|
||||||
|
|
||||||
conn = Faraday.new do |f|
|
|
||||||
f.request :json # encode req bodies as JSON
|
|
||||||
f.request :retry # retry transient failures
|
|
||||||
f.response :follow_redirects # follow redirects
|
|
||||||
f.response :json # decode response bodies as JSON
|
|
||||||
end
|
|
||||||
response = conn.get("http://httpbingo.org/get")
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Default Connection, Default Middleware
|
|
||||||
|
|
||||||
Remember how we said that Faraday will automatically encode key/value hash
|
|
||||||
bodies into form bodies? Internally, the top level shortcut methods
|
|
||||||
`Faraday.get`, `post`, etc. use a simple default `Faraday::Connection`. The only
|
|
||||||
middleware used for the default connection is `:url_encoded`, which encodes
|
|
||||||
those form hashes.
|
|
||||||
|
|
||||||
Note that if you create your own connection with middleware, it won't encode
|
|
||||||
form bodies unless you too include the `:url_encoded` middleware!
|
|
||||||
|
|
||||||
[encoding]: ../middleware/url-encoded
|
|
||||||
[multipart]: ../middleware/multipart
|
|
@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
layout: documentation
|
|
||||||
title: "Streaming Responses"
|
|
||||||
permalink: /usage/streaming
|
|
||||||
hide: true
|
|
||||||
top_name: Usage
|
|
||||||
top_link: ./
|
|
||||||
prev_name: Customizing the Request
|
|
||||||
prev_link: ./customize
|
|
||||||
---
|
|
||||||
|
|
||||||
Sometimes you might need to receive a streaming response.
|
|
||||||
You can do this with the `on_data` request option.
|
|
||||||
|
|
||||||
The `on_data` callback is a receives tuples of chunk Strings, and the total
|
|
||||||
of received bytes so far.
|
|
||||||
|
|
||||||
This example implements such a callback:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
# A buffer to store the streamed data
|
|
||||||
streamed = []
|
|
||||||
|
|
||||||
conn.get('/nigiri/sake.json') do |req|
|
|
||||||
# Set a callback which will receive tuples of chunk Strings
|
|
||||||
# and the sum of characters received so far
|
|
||||||
req.options.on_data = Proc.new do |chunk, overall_received_bytes|
|
|
||||||
puts "Received #{overall_received_bytes} characters"
|
|
||||||
streamed << chunk
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Joins all response chunks together
|
|
||||||
streamed.join
|
|
||||||
```
|
|
||||||
|
|
||||||
The `on_data` streaming is currently only supported by the `Net::HTTP` adapter.
|
|
@ -12,10 +12,15 @@ class Client
|
|||||||
@conn = conn
|
@conn = conn
|
||||||
end
|
end
|
||||||
|
|
||||||
def sushi(jname, params: {})
|
def httpbingo(jname, params: {})
|
||||||
res = @conn.get("/#{jname}", params)
|
res = @conn.get("/#{jname}", params)
|
||||||
data = JSON.parse(res.body)
|
data = JSON.parse(res.body)
|
||||||
data['name']
|
data['origin']
|
||||||
|
end
|
||||||
|
|
||||||
|
def foo(params)
|
||||||
|
res = @conn.post('/foo', JSON.dump(params))
|
||||||
|
res.status
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -24,42 +29,42 @@ RSpec.describe Client do
|
|||||||
let(:conn) { Faraday.new { |b| b.adapter(:test, stubs) } }
|
let(:conn) { Faraday.new { |b| b.adapter(:test, stubs) } }
|
||||||
let(:client) { Client.new(conn) }
|
let(:client) { Client.new(conn) }
|
||||||
|
|
||||||
it 'parses name' do
|
it 'parses origin' do
|
||||||
stubs.get('/ebi') do |env|
|
stubs.get('/ip') do |env|
|
||||||
# optional: you can inspect the Faraday::Env
|
# optional: you can inspect the Faraday::Env
|
||||||
expect(env.url.path).to eq('/ebi')
|
expect(env.url.path).to eq('/ip')
|
||||||
[
|
[
|
||||||
200,
|
200,
|
||||||
{ 'Content-Type': 'application/javascript' },
|
{ 'Content-Type': 'application/javascript' },
|
||||||
'{"name": "shrimp"}'
|
'{"origin": "127.0.0.1"}'
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
# uncomment to trigger stubs.verify_stubbed_calls failure
|
# uncomment to trigger stubs.verify_stubbed_calls failure
|
||||||
# stubs.get('/unused') { [404, {}, ''] }
|
# stubs.get('/unused') { [404, {}, ''] }
|
||||||
|
|
||||||
expect(client.sushi('ebi')).to eq('shrimp')
|
expect(client.httpbingo('ip')).to eq('127.0.0.1')
|
||||||
stubs.verify_stubbed_calls
|
stubs.verify_stubbed_calls
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles 404' do
|
it 'handles 404' do
|
||||||
stubs.get('/ebi') do
|
stubs.get('/api') do
|
||||||
[
|
[
|
||||||
404,
|
404,
|
||||||
{ 'Content-Type': 'application/javascript' },
|
{ 'Content-Type': 'application/javascript' },
|
||||||
'{}'
|
'{}'
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
expect(client.sushi('ebi')).to be_nil
|
expect(client.httpbingo('api')).to be_nil
|
||||||
stubs.verify_stubbed_calls
|
stubs.verify_stubbed_calls
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles exception' do
|
it 'handles exception' do
|
||||||
stubs.get('/ebi') do
|
stubs.get('/api') do
|
||||||
raise Faraday::ConnectionFailed, nil
|
raise Faraday::ConnectionFailed
|
||||||
end
|
end
|
||||||
|
|
||||||
expect { client.sushi('ebi') }.to raise_error(Faraday::ConnectionFailed)
|
expect { client.httpbingo('api') }.to raise_error(Faraday::ConnectionFailed)
|
||||||
stubs.verify_stubbed_calls
|
stubs.verify_stubbed_calls
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -67,17 +72,47 @@ RSpec.describe Client do
|
|||||||
let(:stubs) { Faraday::Adapter::Test::Stubs.new(strict_mode: true) }
|
let(:stubs) { Faraday::Adapter::Test::Stubs.new(strict_mode: true) }
|
||||||
|
|
||||||
it 'verifies the all parameter values are identical' do
|
it 'verifies the all parameter values are identical' do
|
||||||
stubs.get('/ebi?abc=123') do
|
stubs.get('/api?abc=123') do
|
||||||
[
|
[
|
||||||
200,
|
200,
|
||||||
{ 'Content-Type': 'application/javascript' },
|
{ 'Content-Type': 'application/javascript' },
|
||||||
'{"name": "shrimp"}'
|
'{"origin": "127.0.0.1"}'
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
# uncomment to raise Stubs::NotFound
|
# uncomment to raise Stubs::NotFound
|
||||||
# expect(client.sushi('ebi', params: { abc: 123, foo: 'Kappa' })).to eq('shrimp')
|
# expect(client.httpbingo('api', params: { abc: 123, foo: 'Kappa' })).to eq('127.0.0.1')
|
||||||
expect(client.sushi('ebi', params: { abc: 123 })).to eq('shrimp')
|
expect(client.httpbingo('api', params: { abc: 123 })).to eq('127.0.0.1')
|
||||||
|
stubs.verify_stubbed_calls
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'When the Faraday connection is configured with FlatParamsEncoder' do
|
||||||
|
let(:conn) { Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) { |b| b.adapter(:test, stubs) } }
|
||||||
|
|
||||||
|
it 'handles the same multiple URL parameters' do
|
||||||
|
stubs.get('/api?a=x&a=y&a=z') { [200, { 'Content-Type' => 'application/json' }, '{"origin": "127.0.0.1"}'] }
|
||||||
|
|
||||||
|
# uncomment to raise Stubs::NotFound
|
||||||
|
# expect(client.httpbingo('api', params: { a: %w[x y] })).to eq('127.0.0.1')
|
||||||
|
expect(client.httpbingo('api', params: { a: %w[x y z] })).to eq('127.0.0.1')
|
||||||
|
stubs.verify_stubbed_calls
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'When you want to test the body, you can use a proc as well as string' do
|
||||||
|
it 'tests with a string' do
|
||||||
|
stubs.post('/foo', '{"name":"YK"}') { [200, {}, ''] }
|
||||||
|
|
||||||
|
expect(client.foo(name: 'YK')).to eq 200
|
||||||
|
stubs.verify_stubbed_calls
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'tests with a proc' do
|
||||||
|
check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }
|
||||||
|
stubs.post('/foo', check) { [200, {}, ''] }
|
||||||
|
|
||||||
|
expect(client.foo(name: 'YK', created_at: Time.now)).to eq 200
|
||||||
stubs.verify_stubbed_calls
|
stubs.verify_stubbed_calls
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -13,24 +13,29 @@ class Client
|
|||||||
@conn = conn
|
@conn = conn
|
||||||
end
|
end
|
||||||
|
|
||||||
def sushi(jname)
|
def httpbingo(jname, params: {})
|
||||||
res = @conn.get("/#{jname}")
|
res = @conn.get("/#{jname}", params)
|
||||||
data = JSON.parse(res.body)
|
data = JSON.parse(res.body)
|
||||||
data['name']
|
data['origin']
|
||||||
|
end
|
||||||
|
|
||||||
|
def foo(params)
|
||||||
|
res = @conn.post('/foo', JSON.dump(params))
|
||||||
|
res.status
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Example API client test
|
# Example API client test
|
||||||
class ClientTest < Test::Unit::TestCase
|
class ClientTest < Test::Unit::TestCase
|
||||||
def test_sushi_name
|
def test_httpbingo_name
|
||||||
stubs = Faraday::Adapter::Test::Stubs.new
|
stubs = Faraday::Adapter::Test::Stubs.new
|
||||||
stubs.get('/ebi') do |env|
|
stubs.get('/api') do |env|
|
||||||
# optional: you can inspect the Faraday::Env
|
# optional: you can inspect the Faraday::Env
|
||||||
assert_equal '/ebi', env.url.path
|
assert_equal '/api', env.url.path
|
||||||
[
|
[
|
||||||
200,
|
200,
|
||||||
{ 'Content-Type': 'application/javascript' },
|
{ 'Content-Type': 'application/javascript' },
|
||||||
'{"name": "shrimp"}'
|
'{"origin": "127.0.0.1"}'
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -38,13 +43,13 @@ class ClientTest < Test::Unit::TestCase
|
|||||||
# stubs.get('/unused') { [404, {}, ''] }
|
# stubs.get('/unused') { [404, {}, ''] }
|
||||||
|
|
||||||
cli = client(stubs)
|
cli = client(stubs)
|
||||||
assert_equal 'shrimp', cli.sushi('ebi')
|
assert_equal '127.0.0.1', cli.httpbingo('api')
|
||||||
stubs.verify_stubbed_calls
|
stubs.verify_stubbed_calls
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sushi_404
|
def test_httpbingo_not_found
|
||||||
stubs = Faraday::Adapter::Test::Stubs.new
|
stubs = Faraday::Adapter::Test::Stubs.new
|
||||||
stubs.get('/ebi') do
|
stubs.get('/api') do
|
||||||
[
|
[
|
||||||
404,
|
404,
|
||||||
{ 'Content-Type': 'application/javascript' },
|
{ 'Content-Type': 'application/javascript' },
|
||||||
@ -53,23 +58,83 @@ class ClientTest < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
|
|
||||||
cli = client(stubs)
|
cli = client(stubs)
|
||||||
assert_nil cli.sushi('ebi')
|
assert_nil cli.httpbingo('api')
|
||||||
stubs.verify_stubbed_calls
|
stubs.verify_stubbed_calls
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sushi_exception
|
def test_httpbingo_exception
|
||||||
stubs = Faraday::Adapter::Test::Stubs.new
|
stubs = Faraday::Adapter::Test::Stubs.new
|
||||||
stubs.get('/ebi') do
|
stubs.get('/api') do
|
||||||
raise Faraday::ConnectionFailed, nil
|
raise Faraday::ConnectionFailed
|
||||||
end
|
end
|
||||||
|
|
||||||
cli = client(stubs)
|
cli = client(stubs)
|
||||||
assert_raise Faraday::ConnectionFailed do
|
assert_raise Faraday::ConnectionFailed do
|
||||||
cli.sushi('ebi')
|
cli.httpbingo('api')
|
||||||
end
|
end
|
||||||
stubs.verify_stubbed_calls
|
stubs.verify_stubbed_calls
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_strict_mode
|
||||||
|
stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true)
|
||||||
|
stubs.get('/api?abc=123') do
|
||||||
|
[
|
||||||
|
200,
|
||||||
|
{ 'Content-Type': 'application/javascript' },
|
||||||
|
'{"origin": "127.0.0.1"}'
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
cli = client(stubs)
|
||||||
|
assert_equal '127.0.0.1', cli.httpbingo('api', params: { abc: 123 })
|
||||||
|
|
||||||
|
# uncomment to raise Stubs::NotFound
|
||||||
|
# assert_equal '127.0.0.1', cli.httpbingo('api', params: { abc: 123, foo: 'Kappa' })
|
||||||
|
stubs.verify_stubbed_calls
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_non_default_params_encoder
|
||||||
|
stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true)
|
||||||
|
stubs.get('/api?a=x&a=y&a=z') do
|
||||||
|
[
|
||||||
|
200,
|
||||||
|
{ 'Content-Type': 'application/javascript' },
|
||||||
|
'{"origin": "127.0.0.1"}'
|
||||||
|
]
|
||||||
|
end
|
||||||
|
conn = Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) do |builder|
|
||||||
|
builder.adapter :test, stubs
|
||||||
|
end
|
||||||
|
|
||||||
|
cli = Client.new(conn)
|
||||||
|
assert_equal '127.0.0.1', cli.httpbingo('api', params: { a: %w[x y z] })
|
||||||
|
|
||||||
|
# uncomment to raise Stubs::NotFound
|
||||||
|
# assert_equal '127.0.0.1', cli.httpbingo('api', params: { a: %w[x y] })
|
||||||
|
stubs.verify_stubbed_calls
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_with_string_body
|
||||||
|
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
||||||
|
stub.post('/foo', '{"name":"YK"}') { [200, {}, ''] }
|
||||||
|
end
|
||||||
|
cli = client(stubs)
|
||||||
|
assert_equal 200, cli.foo(name: 'YK')
|
||||||
|
|
||||||
|
stubs.verify_stubbed_calls
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_with_proc_body
|
||||||
|
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
||||||
|
check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }
|
||||||
|
stub.post('/foo', check) { [200, {}, ''] }
|
||||||
|
end
|
||||||
|
cli = client(stubs)
|
||||||
|
assert_equal 200, cli.foo(name: 'YK', created_at: Time.now)
|
||||||
|
|
||||||
|
stubs.verify_stubbed_calls
|
||||||
|
end
|
||||||
|
|
||||||
def client(stubs)
|
def client(stubs)
|
||||||
conn = Faraday.new do |builder|
|
conn = Faraday.new do |builder|
|
||||||
builder.adapter :test, stubs
|
builder.adapter :test, stubs
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
require_relative 'lib/faraday/version'
|
require_relative 'lib/faraday/version'
|
||||||
|
|
||||||
Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
Gem::Specification.new do |spec|
|
||||||
spec.name = 'faraday'
|
spec.name = 'faraday'
|
||||||
spec.version = Faraday::VERSION
|
spec.version = Faraday::VERSION
|
||||||
|
|
||||||
@ -13,18 +13,17 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
|||||||
spec.homepage = 'https://lostisland.github.io/faraday'
|
spec.homepage = 'https://lostisland.github.io/faraday'
|
||||||
spec.licenses = ['MIT']
|
spec.licenses = ['MIT']
|
||||||
|
|
||||||
spec.required_ruby_version = '>= 2.4'
|
spec.required_ruby_version = '>= 3.0'
|
||||||
|
|
||||||
spec.add_dependency 'faraday-em_http', '~> 1.0'
|
# faraday-net_http is the "default adapter", but being a Faraday dependency it can't
|
||||||
spec.add_dependency 'faraday-em_synchrony', '~> 1.0'
|
# control which version of faraday it will be pulled from.
|
||||||
spec.add_dependency 'faraday-excon', '~> 1.1'
|
# To avoid releasing a major version every time there's a new Faraday API, we should
|
||||||
spec.add_dependency 'faraday-httpclient', '~> 1.0.1'
|
# always fix its required version to the next MINOR version.
|
||||||
spec.add_dependency 'faraday-net_http', '~> 1.0'
|
# This way, we can release minor versions of the adapter with "breaking" changes for older versions of Faraday
|
||||||
spec.add_dependency 'faraday-net_http_persistent', '~> 1.1'
|
# and then bump the version requirement on the next compatible version of faraday.
|
||||||
spec.add_dependency 'faraday-patron', '~> 1.0'
|
spec.add_dependency 'faraday-net_http', '>= 2.0', '< 3.5'
|
||||||
spec.add_dependency 'faraday-rack', '~> 1.0'
|
spec.add_dependency 'json'
|
||||||
spec.add_dependency 'multipart-post', '>= 1.2', '< 3'
|
spec.add_dependency 'logger'
|
||||||
spec.add_dependency 'ruby2_keywords', '>= 0.0.4'
|
|
||||||
|
|
||||||
# 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
|
||||||
spec.files = Dir['CHANGELOG.md', '{examples,lib,spec}/**/*', 'LICENSE.md', 'Rakefile', 'README.md']
|
spec.files = Dir['CHANGELOG.md', '{examples,lib,spec}/**/*', 'LICENSE.md', 'Rakefile', 'README.md']
|
||||||
@ -34,6 +33,7 @@ Gem::Specification.new do |spec| # rubocop:disable Metrics/BlockLength
|
|||||||
'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
|
||||||
|
@ -1,19 +1,14 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'cgi'
|
require 'cgi/escape'
|
||||||
|
require 'cgi/util' if RUBY_VERSION < '3.5'
|
||||||
require 'date'
|
require 'date'
|
||||||
require 'set'
|
require 'set'
|
||||||
require 'forwardable'
|
require 'forwardable'
|
||||||
require 'faraday/middleware_registry'
|
|
||||||
require 'faraday/dependency_loader'
|
|
||||||
|
|
||||||
unless defined?(::Faraday::Timer)
|
|
||||||
require 'timeout'
|
|
||||||
::Faraday::Timer = Timeout
|
|
||||||
end
|
|
||||||
|
|
||||||
require 'faraday/version'
|
require 'faraday/version'
|
||||||
require 'faraday/methods'
|
require 'faraday/methods'
|
||||||
|
require 'faraday/error'
|
||||||
|
require 'faraday/middleware_registry'
|
||||||
require 'faraday/utils'
|
require 'faraday/utils'
|
||||||
require 'faraday/options'
|
require 'faraday/options'
|
||||||
require 'faraday/connection'
|
require 'faraday/connection'
|
||||||
@ -23,21 +18,7 @@ require 'faraday/middleware'
|
|||||||
require 'faraday/adapter'
|
require 'faraday/adapter'
|
||||||
require 'faraday/request'
|
require 'faraday/request'
|
||||||
require 'faraday/response'
|
require 'faraday/response'
|
||||||
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'
|
||||||
require 'faraday/net_http_persistent'
|
|
||||||
require 'faraday/patron'
|
|
||||||
require 'faraday/rack'
|
|
||||||
|
|
||||||
# This is the main namespace for Faraday.
|
# This is the main namespace for Faraday.
|
||||||
#
|
#
|
||||||
# It provides methods to create {Connection} objects, and HTTP-related
|
# It provides methods to create {Connection} objects, and HTTP-related
|
||||||
@ -51,6 +32,8 @@ require 'faraday/rack'
|
|||||||
# conn.get '/'
|
# conn.get '/'
|
||||||
#
|
#
|
||||||
module Faraday
|
module Faraday
|
||||||
|
CONTENT_TYPE = 'Content-Type'
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
# The root path that Faraday is being loaded from.
|
# The root path that Faraday is being loaded from.
|
||||||
#
|
#
|
||||||
@ -72,6 +55,10 @@ module Faraday
|
|||||||
# @return [Symbol] the new default_adapter.
|
# @return [Symbol] the new default_adapter.
|
||||||
attr_reader :default_adapter
|
attr_reader :default_adapter
|
||||||
|
|
||||||
|
# Option for the default_adapter
|
||||||
|
# @return [Hash] default_adapter options
|
||||||
|
attr_accessor :default_adapter_options
|
||||||
|
|
||||||
# Documented below, see default_connection
|
# Documented below, see default_connection
|
||||||
attr_writer :default_connection
|
attr_writer :default_connection
|
||||||
|
|
||||||
@ -108,23 +95,10 @@ module Faraday
|
|||||||
# params: { page: 1 }
|
# params: { page: 1 }
|
||||||
# # => Faraday::Connection to http://faraday.com?page=1
|
# # => Faraday::Connection to http://faraday.com?page=1
|
||||||
def new(url = nil, options = {}, &block)
|
def new(url = nil, options = {}, &block)
|
||||||
options = default_connection_options.merge(options)
|
options = Utils.deep_merge(default_connection_options, options)
|
||||||
Faraday::Connection.new(url, options, &block)
|
Faraday::Connection.new(url, options, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
# @private
|
|
||||||
# Internal: Requires internal Faraday libraries.
|
|
||||||
#
|
|
||||||
# @param libs [Array] one or more relative String names to Faraday classes.
|
|
||||||
# @return [void]
|
|
||||||
def require_libs(*libs)
|
|
||||||
libs.each do |lib|
|
|
||||||
require "#{lib_path}/#{lib}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
alias require_lib require_libs
|
|
||||||
|
|
||||||
# Documented elsewhere, see default_adapter reader
|
# Documented elsewhere, see default_adapter reader
|
||||||
def default_adapter=(adapter)
|
def default_adapter=(adapter)
|
||||||
@default_connection = nil
|
@default_connection = nil
|
||||||
@ -180,6 +154,5 @@ module Faraday
|
|||||||
self.root_path = File.expand_path __dir__
|
self.root_path = File.expand_path __dir__
|
||||||
self.lib_path = File.expand_path 'faraday', __dir__
|
self.lib_path = File.expand_path 'faraday', __dir__
|
||||||
self.default_adapter = :net_http
|
self.default_adapter = :net_http
|
||||||
|
self.default_adapter_options = {}
|
||||||
require_lib 'autoload' unless ENV['FARADAY_NO_AUTOLOAD']
|
|
||||||
end
|
end
|
||||||
|
@ -5,14 +5,9 @@ module Faraday
|
|||||||
# responsible for fulfilling a Faraday request.
|
# responsible for fulfilling a Faraday request.
|
||||||
class Adapter
|
class Adapter
|
||||||
extend MiddlewareRegistry
|
extend MiddlewareRegistry
|
||||||
extend DependencyLoader
|
|
||||||
|
|
||||||
CONTENT_LENGTH = 'Content-Length'
|
CONTENT_LENGTH = 'Content-Length'
|
||||||
|
|
||||||
register_middleware File.expand_path('adapter', __dir__),
|
|
||||||
test: [:Test, 'test'],
|
|
||||||
typhoeus: [:Typhoeus, 'typhoeus']
|
|
||||||
|
|
||||||
# This module marks an Adapter as supporting parallel requests.
|
# This module marks an Adapter as supporting parallel requests.
|
||||||
module Parallelism
|
module Parallelism
|
||||||
attr_writer :supports_parallel
|
attr_writer :supports_parallel
|
||||||
@ -31,7 +26,7 @@ module Faraday
|
|||||||
self.supports_parallel = false
|
self.supports_parallel = false
|
||||||
|
|
||||||
def initialize(_app = nil, opts = {}, &block)
|
def initialize(_app = nil, opts = {}, &block)
|
||||||
@app = ->(env) { env.response }
|
@app = lambda(&:response)
|
||||||
@connection_options = opts
|
@connection_options = opts
|
||||||
@config_block = block
|
@config_block = block
|
||||||
end
|
end
|
||||||
@ -64,7 +59,7 @@ module Faraday
|
|||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def save_response(env, status, body, headers = nil, reason_phrase = nil)
|
def save_response(env, status, body, headers = nil, reason_phrase = nil, finished: true)
|
||||||
env.status = status
|
env.status = status
|
||||||
env.body = body
|
env.body = body
|
||||||
env.reason_phrase = reason_phrase&.to_s&.strip
|
env.reason_phrase = reason_phrase&.to_s&.strip
|
||||||
@ -73,7 +68,7 @@ module Faraday
|
|||||||
yield(response_headers) if block_given?
|
yield(response_headers) if block_given?
|
||||||
end
|
end
|
||||||
|
|
||||||
env.response.finish(env) unless env.parallel?
|
env.response.finish(env) unless env.parallel? || !finished
|
||||||
env.response
|
env.response
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -83,8 +78,7 @@ module Faraday
|
|||||||
# @param type [Symbol] Describes which timeout setting to get: :read,
|
# @param type [Symbol] Describes which timeout setting to get: :read,
|
||||||
# :write, or :open.
|
# :write, or :open.
|
||||||
# @param options [Hash] Hash containing Symbol keys like :timeout,
|
# @param options [Hash] Hash containing Symbol keys like :timeout,
|
||||||
# :read_timeout, :write_timeout, :open_timeout, or
|
# :read_timeout, :write_timeout, or :open_timeout
|
||||||
# :timeout
|
|
||||||
#
|
#
|
||||||
# @return [Integer, nil] Timeout duration in seconds, or nil if no timeout
|
# @return [Integer, nil] Timeout duration in seconds, or nil if no timeout
|
||||||
# has been set.
|
# has been set.
|
||||||
@ -103,3 +97,5 @@ module Faraday
|
|||||||
}.freeze
|
}.freeze
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
require 'faraday/adapter/test'
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'timeout'
|
||||||
|
|
||||||
module Faraday
|
module Faraday
|
||||||
class Adapter
|
class Adapter
|
||||||
# @example
|
# @example
|
||||||
@ -26,6 +28,15 @@ module Faraday
|
|||||||
# ]
|
# ]
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
|
# # Test the request body is the same as the stubbed body
|
||||||
|
# stub.post('/bar', 'name=YK&word=call') { [200, {}, ''] }
|
||||||
|
#
|
||||||
|
# # You can pass a proc as a stubbed body and check the request body in your way.
|
||||||
|
# # In this case, the proc should return true or false.
|
||||||
|
# stub.post('/foo', ->(request_body) do
|
||||||
|
# JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }) { [200, {}, '']
|
||||||
|
# end
|
||||||
|
#
|
||||||
# # You can set strict_mode to exactly match the stubbed requests.
|
# # You can set strict_mode to exactly match the stubbed requests.
|
||||||
# stub.strict_mode = true
|
# stub.strict_mode = true
|
||||||
# end
|
# end
|
||||||
@ -42,6 +53,12 @@ module Faraday
|
|||||||
#
|
#
|
||||||
# resp = test.get '/items/2'
|
# resp = test.get '/items/2'
|
||||||
# resp.body # => 'showing item: 2'
|
# resp.body # => 'showing item: 2'
|
||||||
|
#
|
||||||
|
# resp = test.post '/bar', 'name=YK&word=call'
|
||||||
|
# resp.status # => 200
|
||||||
|
#
|
||||||
|
# resp = test.post '/foo', JSON.dump(name: 'YK', created_at: Time.now)
|
||||||
|
# resp.status # => 200
|
||||||
class Test < Faraday::Adapter
|
class Test < Faraday::Adapter
|
||||||
attr_accessor :stubs
|
attr_accessor :stubs
|
||||||
|
|
||||||
@ -55,6 +72,7 @@ module Faraday
|
|||||||
@stack = {}
|
@stack = {}
|
||||||
@consumed = {}
|
@consumed = {}
|
||||||
@strict_mode = strict_mode
|
@strict_mode = strict_mode
|
||||||
|
@stubs_mutex = Monitor.new
|
||||||
yield(self) if block_given?
|
yield(self) if block_given?
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -62,18 +80,23 @@ module Faraday
|
|||||||
@stack.empty?
|
@stack.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def match(request_method, host, path, headers, body)
|
# @param env [Faraday::Env]
|
||||||
|
def match(env)
|
||||||
|
request_method = env[:method]
|
||||||
return false unless @stack.key?(request_method)
|
return false unless @stack.key?(request_method)
|
||||||
|
|
||||||
stack = @stack[request_method]
|
stack = @stack[request_method]
|
||||||
consumed = (@consumed[request_method] ||= [])
|
consumed = (@consumed[request_method] ||= [])
|
||||||
|
|
||||||
stub, meta = matches?(stack, host, path, headers, body)
|
@stubs_mutex.synchronize do
|
||||||
if stub
|
stub, meta = matches?(stack, env)
|
||||||
consumed << stack.delete(stub)
|
if stub
|
||||||
return stub, meta
|
removed = stack.delete(stub)
|
||||||
|
consumed << removed unless removed.nil?
|
||||||
|
return stub, meta
|
||||||
|
end
|
||||||
end
|
end
|
||||||
matches?(consumed, host, path, headers, body)
|
matches?(consumed, env)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(path, headers = {}, &block)
|
def get(path, headers = {}, &block)
|
||||||
@ -123,7 +146,7 @@ module Faraday
|
|||||||
# which means that all of a path, parameters, and headers must be the same as an actual request.
|
# which means that all of a path, parameters, and headers must be the same as an actual request.
|
||||||
def strict_mode=(value)
|
def strict_mode=(value)
|
||||||
@strict_mode = value
|
@strict_mode = value
|
||||||
@stack.each do |_method, stubs|
|
@stack.each_value do |stubs|
|
||||||
stubs.each do |stub|
|
stubs.each do |stub|
|
||||||
stub.strict_mode = value
|
stub.strict_mode = value
|
||||||
end
|
end
|
||||||
@ -142,15 +165,18 @@ module Faraday
|
|||||||
Faraday::Utils.URI(path).host
|
Faraday::Utils.URI(path).host
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
path, query = normalized_path.respond_to?(:split) ? normalized_path.split('?') : normalized_path
|
||||||
headers = Utils::Headers.new(headers)
|
headers = Utils::Headers.new(headers)
|
||||||
stub = Stub.new(host, normalized_path, headers, body, @strict_mode, block)
|
|
||||||
|
stub = Stub.new(host, path, query, headers, body, @strict_mode, block)
|
||||||
(@stack[request_method] ||= []) << stub
|
(@stack[request_method] ||= []) << stub
|
||||||
end
|
end
|
||||||
|
|
||||||
def matches?(stack, host, path, headers, body)
|
# @param stack [Hash]
|
||||||
|
# @param env [Faraday::Env]
|
||||||
|
def matches?(stack, env)
|
||||||
stack.each do |stub|
|
stack.each do |stub|
|
||||||
match_result, meta = stub.matches?(host, path, headers, body)
|
match_result, meta = stub.matches?(env)
|
||||||
return stub, meta if match_result
|
return stub, meta if match_result
|
||||||
end
|
end
|
||||||
nil
|
nil
|
||||||
@ -158,36 +184,21 @@ module Faraday
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Stub request
|
# Stub request
|
||||||
# rubocop:disable Style/StructInheritance
|
Stub = Struct.new(:host, :path, :query, :headers, :body, :strict_mode, :block) do
|
||||||
class Stub < Struct.new(:host, :path, :params, :headers, :body, :strict_mode, :block)
|
# @param env [Faraday::Env]
|
||||||
# rubocop:enable Style/StructInheritance
|
def matches?(env)
|
||||||
def initialize(host, full, headers, body, strict_mode, block) # rubocop:disable Metrics/ParameterLists
|
request_host = env[:url].host
|
||||||
path, query = full.respond_to?(:split) ? full.split('?') : full
|
request_path = Faraday::Utils.normalize_path(env[:url].path)
|
||||||
params =
|
request_headers = env.request_headers
|
||||||
if query
|
request_body = env[:body]
|
||||||
Faraday::Utils.parse_nested_query(query)
|
|
||||||
else
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
|
|
||||||
super(host, path, params, headers, body, strict_mode, block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def matches?(request_host, request_uri, request_headers, request_body)
|
|
||||||
request_path, request_query = request_uri.split('?')
|
|
||||||
request_params =
|
|
||||||
if request_query
|
|
||||||
Faraday::Utils.parse_nested_query(request_query)
|
|
||||||
else
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
# meta is a hash used as carrier
|
# meta is a hash used as carrier
|
||||||
# that will be yielded to consumer block
|
# that will be yielded to consumer block
|
||||||
meta = {}
|
meta = {}
|
||||||
[(host.nil? || host == request_host) &&
|
[(host.nil? || host == request_host) &&
|
||||||
path_match?(request_path, meta) &&
|
path_match?(request_path, meta) &&
|
||||||
params_match?(request_params) &&
|
params_match?(env) &&
|
||||||
(body.to_s.size.zero? || request_body == body) &&
|
body_match?(request_body) &&
|
||||||
headers_match?(request_headers), meta]
|
headers_match?(request_headers), meta]
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -199,7 +210,11 @@ module Faraday
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def params_match?(request_params)
|
# @param env [Faraday::Env]
|
||||||
|
def params_match?(env)
|
||||||
|
request_params = env[:params]
|
||||||
|
params = env.params_encoder.decode(query) || {}
|
||||||
|
|
||||||
if strict_mode
|
if strict_mode
|
||||||
return Set.new(params) == Set.new(request_params)
|
return Set.new(params) == Set.new(request_params)
|
||||||
end
|
end
|
||||||
@ -224,6 +239,17 @@ module Faraday
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def body_match?(request_body)
|
||||||
|
return true if body.to_s.empty?
|
||||||
|
|
||||||
|
case body
|
||||||
|
when Proc
|
||||||
|
body.call(request_body)
|
||||||
|
else
|
||||||
|
request_body == body
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
"#{path} #{body}"
|
"#{path} #{body}"
|
||||||
end
|
end
|
||||||
@ -239,37 +265,47 @@ module Faraday
|
|||||||
yield(stubs)
|
yield(stubs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @param env [Faraday::Env]
|
||||||
def call(env)
|
def call(env)
|
||||||
super
|
super
|
||||||
host = env[:url].host
|
|
||||||
normalized_path = Faraday::Utils.normalize_path(env[:url])
|
|
||||||
params_encoder = env.request.params_encoder ||
|
|
||||||
Faraday::Utils.default_params_encoder
|
|
||||||
|
|
||||||
stub, meta = stubs.match(env[:method], host, normalized_path,
|
env.request.params_encoder ||= Faraday::Utils.default_params_encoder
|
||||||
env.request_headers, env[:body])
|
env[:params] = env.params_encoder.decode(env[:url].query) || {}
|
||||||
|
stub, meta = stubs.match(env)
|
||||||
|
|
||||||
unless stub
|
unless stub
|
||||||
raise Stubs::NotFound, "no stubbed request for #{env[:method]} "\
|
raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \
|
||||||
"#{normalized_path} #{env[:body]}"
|
"#{env[:url]} #{env[:body]} #{env[:headers]}"
|
||||||
end
|
end
|
||||||
|
|
||||||
env[:params] = if (query = env[:url].query)
|
|
||||||
params_encoder.decode(query)
|
|
||||||
else
|
|
||||||
{}
|
|
||||||
end
|
|
||||||
block_arity = stub.block.arity
|
block_arity = stub.block.arity
|
||||||
|
params = if block_arity >= 0
|
||||||
|
[env, meta].take(block_arity)
|
||||||
|
else
|
||||||
|
[env, meta]
|
||||||
|
end
|
||||||
|
|
||||||
|
timeout = request_timeout(:open, env[:request])
|
||||||
|
timeout ||= request_timeout(:read, env[:request])
|
||||||
|
|
||||||
status, headers, body =
|
status, headers, body =
|
||||||
if block_arity >= 0
|
if timeout
|
||||||
stub.block.call(*[env, meta].take(block_arity))
|
::Timeout.timeout(timeout, Faraday::TimeoutError) do
|
||||||
|
stub.block.call(*params)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
stub.block.call(env, meta)
|
stub.block.call(*params)
|
||||||
end
|
end
|
||||||
save_response(env, status, body, headers)
|
|
||||||
|
# We need to explicitly pass `reason_phrase = nil` here to avoid keyword args conflicts.
|
||||||
|
# See https://github.com/lostisland/faraday/issues/1444
|
||||||
|
# TODO: remove `nil` explicit reason_phrase once Ruby 3.0 becomes minimum req. version
|
||||||
|
save_response(env, status, body, headers, nil)
|
||||||
|
|
||||||
@app.call(env)
|
@app.call(env)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Faraday::Adapter.register_middleware(test: Faraday::Adapter::Test)
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Faraday
|
|
||||||
class Adapter
|
|
||||||
# Typhoeus adapter. This class is just a stub, the real adapter is in
|
|
||||||
# https://github.com/typhoeus/typhoeus/blob/master/lib/typhoeus/adapters/faraday.rb
|
|
||||||
class Typhoeus < Faraday::Adapter
|
|
||||||
# Needs to define this method in order to support Typhoeus <= 1.3.0
|
|
||||||
def call; end
|
|
||||||
|
|
||||||
dependency 'typhoeus'
|
|
||||||
dependency 'typhoeus/adapters/faraday'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1,87 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Faraday
|
|
||||||
# Adds the ability for other modules to manage autoloadable
|
|
||||||
# constants.
|
|
||||||
#
|
|
||||||
# @api private
|
|
||||||
module AutoloadHelper
|
|
||||||
# Registers the constants to be auto loaded.
|
|
||||||
#
|
|
||||||
# @param prefix [String] The require prefix. If the path is inside Faraday,
|
|
||||||
# then it will be prefixed with the root path of this loaded
|
|
||||||
# Faraday version.
|
|
||||||
# @param options [{ Symbol => String }] library names.
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
#
|
|
||||||
# Faraday.autoload_all 'faraday/foo',
|
|
||||||
# Bar: 'bar'
|
|
||||||
#
|
|
||||||
# # requires faraday/foo/bar to load Faraday::Bar.
|
|
||||||
# Faraday::Bar
|
|
||||||
#
|
|
||||||
# @return [void]
|
|
||||||
def autoload_all(prefix, options)
|
|
||||||
if prefix.match? %r{^faraday(/|$)}i
|
|
||||||
prefix = File.join(Faraday.root_path, prefix)
|
|
||||||
end
|
|
||||||
|
|
||||||
options.each do |const_name, path|
|
|
||||||
autoload const_name, File.join(prefix, path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Loads each autoloaded constant. If thread safety is a concern,
|
|
||||||
# wrap this in a Mutex.
|
|
||||||
#
|
|
||||||
# @return [void]
|
|
||||||
def load_autoloaded_constants
|
|
||||||
constants.each do |const|
|
|
||||||
const_get(const) if autoload?(const)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Filters the module's contents with those that have been already
|
|
||||||
# autoloaded.
|
|
||||||
#
|
|
||||||
# @return [Array<Class, Module>]
|
|
||||||
def all_loaded_constants
|
|
||||||
constants
|
|
||||||
.map { |c| const_get(c) }
|
|
||||||
.select { |a| a.respond_to?(:loaded?) && a.loaded? }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Adapter is the base class for all Faraday adapters.
|
|
||||||
# @see lib/faraday/adapter.rb Original class location
|
|
||||||
class Adapter
|
|
||||||
extend AutoloadHelper
|
|
||||||
autoload_all 'faraday/adapter',
|
|
||||||
Typhoeus: 'typhoeus',
|
|
||||||
Test: 'test'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Request represents a single HTTP request for a Faraday adapter to make.
|
|
||||||
# @see lib/faraday/request.rb Original class location
|
|
||||||
class Request
|
|
||||||
extend AutoloadHelper
|
|
||||||
autoload_all 'faraday/request',
|
|
||||||
UrlEncoded: 'url_encoded',
|
|
||||||
Multipart: 'multipart',
|
|
||||||
Retry: 'retry',
|
|
||||||
Authorization: 'authorization',
|
|
||||||
BasicAuthentication: 'basic_authentication',
|
|
||||||
TokenAuthentication: 'token_authentication',
|
|
||||||
Instrumentation: 'instrumentation'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Response represents the returned value of a sent Faraday request.
|
|
||||||
# @see lib/faraday/response.rb Original class location
|
|
||||||
class Response
|
|
||||||
extend AutoloadHelper
|
|
||||||
autoload_all 'faraday/response',
|
|
||||||
RaiseError: 'raise_error',
|
|
||||||
Logger: 'logger'
|
|
||||||
end
|
|
||||||
end
|
|
@ -6,16 +6,16 @@ module Faraday
|
|||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
#
|
#
|
||||||
# conn = Faraday::Connection.new 'http://sushi.com'
|
# conn = Faraday::Connection.new 'http://httpbingo.org'
|
||||||
#
|
#
|
||||||
# # GET http://sushi.com/nigiri
|
# # GET http://httpbingo.org/nigiri
|
||||||
# conn.get 'nigiri'
|
# conn.get 'nigiri'
|
||||||
# # => #<Faraday::Response>
|
# # => #<Faraday::Response>
|
||||||
#
|
#
|
||||||
class Connection
|
class Connection
|
||||||
# A Set of allowed HTTP verbs.
|
# A Set of allowed HTTP verbs.
|
||||||
METHODS = Set.new %i[get post put delete head patch options trace]
|
METHODS = Set.new %i[get post put delete head patch options trace]
|
||||||
USER_AGENT = "Faraday v#{VERSION}"
|
USER_AGENT = "Faraday v#{VERSION}".freeze
|
||||||
|
|
||||||
# @return [Hash] URI query unencoded key/value pairs.
|
# @return [Hash] URI query unencoded key/value pairs.
|
||||||
attr_reader :params
|
attr_reader :params
|
||||||
@ -64,7 +64,7 @@ module Faraday
|
|||||||
options = ConnectionOptions.from(options)
|
options = ConnectionOptions.from(options)
|
||||||
|
|
||||||
if url.is_a?(Hash) || url.is_a?(ConnectionOptions)
|
if url.is_a?(Hash) || url.is_a?(ConnectionOptions)
|
||||||
options = options.merge(url)
|
options = Utils.deep_merge(options, url)
|
||||||
url = options.url
|
url = options.url
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -117,7 +117,7 @@ module Faraday
|
|||||||
|
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
|
|
||||||
def_delegators :builder, :build, :use, :request, :response, :adapter, :app
|
def_delegators :builder, :use, :request, :response, :adapter, :app
|
||||||
|
|
||||||
# Closes the underlying resources and/or connections. In the case of
|
# Closes the underlying resources and/or connections. In the case of
|
||||||
# persistent connections, this closes all currently open connections
|
# persistent connections, this closes all currently open connections
|
||||||
@ -130,10 +130,10 @@ module Faraday
|
|||||||
# Makes a GET HTTP request without a body.
|
# Makes a GET HTTP request without a body.
|
||||||
# @!scope class
|
# @!scope class
|
||||||
#
|
#
|
||||||
# @param url [String] The optional String base URL to use as a prefix for
|
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||||
# all requests. Can also be the options Hash.
|
# all requests. Can also be the options Hash.
|
||||||
# @param params [Hash] Hash of URI query unencoded key/value pairs.
|
# @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
|
||||||
# @param headers [Hash] unencoded HTTP header key/value pairs.
|
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
# conn.get '/items', { page: 1 }, :accept => 'application/json'
|
# conn.get '/items', { page: 1 }, :accept => 'application/json'
|
||||||
@ -152,10 +152,10 @@ module Faraday
|
|||||||
# Makes a HEAD HTTP request without a body.
|
# Makes a HEAD HTTP request without a body.
|
||||||
# @!scope class
|
# @!scope class
|
||||||
#
|
#
|
||||||
# @param url [String] The optional String base URL to use as a prefix for
|
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||||
# all requests. Can also be the options Hash.
|
# all requests. Can also be the options Hash.
|
||||||
# @param params [Hash] Hash of URI query unencoded key/value pairs.
|
# @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
|
||||||
# @param headers [Hash] unencoded HTTP header key/value pairs.
|
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
# conn.head '/items/1'
|
# conn.head '/items/1'
|
||||||
@ -167,10 +167,10 @@ module Faraday
|
|||||||
# Makes a DELETE HTTP request without a body.
|
# Makes a DELETE HTTP request without a body.
|
||||||
# @!scope class
|
# @!scope class
|
||||||
#
|
#
|
||||||
# @param url [String] The optional String base URL to use as a prefix for
|
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||||
# all requests. Can also be the options Hash.
|
# all requests. Can also be the options Hash.
|
||||||
# @param params [Hash] Hash of URI query unencoded key/value pairs.
|
# @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
|
||||||
# @param headers [Hash] unencoded HTTP header key/value pairs.
|
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
# conn.delete '/items/1'
|
# conn.delete '/items/1'
|
||||||
@ -182,10 +182,10 @@ module Faraday
|
|||||||
# Makes a TRACE HTTP request without a body.
|
# Makes a TRACE HTTP request without a body.
|
||||||
# @!scope class
|
# @!scope class
|
||||||
#
|
#
|
||||||
# @param url [String] The optional String base URL to use as a prefix for
|
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||||
# all requests. Can also be the options Hash.
|
# all requests. Can also be the options Hash.
|
||||||
# @param params [Hash] Hash of URI query unencoded key/value pairs.
|
# @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
|
||||||
# @param headers [Hash] unencoded HTTP header key/value pairs.
|
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
# conn.connect '/items/1'
|
# conn.connect '/items/1'
|
||||||
@ -210,9 +210,9 @@ module Faraday
|
|||||||
#
|
#
|
||||||
# @overload options(url, params = nil, headers = nil)
|
# @overload options(url, params = nil, headers = nil)
|
||||||
# Makes an OPTIONS HTTP request to the given URL.
|
# Makes an OPTIONS HTTP request to the given URL.
|
||||||
# @param url [String] String base URL to sue as a prefix for all requests.
|
# @param url [String, URI, nil] String base URL to sue as a prefix for all requests.
|
||||||
# @param params [Hash] Hash of URI query unencoded key/value pairs.
|
# @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
|
||||||
# @param headers [Hash] unencoded HTTP header key/value pairs.
|
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
# conn.options '/items/1'
|
# conn.options '/items/1'
|
||||||
@ -220,7 +220,7 @@ module Faraday
|
|||||||
# @yield [Faraday::Request] for further request customizations
|
# @yield [Faraday::Request] for further request customizations
|
||||||
# @return [Faraday::Response]
|
# @return [Faraday::Response]
|
||||||
def options(*args)
|
def options(*args)
|
||||||
return @options if args.size.zero?
|
return @options if args.empty?
|
||||||
|
|
||||||
url, params, headers = *args
|
url, params, headers = *args
|
||||||
run_request(:options, url, nil, headers) do |request|
|
run_request(:options, url, nil, headers) do |request|
|
||||||
@ -233,10 +233,10 @@ module Faraday
|
|||||||
# Makes a POST HTTP request with a body.
|
# Makes a POST HTTP request with a body.
|
||||||
# @!scope class
|
# @!scope class
|
||||||
#
|
#
|
||||||
# @param url [String] The optional String base URL to use as a prefix for
|
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||||
# all requests. Can also be the options Hash.
|
# all requests. Can also be the options Hash.
|
||||||
# @param body [String] body for the request.
|
# @param body [String, nil] body for the request.
|
||||||
# @param headers [Hash] unencoded HTTP header key/value pairs.
|
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
# conn.post '/items', data, content_type: 'application/json'
|
# conn.post '/items', data, content_type: 'application/json'
|
||||||
@ -255,20 +255,19 @@ module Faraday
|
|||||||
# Makes a PUT HTTP request with a body.
|
# Makes a PUT HTTP request with a body.
|
||||||
# @!scope class
|
# @!scope class
|
||||||
#
|
#
|
||||||
# @param url [String] The optional String base URL to use as a prefix for
|
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||||
# all requests. Can also be the options Hash.
|
# all requests. Can also be the options Hash.
|
||||||
# @param body [String] body for the request.
|
# @param body [String, nil] body for the request.
|
||||||
# @param headers [Hash] unencoded HTTP header key/value pairs.
|
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
# # TODO: Make it a PUT example
|
# conn.put '/products/123', data, content_type: 'application/json'
|
||||||
# conn.post '/items', data, content_type: 'application/json'
|
|
||||||
#
|
#
|
||||||
# # Simple ElasticSearch indexing sample.
|
# # Star a gist.
|
||||||
# conn.post '/twitter/tweet' do |req|
|
# conn.put 'https://api.github.com/gists/GIST_ID/star' do |req|
|
||||||
# req.headers[:content_type] = 'application/json'
|
# req.headers['Accept'] = 'application/vnd.github+json'
|
||||||
# req.params[:routing] = 'kimchy'
|
# req.headers['Authorization'] = 'Bearer <YOUR-TOKEN>'
|
||||||
# req.body = JSON.generate(user: 'kimchy', ...)
|
# req.headers['X-GitHub-Api-Version'] = '2022-11-28'
|
||||||
# end
|
# end
|
||||||
#
|
#
|
||||||
# @yield [Faraday::Request] for further request customizations
|
# @yield [Faraday::Request] for further request customizations
|
||||||
@ -283,77 +282,6 @@ module Faraday
|
|||||||
RUBY
|
RUBY
|
||||||
end
|
end
|
||||||
|
|
||||||
# Sets up the Authorization header with these credentials, encoded
|
|
||||||
# with base64.
|
|
||||||
#
|
|
||||||
# @param login [String] The authentication login.
|
|
||||||
# @param pass [String] The authentication password.
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
#
|
|
||||||
# conn.basic_auth 'Aladdin', 'open sesame'
|
|
||||||
# conn.headers['Authorization']
|
|
||||||
# # => "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ=="
|
|
||||||
#
|
|
||||||
# @return [void]
|
|
||||||
def basic_auth(login, pass)
|
|
||||||
warn <<~TEXT
|
|
||||||
WARNING: `Faraday::Connection#basic_auth` is deprecated; it will be removed in version 2.0.
|
|
||||||
While initializing your connection, use `#request(:basic_auth, ...)` instead.
|
|
||||||
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
|
|
||||||
TEXT
|
|
||||||
set_authorization_header(:basic_auth, login, pass)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sets up the Authorization header with the given token.
|
|
||||||
#
|
|
||||||
# @param token [String]
|
|
||||||
# @param options [Hash] extra token options.
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
#
|
|
||||||
# conn.token_auth 'abcdef', foo: 'bar'
|
|
||||||
# conn.headers['Authorization']
|
|
||||||
# # => "Token token=\"abcdef\",
|
|
||||||
# foo=\"bar\""
|
|
||||||
#
|
|
||||||
# @return [void]
|
|
||||||
def token_auth(token, options = nil)
|
|
||||||
warn <<~TEXT
|
|
||||||
WARNING: `Faraday::Connection#token_auth` is deprecated; it will be removed in version 2.0.
|
|
||||||
While initializing your connection, use `#request(:token_auth, ...)` instead.
|
|
||||||
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
|
|
||||||
TEXT
|
|
||||||
set_authorization_header(:token_auth, token, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Sets up a custom Authorization header.
|
|
||||||
#
|
|
||||||
# @param type [String] authorization type
|
|
||||||
# @param token [String, Hash] token. A String value is taken literally, and
|
|
||||||
# a Hash is encoded into comma-separated key/value pairs.
|
|
||||||
#
|
|
||||||
# @example
|
|
||||||
#
|
|
||||||
# conn.authorization :Bearer, 'mF_9.B5f-4.1JqM'
|
|
||||||
# conn.headers['Authorization']
|
|
||||||
# # => "Bearer mF_9.B5f-4.1JqM"
|
|
||||||
#
|
|
||||||
# conn.authorization :Token, token: 'abcdef', foo: 'bar'
|
|
||||||
# conn.headers['Authorization']
|
|
||||||
# # => "Token token=\"abcdef\",
|
|
||||||
# foo=\"bar\""
|
|
||||||
#
|
|
||||||
# @return [void]
|
|
||||||
def authorization(type, token)
|
|
||||||
warn <<~TEXT
|
|
||||||
WARNING: `Faraday::Connection#authorization` is deprecated; it will be removed in version 2.0.
|
|
||||||
While initializing your connection, use `#request(:authorization, ...)` instead.
|
|
||||||
See https://lostisland.github.io/faraday/middleware/authentication for more usage info.
|
|
||||||
TEXT
|
|
||||||
set_authorization_header(:authorization, type, token)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check if the adapter is parallel-capable.
|
# Check if the adapter is parallel-capable.
|
||||||
#
|
#
|
||||||
# @yield if the adapter isn't parallel-capable, or if no adapter is set yet.
|
# @yield if the adapter isn't parallel-capable, or if no adapter is set yet.
|
||||||
@ -386,15 +314,23 @@ 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)
|
def in_parallel(manager = nil, &block)
|
||||||
@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
|
||||||
yield
|
return yield unless @parallel_manager
|
||||||
@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
|
||||||
@ -420,11 +356,11 @@ module Faraday
|
|||||||
# @example
|
# @example
|
||||||
#
|
#
|
||||||
# conn = Faraday::Connection.new { ... }
|
# conn = Faraday::Connection.new { ... }
|
||||||
# conn.url_prefix = "https://sushi.com/api"
|
# conn.url_prefix = "https://httpbingo.org/api"
|
||||||
# conn.scheme # => https
|
# conn.scheme # => https
|
||||||
# conn.path_prefix # => "/api"
|
# conn.path_prefix # => "/api"
|
||||||
#
|
#
|
||||||
# conn.get("nigiri?page=2") # accesses https://sushi.com/api/nigiri
|
# conn.get("nigiri?page=2") # accesses https://httpbingo.org/api/nigiri
|
||||||
def url_prefix=(url, encoder = nil)
|
def url_prefix=(url, encoder = nil)
|
||||||
uri = @url_prefix = Utils.URI(url)
|
uri = @url_prefix = Utils.URI(url)
|
||||||
self.path_prefix = uri.path
|
self.path_prefix = uri.path
|
||||||
@ -433,13 +369,18 @@ module Faraday
|
|||||||
uri.query = nil
|
uri.query = nil
|
||||||
|
|
||||||
with_uri_credentials(uri) do |user, password|
|
with_uri_credentials(uri) do |user, password|
|
||||||
basic_auth user, password
|
set_basic_auth(user, password)
|
||||||
uri.user = uri.password = nil
|
uri.user = uri.password = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
@proxy = proxy_from_env(url) unless @manual_proxy
|
@proxy = proxy_from_env(url) unless @manual_proxy
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_basic_auth(user, password)
|
||||||
|
header = Faraday::Utils.basic_header_from(user, password)
|
||||||
|
headers[Faraday::Request::Authorization::KEY] = header
|
||||||
|
end
|
||||||
|
|
||||||
# Sets the path prefix and ensures that it always has a leading
|
# Sets the path prefix and ensures that it always has a leading
|
||||||
# slash.
|
# slash.
|
||||||
#
|
#
|
||||||
@ -456,20 +397,20 @@ module Faraday
|
|||||||
# Takes a relative url for a request and combines it with the defaults
|
# Takes a relative url for a request and combines it with the defaults
|
||||||
# set on the connection instance.
|
# set on the connection instance.
|
||||||
#
|
#
|
||||||
# @param url [String]
|
# @param url [String, URI, nil]
|
||||||
# @param extra_params [Hash]
|
# @param extra_params [Hash]
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
# conn = Faraday::Connection.new { ... }
|
# conn = Faraday::Connection.new { ... }
|
||||||
# conn.url_prefix = "https://sushi.com/api?token=abc"
|
# conn.url_prefix = "https://httpbingo.org/api?token=abc"
|
||||||
# conn.scheme # => https
|
# conn.scheme # => https
|
||||||
# conn.path_prefix # => "/api"
|
# conn.path_prefix # => "/api"
|
||||||
#
|
#
|
||||||
# conn.build_url("nigiri?page=2")
|
# conn.build_url("nigiri?page=2")
|
||||||
# # => https://sushi.com/api/nigiri?token=abc&page=2
|
# # => https://httpbingo.org/api/nigiri?token=abc&page=2
|
||||||
#
|
#
|
||||||
# conn.build_url("nigiri", page: 2)
|
# conn.build_url("nigiri", page: 2)
|
||||||
# # => https://sushi.com/api/nigiri?token=abc&page=2
|
# # => https://httpbingo.org/api/nigiri?token=abc&page=2
|
||||||
#
|
#
|
||||||
def build_url(url = nil, extra_params = nil)
|
def build_url(url = nil, extra_params = nil)
|
||||||
uri = build_exclusive_url(url)
|
uri = build_exclusive_url(url)
|
||||||
@ -489,10 +430,10 @@ module Faraday
|
|||||||
# Builds and runs the Faraday::Request.
|
# Builds and runs the Faraday::Request.
|
||||||
#
|
#
|
||||||
# @param method [Symbol] HTTP method.
|
# @param method [Symbol] HTTP method.
|
||||||
# @param url [String, URI] String or URI to access.
|
# @param url [String, URI, nil] String or URI to access.
|
||||||
# @param body [Object] The request body that will eventually be converted to
|
# @param body [String, Hash, Array, nil] The request body that will eventually be converted to
|
||||||
# a string.
|
# a string; middlewares can be used to support more complex types.
|
||||||
# @param headers [Hash] unencoded HTTP header key/value pairs.
|
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||||
#
|
#
|
||||||
# @return [Faraday::Response]
|
# @return [Faraday::Response]
|
||||||
def run_request(method, url, body, headers)
|
def run_request(method, url, body, headers)
|
||||||
@ -528,7 +469,7 @@ module Faraday
|
|||||||
|
|
||||||
# Build an absolute URL based on url_prefix.
|
# Build an absolute URL based on url_prefix.
|
||||||
#
|
#
|
||||||
# @param url [String, URI]
|
# @param url [String, URI, nil]
|
||||||
# @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to
|
# @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to
|
||||||
# replace the query values
|
# replace the query values
|
||||||
# of the resulting url (default: nil).
|
# of the resulting url (default: nil).
|
||||||
@ -537,17 +478,16 @@ module Faraday
|
|||||||
def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
|
def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
|
||||||
url = nil if url.respond_to?(:empty?) && url.empty?
|
url = nil if url.respond_to?(:empty?) && url.empty?
|
||||||
base = url_prefix.dup
|
base = url_prefix.dup
|
||||||
if url && base.path && base.path !~ %r{/$}
|
if url && !base.path.end_with?('/')
|
||||||
base.path = "#{base.path}/" # ensure trailing slash
|
base.path = "#{base.path}/" # ensure trailing slash
|
||||||
end
|
end
|
||||||
url = url && URI.parse(url.to_s).opaque ? url.to_s.gsub(':', '%3A') : url
|
# Ensure relative url will be parsed correctly (such as `service:search` )
|
||||||
|
url = "./#{url}" if url.respond_to?(:start_with?) && !url.start_with?('http://', 'https://', '/', './', '../')
|
||||||
uri = url ? base + url : base
|
uri = url ? base + url : base
|
||||||
if params
|
if params
|
||||||
uri.query = params.to_query(params_encoder || options.params_encoder)
|
uri.query = params.to_query(params_encoder || options.params_encoder)
|
||||||
end
|
end
|
||||||
# rubocop:disable Style/SafeNavigation
|
|
||||||
uri.query = nil if uri.query && uri.query.empty?
|
uri.query = nil if uri.query && uri.query.empty?
|
||||||
# rubocop:enable Style/SafeNavigation
|
|
||||||
uri
|
uri
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -579,41 +519,28 @@ module Faraday
|
|||||||
yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
|
yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_authorization_header(header_type, *args)
|
|
||||||
header = Faraday::Request
|
|
||||||
.lookup_middleware(header_type)
|
|
||||||
.header(*args)
|
|
||||||
|
|
||||||
headers[Faraday::Request::Authorization::KEY] = header
|
|
||||||
end
|
|
||||||
|
|
||||||
def proxy_from_env(url)
|
def proxy_from_env(url)
|
||||||
return if Faraday.ignore_env_proxy
|
return if Faraday.ignore_env_proxy
|
||||||
|
|
||||||
uri = nil
|
uri = nil
|
||||||
if URI.parse('').respond_to?(:find_proxy)
|
case url
|
||||||
case url
|
when String
|
||||||
when String
|
uri = Utils.URI(url)
|
||||||
uri = Utils.URI(url)
|
uri = if uri.host.nil?
|
||||||
uri = if uri.host.nil?
|
find_default_proxy
|
||||||
find_default_proxy
|
else
|
||||||
else
|
URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
|
||||||
URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
|
end
|
||||||
end
|
when URI
|
||||||
when URI
|
uri = url.find_proxy
|
||||||
uri = url.find_proxy
|
when nil
|
||||||
when nil
|
|
||||||
uri = find_default_proxy
|
|
||||||
end
|
|
||||||
else
|
|
||||||
warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY']
|
|
||||||
uri = find_default_proxy
|
uri = find_default_proxy
|
||||||
end
|
end
|
||||||
ProxyOptions.from(uri) if uri
|
ProxyOptions.from(uri) if uri
|
||||||
end
|
end
|
||||||
|
|
||||||
def find_default_proxy
|
def find_default_proxy
|
||||||
uri = ENV['http_proxy']
|
uri = ENV.fetch('http_proxy', nil)
|
||||||
return unless uri && !uri.empty?
|
return unless uri && !uri.empty?
|
||||||
|
|
||||||
uri = "http://#{uri}" unless uri.match?(/^http/i)
|
uri = "http://#{uri}" unless uri.match?(/^http/i)
|
||||||
@ -631,7 +558,7 @@ module Faraday
|
|||||||
end
|
end
|
||||||
|
|
||||||
def support_parallel?(adapter)
|
def support_parallel?(adapter)
|
||||||
adapter&.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
|
adapter.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Faraday
|
|
||||||
# DependencyLoader helps Faraday adapters and middleware load dependencies.
|
|
||||||
module DependencyLoader
|
|
||||||
attr_reader :load_error
|
|
||||||
|
|
||||||
# Executes a block which should try to require and reference dependent
|
|
||||||
# libraries
|
|
||||||
def dependency(lib = nil)
|
|
||||||
lib ? require(lib) : yield
|
|
||||||
rescue LoadError, NameError => e
|
|
||||||
self.load_error = e
|
|
||||||
end
|
|
||||||
|
|
||||||
def new(*)
|
|
||||||
unless loaded?
|
|
||||||
raise "missing dependency for #{self}: #{load_error.message}"
|
|
||||||
end
|
|
||||||
|
|
||||||
super
|
|
||||||
end
|
|
||||||
|
|
||||||
def loaded?
|
|
||||||
load_error.nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
def inherited(subclass)
|
|
||||||
super
|
|
||||||
subclass.send(:load_error=, load_error)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
attr_writer :load_error
|
|
||||||
end
|
|
||||||
end
|
|
@ -62,11 +62,17 @@ module Faraday
|
|||||||
end
|
end
|
||||||
|
|
||||||
def encode_array(parent, value)
|
def encode_array(parent, value)
|
||||||
new_parent = "#{parent}%5B%5D"
|
return "#{parent}%5B%5D" if value.empty?
|
||||||
return new_parent if value.empty?
|
|
||||||
|
|
||||||
buffer = +''
|
buffer = +''
|
||||||
value.each { |val| buffer << "#{encode_pair(new_parent, val)}&" }
|
value.each_with_index do |val, index|
|
||||||
|
new_parent = if @array_indices
|
||||||
|
"#{parent}%5B#{index}%5D"
|
||||||
|
else
|
||||||
|
"#{parent}%5B%5D"
|
||||||
|
end
|
||||||
|
buffer << "#{encode_pair(new_parent, val)}&"
|
||||||
|
end
|
||||||
buffer.chop
|
buffer.chop
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -96,13 +102,13 @@ module Faraday
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/.freeze
|
SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/
|
||||||
|
|
||||||
def decode_pair(key, value, context)
|
def decode_pair(key, value, context)
|
||||||
subkeys = key.scan(SUBKEYS_REGEX)
|
subkeys = key.scan(SUBKEYS_REGEX)
|
||||||
subkeys.each_with_index do |subkey, i|
|
subkeys.each_with_index do |subkey, i|
|
||||||
is_array = subkey =~ /[\[\]]+\Z/
|
is_array = subkey =~ /[\[\]]+\Z/
|
||||||
subkey = $` if is_array
|
subkey = Regexp.last_match.pre_match if is_array
|
||||||
last_subkey = i == subkeys.length - 1
|
last_subkey = i == subkeys.length - 1
|
||||||
|
|
||||||
context = prepare_context(context, subkey, is_array, last_subkey)
|
context = prepare_context(context, subkey, is_array, last_subkey)
|
||||||
@ -124,7 +130,7 @@ module Faraday
|
|||||||
value_type = is_array ? Array : Hash
|
value_type = is_array ? Array : Hash
|
||||||
if context[subkey] && !context[subkey].is_a?(value_type)
|
if context[subkey] && !context[subkey].is_a?(value_type)
|
||||||
raise TypeError, "expected #{value_type.name} " \
|
raise TypeError, "expected #{value_type.name} " \
|
||||||
"(got #{context[subkey].class.name}) for param `#{subkey}'"
|
"(got #{context[subkey].class.name}) for param `#{subkey}'"
|
||||||
end
|
end
|
||||||
|
|
||||||
context[subkey] ||= value_type.new
|
context[subkey] ||= value_type.new
|
||||||
@ -161,7 +167,7 @@ module Faraday
|
|||||||
# for your requests.
|
# for your requests.
|
||||||
module NestedParamsEncoder
|
module NestedParamsEncoder
|
||||||
class << self
|
class << self
|
||||||
attr_accessor :sort_params
|
attr_accessor :sort_params, :array_indices
|
||||||
|
|
||||||
extend Forwardable
|
extend Forwardable
|
||||||
def_delegators :'Faraday::Utils', :escape, :unescape
|
def_delegators :'Faraday::Utils', :escape, :unescape
|
||||||
@ -169,6 +175,7 @@ module Faraday
|
|||||||
|
|
||||||
# Useful default for OAuth and caching.
|
# Useful default for OAuth and caching.
|
||||||
@sort_params = true
|
@sort_params = true
|
||||||
|
@array_indices = false
|
||||||
|
|
||||||
extend EncodeMethods
|
extend EncodeMethods
|
||||||
extend DecodeMethods
|
extend DecodeMethods
|
||||||
|
@ -6,7 +6,7 @@ module Faraday
|
|||||||
class Error < StandardError
|
class Error < StandardError
|
||||||
attr_reader :response, :wrapped_exception
|
attr_reader :response, :wrapped_exception
|
||||||
|
|
||||||
def initialize(exc, response = nil)
|
def initialize(exc = nil, response = nil)
|
||||||
@wrapped_exception = nil unless defined?(@wrapped_exception)
|
@wrapped_exception = nil unless defined?(@wrapped_exception)
|
||||||
@response = nil unless defined?(@response)
|
@response = nil unless defined?(@response)
|
||||||
super(exc_msg_and_response!(exc, response))
|
super(exc_msg_and_response!(exc, response))
|
||||||
@ -29,15 +29,21 @@ module Faraday
|
|||||||
end
|
end
|
||||||
|
|
||||||
def response_status
|
def response_status
|
||||||
@response[:status] if @response
|
return unless @response
|
||||||
|
|
||||||
|
@response.is_a?(Faraday::Response) ? @response.status : @response[:status]
|
||||||
end
|
end
|
||||||
|
|
||||||
def response_headers
|
def response_headers
|
||||||
@response[:headers] if @response
|
return unless @response
|
||||||
|
|
||||||
|
@response.is_a?(Faraday::Response) ? @response.headers : @response[:headers]
|
||||||
end
|
end
|
||||||
|
|
||||||
def response_body
|
def response_body
|
||||||
@response[:body] if @response
|
return unless @response
|
||||||
|
|
||||||
|
@response.is_a?(Faraday::Response) ? @response.body : @response[:body]
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
@ -52,6 +58,7 @@ module Faraday
|
|||||||
# :body - Optional string HTTP response body.
|
# :body - Optional string HTTP response body.
|
||||||
# :request - Hash
|
# :request - Hash
|
||||||
# :method - Symbol with the request HTTP method.
|
# :method - Symbol with the request HTTP method.
|
||||||
|
# :url - URI object with the url requested.
|
||||||
# :url_path - String with the url path requested.
|
# :url_path - String with the url path requested.
|
||||||
# :params - String key/value hash of query params
|
# :params - String key/value hash of query params
|
||||||
# present in the request.
|
# present in the request.
|
||||||
@ -72,12 +79,46 @@ 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)
|
||||||
return [exc, exc.message, response] if exc.respond_to?(:backtrace)
|
case exc
|
||||||
|
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
|
||||||
|
|
||||||
return [nil, "the server responded with status #{exc[:status]}", exc] \
|
private
|
||||||
if exc.respond_to?(:each_key)
|
|
||||||
|
|
||||||
[nil, exc.to_s, response]
|
def build_error_message_from_hash(hash)
|
||||||
|
# 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
|
||||||
|
|
||||||
@ -105,6 +146,10 @@ module Faraday
|
|||||||
class ProxyAuthError < ClientError
|
class ProxyAuthError < ClientError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Raised by Faraday::Response::RaiseError in case of a 408 response.
|
||||||
|
class RequestTimeoutError < ClientError
|
||||||
|
end
|
||||||
|
|
||||||
# Raised by Faraday::Response::RaiseError in case of a 409 response.
|
# Raised by Faraday::Response::RaiseError in case of a 409 response.
|
||||||
class ConflictError < ClientError
|
class ConflictError < ClientError
|
||||||
end
|
end
|
||||||
@ -113,6 +158,10 @@ module Faraday
|
|||||||
class UnprocessableEntityError < ClientError
|
class UnprocessableEntityError < ClientError
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Raised by Faraday::Response::RaiseError in case of a 429 response.
|
||||||
|
class TooManyRequestsError < ClientError
|
||||||
|
end
|
||||||
|
|
||||||
# Faraday server error class. Represents 5xx status responses.
|
# Faraday server error class. Represents 5xx status responses.
|
||||||
class ServerError < Error
|
class ServerError < Error
|
||||||
end
|
end
|
||||||
@ -140,13 +189,11 @@ module Faraday
|
|||||||
class SSLError < Error
|
class SSLError < Error
|
||||||
end
|
end
|
||||||
|
|
||||||
# Raised by FaradayMiddleware::ResponseMiddleware
|
# Raised by middlewares that parse the response, like the JSON response middleware.
|
||||||
class ParsingError < Error
|
class ParsingError < Error
|
||||||
end
|
end
|
||||||
|
|
||||||
# Exception used to control the Retry middleware.
|
# Raised by Faraday::Middleware and subclasses when invalid default_options are used
|
||||||
#
|
class InitializationError < Error
|
||||||
# @see Faraday::Request::Retry
|
|
||||||
class RetriableResponse < Error
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|