Compare commits
136 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
bc27144430 | ||
|
f0aab63319 | ||
|
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 |
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.
|
||||
|
||||
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
|
||||
@ -64,50 +64,16 @@ We encourage adapters that:
|
||||
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.
|
||||
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/
|
||||
[changelog]: https://github.com/lostisland/faraday/releases
|
||||
[faraday_middleware]: https://github.com/lostisland/faraday_middleware
|
||||
[website]: https://lostisland.github.io/faraday
|
||||
[docs]: ../docs/README.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"
|
11
.github/workflows/ci.yml
vendored
@ -22,12 +22,12 @@ jobs:
|
||||
BUNDLE_WITHOUT: development:test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Set up Ruby 2.7
|
||||
- name: Setup Ruby 3.x
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.7
|
||||
ruby-version: 3
|
||||
bundler-cache: true
|
||||
|
||||
- name: Rubocop
|
||||
@ -43,7 +43,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ruby: [ '2.6', '2.7', '3.0', '3.1' ]
|
||||
ruby: [ '3.0', '3.1', '3.2', '3.3', '3.4' ]
|
||||
experimental: [false]
|
||||
include:
|
||||
- ruby: head
|
||||
@ -52,7 +52,7 @@ jobs:
|
||||
experimental: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v5
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
@ -63,7 +63,6 @@ jobs:
|
||||
run: bundle exec rake
|
||||
|
||||
- name: Test External Adapters
|
||||
if: ${{ matrix.ruby != '2.6' }}
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
run: bundle exec bake test:external
|
||||
|
||||
|
19
.github/workflows/publish.yml
vendored
@ -4,22 +4,23 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read # to checkout the code (actions/checkout)
|
||||
jobs:
|
||||
build:
|
||||
name: Publish to Rubygems
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
|
||||
- name: Set up Ruby 2.7
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Ruby 3.x
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 2.7
|
||||
bundler-cache: true
|
||||
ruby-version: 3
|
||||
|
||||
- name: Publish to RubyGems
|
||||
uses: dawidd6/action-publish-gem@v1
|
||||
with:
|
||||
api_key: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
|
||||
uses: rubygems/release-gem@v1
|
||||
|
4
.gitignore
vendored
@ -8,6 +8,7 @@ tmp
|
||||
.rvmrc
|
||||
.ruby-version
|
||||
.yardoc
|
||||
.DS_Store
|
||||
|
||||
## BUNDLER
|
||||
*.gem
|
||||
@ -16,6 +17,9 @@ Gemfile.lock
|
||||
vendor/bundle
|
||||
external
|
||||
|
||||
## NPM
|
||||
node_modules
|
||||
|
||||
## PROJECT::SPECIFIC
|
||||
.rbx
|
||||
|
||||
|
@ -7,7 +7,7 @@ require:
|
||||
AllCops:
|
||||
DisplayCopNames: true
|
||||
DisplayStyleGuide: true
|
||||
TargetRubyVersion: 2.6
|
||||
TargetRubyVersion: 3.0
|
||||
|
||||
# Custom config
|
||||
Gemspec/RequireMFA: # we don't know if this works with auto-deployments yet
|
||||
@ -18,8 +18,12 @@ Layout/LineLength:
|
||||
- examples/**/*.rb
|
||||
Metrics/BlockLength:
|
||||
Exclude:
|
||||
- lib/faraday/options/env.rb
|
||||
- spec/**/*.rb
|
||||
- examples/**/*.rb
|
||||
Metrics/ModuleLength:
|
||||
Exclude:
|
||||
- lib/faraday/options/env.rb
|
||||
Style/Documentation:
|
||||
Exclude:
|
||||
- 'spec/**/*'
|
||||
|
@ -1,6 +1,6 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config`
|
||||
# on 2022-08-08 14:26:32 UTC using RuboCop version 1.33.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
|
||||
# one by one as the offenses are removed from the code base.
|
||||
# Note that changes in the inspected code, or installation of new
|
||||
@ -23,23 +23,23 @@ Lint/EmptyBlock:
|
||||
- 'spec/faraday/rack_builder_spec.rb'
|
||||
- 'spec/faraday/response_spec.rb'
|
||||
|
||||
# Offense count: 12
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods, CountRepeatedAttributes.
|
||||
# Offense count: 13
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||
Metrics/AbcSize:
|
||||
Max: 42
|
||||
|
||||
# Offense count: 4
|
||||
# Offense count: 3
|
||||
# Configuration parameters: CountComments, CountAsOne.
|
||||
Metrics/ClassLength:
|
||||
Max: 230
|
||||
|
||||
# Offense count: 9
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods.
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 13
|
||||
|
||||
# Offense count: 26
|
||||
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, AllowedMethods, AllowedPatterns, IgnoredMethods.
|
||||
# Offense count: 27
|
||||
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
||||
Metrics/MethodLength:
|
||||
Max: 33
|
||||
|
||||
@ -48,11 +48,22 @@ Metrics/MethodLength:
|
||||
Metrics/ParameterLists:
|
||||
Max: 6
|
||||
|
||||
# Offense count: 6
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, IgnoredMethods.
|
||||
# Offense count: 7
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
Metrics/PerceivedComplexity:
|
||||
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:
|
||||
|
@ -517,7 +517,7 @@ Breaking changes:
|
||||
- Drop support for Ruby 1.8
|
||||
|
||||
Features:
|
||||
- Include wrapped exception/reponse in ClientErrors
|
||||
- Include wrapped exception/response in ClientErrors
|
||||
- Add `response.reason_phrase`
|
||||
- Provide option to selectively skip logging request/response headers
|
||||
- Add regex support for pattern matching in `test` adapter
|
||||
|
5
Gemfile
@ -10,7 +10,7 @@ group :development, :test do
|
||||
gem 'bake-test-external'
|
||||
gem 'coveralls_reborn', require: false
|
||||
gem 'pry'
|
||||
gem 'rack', '~> 2.2'
|
||||
gem 'rack', '~> 3.0'
|
||||
gem 'rake'
|
||||
gem 'rspec', '~> 3.7'
|
||||
gem 'rspec_junit_formatter', '~> 0.4'
|
||||
@ -19,8 +19,9 @@ group :development, :test do
|
||||
end
|
||||
|
||||
group :development, :lint do
|
||||
gem 'racc', '~> 1.7' # for RuboCop, on Ruby 3.3
|
||||
gem 'rubocop'
|
||||
gem 'rubocop-packaging', github: 'utkarsh2102/rubocop-packaging' # '~> 0.5'
|
||||
gem 'rubocop-packaging', '~> 0.5'
|
||||
gem 'rubocop-performance', '~> 1.0'
|
||||
gem 'yard-junk'
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009-2022 Rick Olson, Zack Hobson
|
||||
Copyright (c) 2009-2023 Rick Olson, Zack Hobson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
44
README.md
@ -1,26 +1,41 @@
|
||||
# [][website]
|
||||
# [][website]
|
||||
|
||||
[](https://rubygems.org/gems/faraday)
|
||||
[](https://github.com/lostisland/faraday/actions?query=workflow%3ACI)
|
||||
[](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.
|
||||
You probably don't want to use Faraday directly in your project, as it will lack an actual client library to perform
|
||||
requests. Instead, you probably want to have a look at [Awesome Faraday][awesome] for a list of available adapters.
|
||||
Take a look at [Awesome Faraday][awesome] for a list of available adapters and middleware.
|
||||
|
||||
## Why use Faraday?
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
This library aims to support and is [tested against][actions] the currently officially supported Ruby
|
||||
implementations. This means that, even without a major release, we could add or drop support for Ruby versions,
|
||||
following their [EOL](https://endoflife.date/ruby).
|
||||
Currently that means we support Ruby 2.6+
|
||||
Currently that means we support Ruby 3.0+
|
||||
|
||||
If something doesn't work on one of these Ruby versions, it's a bug.
|
||||
|
||||
@ -42,14 +57,11 @@ Open the issues page and check for the `help wanted` label!
|
||||
But before you start coding, please read our [Contributing Guide][contributing]
|
||||
|
||||
## Copyright
|
||||
© 2009 - 2022, the [Faraday Team][faraday_team]. Website and branding design by [Elena Lo Piccolo](https://elelopic.design).
|
||||
|
||||
[awesome]: https://github.com/lostisland/awesome-faraday/#adapters
|
||||
[website]: https://lostisland.github.io/faraday
|
||||
[faraday_team]: https://lostisland.github.io/faraday/team
|
||||
[contributing]: https://github.com/lostisland/faraday/blob/master/.github/CONTRIBUTING.md
|
||||
[apidoc]: https://www.rubydoc.info/github/lostisland/faraday
|
||||
[actions]: https://github.com/lostisland/faraday/actions
|
||||
[jruby]: http://jruby.org/
|
||||
[rubinius]: http://rubini.us/
|
||||
[license]: LICENSE.md
|
||||
© 2009 - 2023, the Faraday Team. Website and branding design by [Elena Lo Piccolo](https://elelopic.design).
|
||||
|
||||
[awesome]: https://github.com/lostisland/awesome-faraday/#adapters
|
||||
[website]: https://lostisland.github.io/faraday
|
||||
[contributing]: https://github.com/lostisland/faraday/blob/main/.github/CONTRIBUTING.md
|
||||
[apidoc]: https://www.rubydoc.info/github/lostisland/faraday
|
||||
[actions]: https://github.com/lostisland/faraday/actions
|
||||
|
7
Rakefile
@ -1,7 +1,12 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
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
|
||||
|
@ -19,7 +19,7 @@ We've taken this decision for the following technical reasons:
|
||||
focused on the Faraday API more quickly, without having to push it on all adapters immediately.
|
||||
* With the community creating more and more adapters, we wanted to avoid having first and second-class adapters
|
||||
by having some of them included with the gem and others available externally.
|
||||
* Moving adapters into separate gems allow to solve the dependency issues once and for all.
|
||||
* 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.
|
||||
|
||||
@ -112,7 +112,7 @@ but this has been removed from the v2.0 release to simplify the interface.
|
||||
### 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/authentication).
|
||||
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
|
||||
|
||||
|
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>
|
37
docs/Gemfile
@ -1,37 +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?
|
||||
|
||||
# Ruby 3.X doesn't come with webrick by default anymore
|
||||
gem 'webrick', '~> 1.7'
|
@ -1,21 +1,21 @@
|
||||
# Faraday Website
|
||||
# Faraday Docs
|
||||
|
||||
This is the root directory of the [Faraday Website][website].
|
||||
If you want to apply changes to it, please test it locally using `Jekyll`.
|
||||
Faraday Docs are powered by [Docsify](https://docsify.js.org/#/).
|
||||
|
||||
Here is how:
|
||||
## Development
|
||||
|
||||
### Setup
|
||||
|
||||
From the Faraday project root, run the following:
|
||||
|
||||
```bash
|
||||
# Navigate into the /docs folder
|
||||
$ 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/
|
||||
npm install # this will install the necessary dependencies
|
||||
```
|
||||
|
||||
[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 - 2022, 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,9 +1,4 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "Adapters"
|
||||
permalink: /adapters/
|
||||
order: 2
|
||||
---
|
||||
# Adapters
|
||||
|
||||
The Faraday Adapter interface determines how a Faraday request is turned into
|
||||
a Faraday response object. Adapters are typically implemented with common Ruby
|
||||
@ -12,7 +7,7 @@ either globally or per Faraday Connection through the configuration block.
|
||||
|
||||
For example, consider using `httpclient` as an adapter. Note that [faraday-httpclient](https://github.com/lostisland/faraday-httpclient) must be installed beforehand.
|
||||
|
||||
If you wants to configure it globally, do the following:
|
||||
If you want to configure it globally, do the following:
|
||||
|
||||
```ruby
|
||||
require 'faraday'
|
||||
@ -32,11 +27,10 @@ conn = Faraday.new do |f|
|
||||
end
|
||||
```
|
||||
|
||||
{: .mt-60}
|
||||
## Fantastic adapters and where to find them
|
||||
|
||||
With the only exception being the [Test Adapter][testing], which is for _test purposes only_,
|
||||
adapters are distributed separately from Faraday.
|
||||
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
|
||||
@ -45,13 +39,8 @@ have completely custom implementations.
|
||||
If you're just getting started you can find a list of featured adapters in [Awesome Faraday][awesome].
|
||||
Anyone can create a Faraday adapter and distribute it. If you're interested learning more, check how to [build your own][build_adapters]!
|
||||
|
||||
## 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.
|
||||
|
||||
[testing]: ./testing
|
||||
[testing]: /adapters/test-adapter.md
|
||||
[net_http]: /adapters/net-http.md
|
||||
[awesome]: https://github.com/lostisland/awesome-faraday/#adapters
|
||||
[build_adapters]: ./write_your_adapter.md
|
||||
[build_adapters]: /adapters/custom/index.md
|
||||
|
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,11 +1,4 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "Testing"
|
||||
permalink: /adapters/testing
|
||||
hide: true
|
||||
top_name: Adapters
|
||||
top_link: ./
|
||||
---
|
||||
# Test Adapter
|
||||
|
||||
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.
|
||||
@ -120,5 +113,5 @@ Faraday.default_connection = nil
|
||||
Working [RSpec] and [test/unit] examples for a fictional JSON API client are
|
||||
available.
|
||||
|
||||
[RSpec]: https://github.com/lostisland/faraday/blob/master/examples/client_spec.rb
|
||||
[test/unit]: https://github.com/lostisland/faraday/blob/master/examples/client_test.rb
|
||||
[RSpec]: https://github.com/lostisland/faraday/blob/main/examples/client_spec.rb
|
||||
[test/unit]: https://github.com/lostisland/faraday/blob/main/examples/client_test.rb
|
@ -1,171 +0,0 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "Write your own adapter"
|
||||
permalink: /adapters/write_your_adapter
|
||||
hide: true
|
||||
order: 2
|
||||
---
|
||||
|
||||
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 methods 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.
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### 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
|
||||
# ...
|
||||
end
|
||||
|
||||
Faraday::Adapter.register_middleware(florp_http: FlorpHttp)
|
||||
```
|
||||
|
||||
## 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-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).
|
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
|
@ -1,13 +1,4 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "Streaming Responses"
|
||||
permalink: /usage/streaming
|
||||
hide: true
|
||||
top_name: Usage
|
||||
top_link: ./
|
||||
prev_name: Customizing the Request
|
||||
prev_link: ./customize
|
||||
---
|
||||
# Streaming Responses
|
||||
|
||||
Sometimes you might need to receive a streaming response.
|
||||
You can do this with the `on_data` request option.
|
||||
@ -21,7 +12,8 @@ This example implements such a callback:
|
||||
# A buffer to store the streamed data
|
||||
streamed = []
|
||||
|
||||
conn.get('/stream/10') do |req|
|
||||
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.
|
||||
@ -41,4 +33,3 @@ Moreover, the `env` parameter was only recently added, which means some adapters
|
||||
(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,14 +1,9 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "Customizing the Request"
|
||||
permalink: /usage/customize
|
||||
hide: true
|
||||
top_name: Usage
|
||||
top_link: ./
|
||||
next_name: Streaming Responses
|
||||
next_link: ./streaming
|
||||
---
|
||||
# Configuration
|
||||
|
||||
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.
|
||||
|
||||
As connection options:
|
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,47 +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)
|
||||
and embraces the concept of Rack middleware when processing the request/response cycle.
|
||||
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.
|
||||
|
||||
{: .text-center}
|
||||
[<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}
|
||||
## Why use Faraday?
|
||||
|
||||
{: .mt-60}
|
||||
## Installation
|
||||
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.
|
||||
|
||||
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
|
||||
gem 'faraday'
|
||||
```
|
||||
## Who uses Faraday?
|
||||
|
||||
And then execute:
|
||||
|
||||
```bash
|
||||
$ bundle
|
||||
```
|
||||
|
||||
Or install it yourself as:
|
||||
|
||||
```bash
|
||||
$ gem install faraday
|
||||
```
|
||||
|
||||
{: .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
|
||||
[usage]: ./usage
|
||||
Faraday is used by many popular Ruby libraries, such as:
|
||||
* [Signet](https://github.com/googleapis/signet)
|
||||
* [Octokit](https://github.com/octokit/octokit.rb)
|
||||
* [Oauth2](https://bestgems.org/gems/oauth2)
|
||||
* [Elasticsearch](https://github.com/elastic/elasticsearch-ruby)
|
||||
|
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,63 +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!
|
||||
|
||||
### Can I find a middleware template somewhere?
|
||||
|
||||
Yes, you can! Look at the [`faraday-middleware-template`](https://github.com/lostisland/faraday-middleware-template) repository.
|
@ -1,13 +1,4 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "Authentication Middleware"
|
||||
permalink: /middleware/authentication
|
||||
hide: true
|
||||
next_name: UrlEncoded Middleware
|
||||
next_link: ./url-encoded
|
||||
top_name: Back to Middleware
|
||||
top_link: ./list
|
||||
---
|
||||
# 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.
|
||||
@ -30,7 +21,7 @@ Faraday.new(...) do |conn|
|
||||
end
|
||||
```
|
||||
|
||||
If the proc takes an argument, it will receive the forwarded `env`
|
||||
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|
|
@ -1,13 +1,4 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "Available Middleware"
|
||||
permalink: /middleware/list
|
||||
hide: true
|
||||
top_name: Middleware
|
||||
top_link: ./
|
||||
next_name: Writing Middleware
|
||||
next_link: ./custom
|
||||
---
|
||||
# Included middleware
|
||||
|
||||
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**.
|
||||
@ -21,9 +12,7 @@ parsing the response body, logging useful info or checking the response status.
|
||||
middleware set Header values or transform the request body based on the
|
||||
content type.
|
||||
|
||||
* [`BasicAuthentication`][authentication] sets the `Authorization` header to the `user:password`
|
||||
base64 representation.
|
||||
* [`TokenAuthentication`][authentication] sets the `Authorization` header to the specified token.
|
||||
* [`Authorization`][authentication] allows you to automatically add an Authorization header to your requests.
|
||||
* [`UrlEncoded`][url_encoded] converts a `Faraday::Request#body` hash of key/value pairs into a url-encoded request body.
|
||||
* [`Json Request`][json-request] converts a `Faraday::Request#body` hash of key/value pairs into a JSON request body.
|
||||
* [`Instrumentation`][instrumentation] allows to instrument requests using different tools.
|
||||
@ -39,10 +28,10 @@ before returning it.
|
||||
* [`RaiseError`][raise_error] checks the response HTTP code and raises an exception if it is a 4xx or 5xx code.
|
||||
|
||||
|
||||
[authentication]: ./authentication
|
||||
[url_encoded]: ./url-encoded
|
||||
[json-request]: ./json-request
|
||||
[instrumentation]: ./instrumentation
|
||||
[json-response]: ./json-response
|
||||
[logger]: ./logger
|
||||
[raise_error]: ./raise-error
|
||||
[authentication]: middleware/included/authentication.md
|
||||
[url_encoded]: middleware/included/url-encoding
|
||||
[json-request]: middleware/included/json#json-requests
|
||||
[instrumentation]: middleware/included/instrumentation
|
||||
[json-response]: middleware/included/json#json-responses
|
||||
[logger]: middleware/included/logging
|
||||
[raise_error]: middleware/included/raising-errors
|
@ -1,15 +1,4 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "Instrumentation Middleware"
|
||||
permalink: /middleware/instrumentation
|
||||
hide: true
|
||||
prev_name: JSON Request Middleware
|
||||
prev_link: ./json-request
|
||||
next_name: JSON Response Middleware
|
||||
next_link: ./json-response
|
||||
top_name: Back to Middleware
|
||||
top_link: ./list
|
||||
---
|
||||
# Instrumentation
|
||||
|
||||
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.
|
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 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "Logger Middleware"
|
||||
permalink: /middleware/logger
|
||||
hide: true
|
||||
prev_name: JSON Response Middleware
|
||||
prev_link: ./json-response
|
||||
next_name: RaiseError Middleware
|
||||
next_link: ./raise-error
|
||||
top_name: Back to Middleware
|
||||
top_link: ./list
|
||||
---
|
||||
# Logging
|
||||
|
||||
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.
|
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 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "UrlEncoded Middleware"
|
||||
permalink: /middleware/url-encoded
|
||||
hide: true
|
||||
prev_name: Authentication Middleware
|
||||
prev_link: ./authentication
|
||||
next_name: JSON Request Middleware
|
||||
next_link: ./json-request
|
||||
top_name: Back to Middleware
|
||||
top_link: ./list
|
||||
---
|
||||
# URL Encoding
|
||||
|
||||
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 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
|
@ -1,11 +1,4 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "Middleware"
|
||||
permalink: /middleware/
|
||||
next_name: Available Middleware
|
||||
next_link: ./list
|
||||
order: 3
|
||||
---
|
||||
# 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
|
||||
@ -28,7 +21,7 @@ require 'faraday'
|
||||
|
||||
conn = Faraday.new do |f|
|
||||
f.request :json # encode req bodies as JSON
|
||||
f.request :logger # logs request and responses
|
||||
f.response :logger # logs request and responses
|
||||
f.response :json # decode response bodies as JSON
|
||||
f.adapter :net_http # Use the Net::HTTP adapter
|
||||
end
|
||||
@ -44,10 +37,10 @@ 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
|
||||
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.
|
||||
|
||||

|
||||

|
||||
|
||||
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.
|
||||
@ -121,16 +114,64 @@ conn = Faraday.new do |f|
|
||||
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
|
||||
|
||||
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
|
||||
has a complete list of useful, well-maintained Faraday middleware. Middleware is
|
||||
often provided by external gems, like the
|
||||
[faraday-retry](https://github.com/lostisland/faraday-retry) gem.
|
||||
|
||||
We also have [great documentation](list) for the middleware that ships with
|
||||
Faraday.
|
||||
|
||||
### Detailed Example
|
||||
|
||||
Here's a more realistic example:
|
||||
@ -151,7 +192,7 @@ end
|
||||
This request middleware setup affects POST/PUT requests in the following way:
|
||||
|
||||
1. `Request::UrlEncoded` encodes as "application/x-www-form-urlencoded" if not
|
||||
already encoded or of another type.
|
||||
already encoded or of another type.
|
||||
2. `Response::Logger` logs request and response headers, can be configured to log bodies as well.
|
||||
|
||||
Swapping middleware means giving the other priority. Specifying the
|
||||
|
@ -1,31 +0,0 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "JSON Request Middleware"
|
||||
permalink: /middleware/json-request
|
||||
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 `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}
|
||||
```
|
@ -1,29 +0,0 @@
|
||||
---
|
||||
layout: documentation
|
||||
title: "JSON Response Middleware"
|
||||
permalink: /middleware/json-response
|
||||
hide: true
|
||||
prev_name: Instrumentation Middleware
|
||||
prev_link: ./instrumentation
|
||||
next_name: Logger Middleware
|
||||
next_link: ./logger
|
||||
top_name: Back to Middleware
|
||||
top_link: ./list
|
||||
---
|
||||
|
||||
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"}}
|
||||
```
|
@ -1,53 +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
|
||||
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, 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,179 +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.
|
||||
|
||||
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 API, is to create
|
||||
a `Faraday::Connection`. 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`. 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
|
||||
```
|
||||
|
||||
### 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'
|
||||
require 'faraday/retry'
|
||||
|
||||
conn = Faraday.new('http://httpbingo.org') do |f|
|
||||
f.request :json # encode req bodies as JSON and automatically set the Content-Type header
|
||||
f.request :retry # retry transient failures
|
||||
f.response :json # decode response bodies as JSON
|
||||
f.adapter :net_http # adds the adapter to the connection, defaults to `Faraday.default_adapter`
|
||||
end
|
||||
|
||||
# Sends a GET request with JSON body that will automatically retry in case of failure.
|
||||
response = conn.get('get', boom: 'zap')
|
||||
|
||||
# response body is automatically decoded from JSON to a Ruby hash
|
||||
response.body['args'] #=> {"boom"=>["zap"]}
|
||||
```
|
||||
|
||||
#### 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, and the `default_adapter`.
|
||||
|
||||
Note that if you create your own connection with middleware, it won't encode
|
||||
form bodies unless you too include the [`:url_encoded`][encoding] middleware!
|
||||
|
||||
[encoding]: ../middleware/url-encoded
|
@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
|
||||
spec.homepage = 'https://lostisland.github.io/faraday'
|
||||
spec.licenses = ['MIT']
|
||||
|
||||
spec.required_ruby_version = '>= 2.6'
|
||||
spec.required_ruby_version = '>= 3.0'
|
||||
|
||||
# faraday-net_http is the "default adapter", but being a Faraday dependency it can't
|
||||
# control which version of faraday it will be pulled from.
|
||||
@ -21,8 +21,9 @@ Gem::Specification.new do |spec|
|
||||
# always fix its required version to the next MINOR version.
|
||||
# This way, we can release minor versions of the adapter with "breaking" changes for older versions of Faraday
|
||||
# and then bump the version requirement on the next compatible version of faraday.
|
||||
spec.add_dependency 'faraday-net_http', '>= 2.0', '< 3.1'
|
||||
spec.add_dependency 'ruby2_keywords', '>= 0.0.4'
|
||||
spec.add_dependency 'faraday-net_http', '>= 2.0', '< 3.5'
|
||||
spec.add_dependency 'json'
|
||||
spec.add_dependency 'logger'
|
||||
|
||||
# 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']
|
||||
@ -32,6 +33,7 @@ Gem::Specification.new do |spec|
|
||||
'changelog_uri' =>
|
||||
"https://github.com/lostisland/faraday/releases/tag/v#{spec.version}",
|
||||
'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
|
||||
|
@ -1,6 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'cgi'
|
||||
require 'cgi/escape'
|
||||
require 'cgi/util' if RUBY_VERSION < '3.5'
|
||||
require 'date'
|
||||
require 'set'
|
||||
require 'forwardable'
|
||||
|
@ -26,7 +26,7 @@ module Faraday
|
||||
self.supports_parallel = false
|
||||
|
||||
def initialize(_app = nil, opts = {}, &block)
|
||||
@app = ->(env) { env.response }
|
||||
@app = lambda(&:response)
|
||||
@connection_options = opts
|
||||
@config_block = block
|
||||
end
|
||||
|
@ -146,7 +146,7 @@ module Faraday
|
||||
# which means that all of a path, parameters, and headers must be the same as an actual request.
|
||||
def strict_mode=(value)
|
||||
@strict_mode = value
|
||||
@stack.each do |_method, stubs|
|
||||
@stack.each_value do |stubs|
|
||||
stubs.each do |stub|
|
||||
stub.strict_mode = value
|
||||
end
|
||||
@ -184,7 +184,7 @@ module Faraday
|
||||
end
|
||||
|
||||
# Stub request
|
||||
class Stub < Struct.new(:host, :path, :query, :headers, :body, :strict_mode, :block) # rubocop:disable Style/StructInheritance
|
||||
Stub = Struct.new(:host, :path, :query, :headers, :body, :strict_mode, :block) do
|
||||
# @param env [Faraday::Env]
|
||||
def matches?(env)
|
||||
request_host = env[:url].host
|
||||
@ -240,7 +240,7 @@ module Faraday
|
||||
end
|
||||
|
||||
def body_match?(request_body)
|
||||
return true if body.to_s.size.zero?
|
||||
return true if body.to_s.empty?
|
||||
|
||||
case body
|
||||
when Proc
|
||||
@ -275,7 +275,7 @@ module Faraday
|
||||
|
||||
unless stub
|
||||
raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \
|
||||
"#{env[:url]} #{env[:body]}"
|
||||
"#{env[:url]} #{env[:body]} #{env[:headers]}"
|
||||
end
|
||||
|
||||
block_arity = stub.block.arity
|
||||
|
@ -15,7 +15,7 @@ module Faraday
|
||||
class Connection
|
||||
# A Set of allowed HTTP verbs.
|
||||
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.
|
||||
attr_reader :params
|
||||
@ -220,7 +220,7 @@ module Faraday
|
||||
# @yield [Faraday::Request] for further request customizations
|
||||
# @return [Faraday::Response]
|
||||
def options(*args)
|
||||
return @options if args.size.zero?
|
||||
return @options if args.empty?
|
||||
|
||||
url, params, headers = *args
|
||||
run_request(:options, url, nil, headers) do |request|
|
||||
@ -261,14 +261,13 @@ module Faraday
|
||||
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||
#
|
||||
# @example
|
||||
# # TODO: Make it a PUT example
|
||||
# conn.post '/items', data, content_type: 'application/json'
|
||||
# conn.put '/products/123', data, content_type: 'application/json'
|
||||
#
|
||||
# # Simple ElasticSearch indexing sample.
|
||||
# conn.post '/twitter/tweet' do |req|
|
||||
# req.headers[:content_type] = 'application/json'
|
||||
# req.params[:routing] = 'kimchy'
|
||||
# req.body = JSON.generate(user: 'kimchy', ...)
|
||||
# # Star a gist.
|
||||
# conn.put 'https://api.github.com/gists/GIST_ID/star' do |req|
|
||||
# req.headers['Accept'] = 'application/vnd.github+json'
|
||||
# req.headers['Authorization'] = 'Bearer <YOUR-TOKEN>'
|
||||
# req.headers['X-GitHub-Api-Version'] = '2022-11-28'
|
||||
# end
|
||||
#
|
||||
# @yield [Faraday::Request] for further request customizations
|
||||
@ -315,15 +314,23 @@ module Faraday
|
||||
#
|
||||
# @yield a block to execute multiple requests.
|
||||
# @return [void]
|
||||
def in_parallel(manager = nil)
|
||||
def in_parallel(manager = nil, &block)
|
||||
@parallel_manager = manager || default_parallel_manager do
|
||||
warn 'Warning: `in_parallel` called but no parallel-capable adapter ' \
|
||||
'on Faraday stack'
|
||||
warn caller[2, 10].join("\n")
|
||||
nil
|
||||
end
|
||||
yield
|
||||
@parallel_manager&.run
|
||||
return yield unless @parallel_manager
|
||||
|
||||
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
|
||||
@parallel_manager = nil
|
||||
end
|
||||
@ -424,8 +431,8 @@ module Faraday
|
||||
#
|
||||
# @param method [Symbol] HTTP method.
|
||||
# @param url [String, URI, nil] String or URI to access.
|
||||
# @param body [String, nil] The request body that will eventually be converted to
|
||||
# a string.
|
||||
# @param body [String, Hash, Array, nil] The request body that will eventually be converted to
|
||||
# a string; middlewares can be used to support more complex types.
|
||||
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||
#
|
||||
# @return [Faraday::Response]
|
||||
@ -471,10 +478,11 @@ module Faraday
|
||||
def build_exclusive_url(url = nil, params = nil, params_encoder = nil)
|
||||
url = nil if url.respond_to?(:empty?) && url.empty?
|
||||
base = url_prefix.dup
|
||||
if url && base.path && base.path !~ %r{/$}
|
||||
if url && !base.path.end_with?('/')
|
||||
base.path = "#{base.path}/" # ensure trailing slash
|
||||
end
|
||||
url = url.to_s.gsub(':', '%3A') if url && URI.parse(url.to_s).opaque
|
||||
# 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
|
||||
if params
|
||||
uri.query = params.to_query(params_encoder || options.params_encoder)
|
||||
@ -515,22 +523,17 @@ module Faraday
|
||||
return if Faraday.ignore_env_proxy
|
||||
|
||||
uri = nil
|
||||
if URI.parse('').respond_to?(:find_proxy)
|
||||
case url
|
||||
when String
|
||||
uri = Utils.URI(url)
|
||||
uri = if uri.host.nil?
|
||||
find_default_proxy
|
||||
else
|
||||
URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
|
||||
end
|
||||
when URI
|
||||
uri = url.find_proxy
|
||||
when nil
|
||||
uri = find_default_proxy
|
||||
end
|
||||
else
|
||||
warn 'no_proxy is unsupported' if ENV['no_proxy'] || ENV['NO_PROXY']
|
||||
case url
|
||||
when String
|
||||
uri = Utils.URI(url)
|
||||
uri = if uri.host.nil?
|
||||
find_default_proxy
|
||||
else
|
||||
URI.parse("#{uri.scheme}://#{uri.host}").find_proxy
|
||||
end
|
||||
when URI
|
||||
uri = url.find_proxy
|
||||
when nil
|
||||
uri = find_default_proxy
|
||||
end
|
||||
ProxyOptions.from(uri) if uri
|
||||
|
@ -76,9 +76,9 @@ module Faraday
|
||||
|
||||
empty_accumulator = {}
|
||||
|
||||
split_query = (query.split('&').map do |pair|
|
||||
split_query = query.split('&').filter_map do |pair|
|
||||
pair.split('=', 2) if pair && !pair.empty?
|
||||
end).compact
|
||||
end
|
||||
split_query.each_with_object(empty_accumulator.dup) do |pair, accu|
|
||||
pair[0] = unescape(pair[0])
|
||||
pair[1] = true if pair[1].nil?
|
||||
|
@ -102,7 +102,7 @@ module Faraday
|
||||
|
||||
protected
|
||||
|
||||
SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/.freeze
|
||||
SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/
|
||||
|
||||
def decode_pair(key, value, context)
|
||||
subkeys = key.scan(SUBKEYS_REGEX)
|
||||
|
@ -29,15 +29,21 @@ module Faraday
|
||||
end
|
||||
|
||||
def response_status
|
||||
@response[:status] if @response
|
||||
return unless @response
|
||||
|
||||
@response.is_a?(Faraday::Response) ? @response.status : @response[:status]
|
||||
end
|
||||
|
||||
def response_headers
|
||||
@response[:headers] if @response
|
||||
return unless @response
|
||||
|
||||
@response.is_a?(Faraday::Response) ? @response.headers : @response[:headers]
|
||||
end
|
||||
|
||||
def response_body
|
||||
@response[:body] if @response
|
||||
return unless @response
|
||||
|
||||
@response.is_a?(Faraday::Response) ? @response.body : @response[:body]
|
||||
end
|
||||
|
||||
protected
|
||||
@ -73,12 +79,46 @@ module Faraday
|
||||
|
||||
# Pulls out potential parent exception and response hash.
|
||||
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] \
|
||||
if exc.respond_to?(:each_key)
|
||||
private
|
||||
|
||||
[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
|
||||
|
||||
@ -106,6 +146,10 @@ module Faraday
|
||||
class ProxyAuthError < ClientError
|
||||
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.
|
||||
class ConflictError < ClientError
|
||||
end
|
||||
@ -114,6 +158,10 @@ module Faraday
|
||||
class UnprocessableEntityError < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 429 response.
|
||||
class TooManyRequestsError < ClientError
|
||||
end
|
||||
|
||||
# Faraday server error class. Represents 5xx status responses.
|
||||
class ServerError < Error
|
||||
end
|
||||
@ -144,4 +192,8 @@ module Faraday
|
||||
# Raised by middlewares that parse the response, like the JSON response middleware.
|
||||
class ParsingError < Error
|
||||
end
|
||||
|
||||
# Raised by Faraday::Middleware and subclasses when invalid default_options are used
|
||||
class InitializationError < Error
|
||||
end
|
||||
end
|
||||
|
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'pp' # rubocop:disable Lint/RedundantRequireStatement
|
||||
require 'pp' # This require is necessary for Hash#pretty_inspect to work, do not remove it, people rely on it.
|
||||
|
||||
module Faraday
|
||||
module Logging
|
||||
@ -13,25 +13,26 @@ module Faraday
|
||||
|
||||
def initialize(logger:, options:)
|
||||
@logger = logger
|
||||
@filter = []
|
||||
@options = DEFAULT_OPTIONS.merge(options)
|
||||
unless %i[debug info warn error fatal].include?(@options[:log_level])
|
||||
@options[:log_level] = :info
|
||||
end
|
||||
@filter = []
|
||||
end
|
||||
|
||||
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
||||
|
||||
def request(env)
|
||||
request_log = proc do
|
||||
"#{env.method.upcase} #{apply_filters(env.url.to_s)}"
|
||||
public_send(log_level) do
|
||||
"request: #{env.method.upcase} #{apply_filters(env.url.to_s)}"
|
||||
end
|
||||
public_send(log_level, 'request', &request_log)
|
||||
|
||||
log_headers('request', env.request_headers) if log_headers?(:request)
|
||||
log_body('request', env[:body]) if env[:body] && log_body?(:request)
|
||||
end
|
||||
|
||||
def response(env)
|
||||
status = proc { "Status #{env.status}" }
|
||||
public_send(log_level, 'response', &status)
|
||||
public_send(log_level) { "response: Status #{env.status}" }
|
||||
|
||||
log_headers('response', env.response_headers) if log_headers?(:response)
|
||||
log_body('response', env[:body]) if env[:body] && log_body?(:response)
|
||||
@ -40,8 +41,7 @@ module Faraday
|
||||
def exception(exc)
|
||||
return unless log_errors?
|
||||
|
||||
error_log = proc { exc.full_message }
|
||||
public_send(log_level, 'error', &error_log)
|
||||
public_send(log_level) { "error: #{exc.full_message}" }
|
||||
|
||||
log_headers('error', exc.response_headers) if exc.respond_to?(:response_headers) && log_headers?(:error)
|
||||
return unless exc.respond_to?(:response_body) && exc.response_body && log_body?(:error)
|
||||
@ -56,6 +56,8 @@ module Faraday
|
||||
private
|
||||
|
||||
def dump_headers(headers)
|
||||
return if headers.nil?
|
||||
|
||||
headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
|
||||
end
|
||||
|
||||
@ -101,21 +103,15 @@ module Faraday
|
||||
end
|
||||
|
||||
def log_level
|
||||
unless %i[debug info warn error fatal].include?(@options[:log_level])
|
||||
return :info
|
||||
end
|
||||
|
||||
@options[:log_level]
|
||||
end
|
||||
|
||||
def log_headers(type, headers)
|
||||
headers_log = proc { apply_filters(dump_headers(headers)) }
|
||||
public_send(log_level, type, &headers_log)
|
||||
public_send(log_level) { "#{type}: #{apply_filters(dump_headers(headers))}" }
|
||||
end
|
||||
|
||||
def log_body(type, body)
|
||||
body_log = proc { apply_filters(dump_body(body)) }
|
||||
public_send(log_level, type, &body_log)
|
||||
public_send(log_level) { "#{type}: #{apply_filters(dump_body(body))}" }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'monitor'
|
||||
|
||||
module Faraday
|
||||
# Middleware is the basic base class of any Faraday middleware.
|
||||
class Middleware
|
||||
@ -7,9 +9,46 @@ module Faraday
|
||||
|
||||
attr_reader :app, :options
|
||||
|
||||
DEFAULT_OPTIONS = {}.freeze
|
||||
LOCK = Mutex.new
|
||||
|
||||
def initialize(app = nil, options = {})
|
||||
@app = app
|
||||
@options = options
|
||||
@options = self.class.default_options.merge(options)
|
||||
end
|
||||
|
||||
class << self
|
||||
# Faraday::Middleware::default_options= allows user to set default options at the Faraday::Middleware
|
||||
# class level.
|
||||
#
|
||||
# @example Set the Faraday::Response::RaiseError option, `include_request` to `false`
|
||||
# my_app/config/initializers/my_faraday_middleware.rb
|
||||
#
|
||||
# Faraday::Response::RaiseError.default_options = { include_request: false }
|
||||
#
|
||||
def default_options=(options = {})
|
||||
validate_default_options(options)
|
||||
LOCK.synchronize do
|
||||
@default_options = default_options.merge(options)
|
||||
end
|
||||
end
|
||||
|
||||
# default_options attr_reader that initializes class instance variable
|
||||
# with the values of any Faraday::Middleware defaults, and merges with
|
||||
# subclass defaults
|
||||
def default_options
|
||||
@default_options ||= DEFAULT_OPTIONS.merge(self::DEFAULT_OPTIONS)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_default_options(options)
|
||||
invalid_keys = options.keys.reject { |opt| self::DEFAULT_OPTIONS.key?(opt) }
|
||||
return unless invalid_keys.any?
|
||||
|
||||
raise(Faraday::InitializationError,
|
||||
"Invalid options provided. Keys not found in #{self}::DEFAULT_OPTIONS: #{invalid_keys.join(', ')}")
|
||||
end
|
||||
end
|
||||
|
||||
def call(env)
|
||||
|
@ -30,7 +30,7 @@ module Faraday
|
||||
new_value = value
|
||||
end
|
||||
|
||||
send("#{key}=", new_value) unless new_value.nil?
|
||||
send(:"#{key}=", new_value) unless new_value.nil?
|
||||
end
|
||||
self
|
||||
end
|
||||
@ -38,7 +38,7 @@ module Faraday
|
||||
# Public
|
||||
def delete(key)
|
||||
value = send(key)
|
||||
send("#{key}=", nil)
|
||||
send(:"#{key}=", nil)
|
||||
value
|
||||
end
|
||||
|
||||
@ -57,7 +57,7 @@ module Faraday
|
||||
else
|
||||
other_value
|
||||
end
|
||||
send("#{key}=", new_value) unless new_value.nil?
|
||||
send(:"#{key}=", new_value) unless new_value.nil?
|
||||
end
|
||||
self
|
||||
end
|
||||
@ -174,6 +174,7 @@ module Faraday
|
||||
|
||||
memoized_attributes[key.to_sym] = block
|
||||
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
remove_method(key) if method_defined?(key, false)
|
||||
def #{key}() self[:#{key}]; end
|
||||
RUBY
|
||||
end
|
||||
|
@ -1,12 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# ConnectionOptions contains the configurable properties for a Faraday
|
||||
# connection object.
|
||||
class ConnectionOptions < Options.new(:request, :proxy, :ssl, :builder, :url,
|
||||
:parallel_manager, :params, :headers,
|
||||
:builder_class)
|
||||
|
||||
# @!parse
|
||||
# # ConnectionOptions contains the configurable properties for a Faraday
|
||||
# # connection object.
|
||||
# class ConnectionOptions < Options; end
|
||||
ConnectionOptions = Options.new(:request, :proxy, :ssl, :builder, :url,
|
||||
:parallel_manager, :params, :headers,
|
||||
:builder_class) do
|
||||
options request: RequestOptions, ssl: SSLOptions
|
||||
|
||||
memoized(:request) { self.class.options_for(:request).new }
|
||||
|
@ -1,65 +1,70 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# @!attribute method
|
||||
# @return [Symbol] HTTP method (`:get`, `:post`)
|
||||
#
|
||||
# @!attribute body
|
||||
# @return [String] The request body that will eventually be converted to a
|
||||
# string.
|
||||
#
|
||||
# @!attribute url
|
||||
# @return [URI] URI instance for the current request.
|
||||
#
|
||||
# @!attribute request
|
||||
# @return [Hash] options for configuring the request.
|
||||
# Options for configuring the request.
|
||||
#
|
||||
# - `:timeout` open/read timeout Integer in seconds
|
||||
# - `:open_timeout` - read timeout Integer in seconds
|
||||
# - `:on_data` - Proc for streaming
|
||||
# - `:proxy` - Hash of proxy options
|
||||
# - `:uri` - Proxy Server URI
|
||||
# - `:user` - Proxy server username
|
||||
# - `:password` - Proxy server password
|
||||
#
|
||||
# @!attribute request_headers
|
||||
# @return [Hash] HTTP Headers to be sent to the server.
|
||||
#
|
||||
# @!attribute ssl
|
||||
# @return [Hash] options for configuring SSL requests
|
||||
#
|
||||
# @!attribute parallel_manager
|
||||
# @return [Object] sent if the connection is in parallel mode
|
||||
#
|
||||
# @!attribute params
|
||||
# @return [Hash]
|
||||
#
|
||||
# @!attribute response
|
||||
# @return [Response]
|
||||
#
|
||||
# @!attribute response_headers
|
||||
# @return [Hash] HTTP headers from the server
|
||||
#
|
||||
# @!attribute status
|
||||
# @return [Integer] HTTP response status code
|
||||
#
|
||||
# @!attribute reason_phrase
|
||||
# @return [String]
|
||||
class Env < Options.new(:method, :request_body, :url, :request,
|
||||
:request_headers, :ssl, :parallel_manager, :params,
|
||||
:response, :response_headers, :status,
|
||||
:reason_phrase, :response_body)
|
||||
|
||||
# rubocop:disable Naming/ConstantName
|
||||
ContentLength = 'Content-Length'
|
||||
StatusesWithoutBody = Set.new [204, 304]
|
||||
SuccessfulStatuses = (200..299).freeze
|
||||
# rubocop:enable Naming/ConstantName
|
||||
# @!parse
|
||||
# # @!attribute method
|
||||
# # @return [Symbol] HTTP method (`:get`, `:post`)
|
||||
# #
|
||||
# # @!attribute body
|
||||
# # @return [String] The request body that will eventually be converted to a
|
||||
# # string.
|
||||
# #
|
||||
# # @!attribute url
|
||||
# # @return [URI] URI instance for the current request.
|
||||
# #
|
||||
# # @!attribute request
|
||||
# # @return [Hash] options for configuring the request.
|
||||
# # Options for configuring the request.
|
||||
# #
|
||||
# # - `:timeout` - time limit for the entire request (Integer in
|
||||
# # seconds)
|
||||
# # - `:open_timeout` - time limit for just the connection phase (e.g.
|
||||
# # handshake) (Integer in seconds)
|
||||
# # - `:read_timeout` - time limit for the first response byte received from
|
||||
# # the server (Integer in seconds)
|
||||
# # - `:write_timeout` - time limit for the client to send the request to the
|
||||
# # server (Integer in seconds)
|
||||
# # - `:on_data` - Proc for streaming
|
||||
# # - `:proxy` - Hash of proxy options
|
||||
# # - `:uri` - Proxy server URI
|
||||
# # - `:user` - Proxy server username
|
||||
# # - `:password` - Proxy server password
|
||||
# #
|
||||
# # @!attribute request_headers
|
||||
# # @return [Hash] HTTP Headers to be sent to the server.
|
||||
# #
|
||||
# # @!attribute ssl
|
||||
# # @return [Hash] options for configuring SSL requests
|
||||
# #
|
||||
# # @!attribute parallel_manager
|
||||
# # @return [Object] sent if the connection is in parallel mode
|
||||
# #
|
||||
# # @!attribute params
|
||||
# # @return [Hash]
|
||||
# #
|
||||
# # @!attribute response
|
||||
# # @return [Response]
|
||||
# #
|
||||
# # @!attribute response_headers
|
||||
# # @return [Hash] HTTP headers from the server
|
||||
# #
|
||||
# # @!attribute status
|
||||
# # @return [Integer] HTTP response status code
|
||||
# #
|
||||
# # @!attribute reason_phrase
|
||||
# # @return [String]
|
||||
# class Env < Options; end
|
||||
Env = Options.new(:method, :request_body, :url, :request,
|
||||
:request_headers, :ssl, :parallel_manager, :params,
|
||||
:response, :response_headers, :status,
|
||||
:reason_phrase, :response_body) do
|
||||
const_set(:ContentLength, 'Content-Length')
|
||||
const_set(:StatusesWithoutBody, Set.new([204, 304]))
|
||||
const_set(:SuccessfulStatuses, 200..299)
|
||||
|
||||
# A Set of HTTP verbs that typically send a body. If no body is set for
|
||||
# these requests, the Content-Length header is set to 0.
|
||||
MethodsWithBodies = Set.new(Faraday::METHODS_WITH_BODY.map(&:to_sym))
|
||||
const_set(:MethodsWithBodies, Set.new(Faraday::METHODS_WITH_BODY.map(&:to_sym)))
|
||||
|
||||
options request: RequestOptions,
|
||||
request_headers: Utils::Headers, response_headers: Utils::Headers
|
||||
@ -120,25 +125,25 @@ module Faraday
|
||||
|
||||
# @return [Boolean] true if status is in the set of {SuccessfulStatuses}.
|
||||
def success?
|
||||
SuccessfulStatuses.include?(status)
|
||||
Env::SuccessfulStatuses.include?(status)
|
||||
end
|
||||
|
||||
# @return [Boolean] true if there's no body yet, and the method is in the
|
||||
# set of {MethodsWithBodies}.
|
||||
# set of {Env::MethodsWithBodies}.
|
||||
def needs_body?
|
||||
!body && MethodsWithBodies.include?(method)
|
||||
!body && Env::MethodsWithBodies.include?(method)
|
||||
end
|
||||
|
||||
# Sets content length to zero and the body to the empty string.
|
||||
def clear_body
|
||||
request_headers[ContentLength] = '0'
|
||||
request_headers[Env::ContentLength] = '0'
|
||||
self.body = +''
|
||||
end
|
||||
|
||||
# @return [Boolean] true if the status isn't in the set of
|
||||
# {StatusesWithoutBody}.
|
||||
# {Env::StatusesWithoutBody}.
|
||||
def parse_body?
|
||||
!StatusesWithoutBody.include?(status)
|
||||
!Env::StatusesWithoutBody.include?(status)
|
||||
end
|
||||
|
||||
# @return [Boolean] true if there is a parallel_manager
|
||||
@ -164,7 +169,7 @@ module Faraday
|
||||
def stream_response(&block)
|
||||
size = 0
|
||||
yielded = false
|
||||
block_result = block.call do |chunk| # rubocop:disable Performance/RedundantBlockCall
|
||||
block_result = block.call do |chunk|
|
||||
if chunk.bytesize.positive? || size.positive?
|
||||
yielded = true
|
||||
size += chunk.bytesize
|
||||
|
@ -1,15 +1,19 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# ProxyOptions contains the configurable properties for the proxy
|
||||
# configuration used when making an HTTP request.
|
||||
class ProxyOptions < Options.new(:uri, :user, :password)
|
||||
# @!parse
|
||||
# # ProxyOptions contains the configurable properties for the proxy
|
||||
# # configuration used when making an HTTP request.
|
||||
# class ProxyOptions < Options; end
|
||||
ProxyOptions = Options.new(:uri, :user, :password) do
|
||||
extend Forwardable
|
||||
def_delegators :uri, :scheme, :scheme=, :host, :host=, :port, :port=,
|
||||
:path, :path=
|
||||
|
||||
def self.from(value)
|
||||
case value
|
||||
when ''
|
||||
value = nil
|
||||
when String
|
||||
# URIs without a scheme should default to http (like 'example:123').
|
||||
# This fixes #1282 and prevents a silent failure in some adapters.
|
||||
@ -18,8 +22,10 @@ module Faraday
|
||||
when URI
|
||||
value = { uri: value }
|
||||
when Hash, Options
|
||||
if (uri = value.delete(:uri))
|
||||
value[:uri] = Utils.URI(uri)
|
||||
if value[:uri]
|
||||
value = value.dup.tap do |duped|
|
||||
duped[:uri] = Utils.URI(duped[:uri])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1,12 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# RequestOptions contains the configurable properties for a Faraday request.
|
||||
class RequestOptions < Options.new(:params_encoder, :proxy, :bind,
|
||||
:timeout, :open_timeout, :read_timeout,
|
||||
:write_timeout, :boundary, :oauth,
|
||||
:context, :on_data)
|
||||
|
||||
# @!parse
|
||||
# # RequestOptions contains the configurable properties for a Faraday request.
|
||||
# class RequestOptions < Options; end
|
||||
RequestOptions = Options.new(:params_encoder, :proxy, :bind,
|
||||
:timeout, :open_timeout, :read_timeout,
|
||||
:write_timeout, :boundary, :oauth,
|
||||
:context, :on_data) do
|
||||
def []=(key, value)
|
||||
if key && key.to_sym == :proxy
|
||||
super(key, value ? ProxyOptions.from(value) : nil)
|
||||
|
@ -1,56 +1,63 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# SSL-related options.
|
||||
#
|
||||
# @!attribute verify
|
||||
# @return [Boolean] whether to verify SSL certificates or not
|
||||
#
|
||||
# @!attribute verify_hostname
|
||||
# @return [Boolean] whether to enable hostname verification on server certificates
|
||||
# during the handshake or not (see https://github.com/ruby/openssl/pull/60)
|
||||
#
|
||||
# @!attribute ca_file
|
||||
# @return [String] CA file
|
||||
#
|
||||
# @!attribute ca_path
|
||||
# @return [String] CA path
|
||||
#
|
||||
# @!attribute verify_mode
|
||||
# @return [Integer] Any `OpenSSL::SSL::` constant (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL.html)
|
||||
#
|
||||
# @!attribute cert_store
|
||||
# @return [OpenSSL::X509::Store] certificate store
|
||||
#
|
||||
# @!attribute client_cert
|
||||
# @return [String, OpenSSL::X509::Certificate] client certificate
|
||||
#
|
||||
# @!attribute client_key
|
||||
# @return [String, OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] client key
|
||||
#
|
||||
# @!attribute certificate
|
||||
# @return [OpenSSL::X509::Certificate] certificate (Excon only)
|
||||
#
|
||||
# @!attribute private_key
|
||||
# @return [OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] private key (Excon only)
|
||||
#
|
||||
# @!attribute verify_depth
|
||||
# @return [Integer] maximum depth for the certificate chain verification
|
||||
#
|
||||
# @!attribute version
|
||||
# @return [String, Symbol] SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D)
|
||||
#
|
||||
# @!attribute min_version
|
||||
# @return [String, Symbol] minimum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D)
|
||||
#
|
||||
# @!attribute max_version
|
||||
# @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)
|
||||
class SSLOptions < Options.new(:verify, :verify_hostname,
|
||||
:ca_file, :ca_path, :verify_mode,
|
||||
:cert_store, :client_cert, :client_key,
|
||||
:certificate, :private_key, :verify_depth,
|
||||
:version, :min_version, :max_version)
|
||||
|
||||
# @!parse
|
||||
# # SSL-related options.
|
||||
# #
|
||||
# # @!attribute verify
|
||||
# # @return [Boolean] whether to verify SSL certificates or not
|
||||
# #
|
||||
# # @!attribute verify_hostname
|
||||
# # @return [Boolean] whether to enable hostname verification on server certificates
|
||||
# # during the handshake or not (see https://github.com/ruby/openssl/pull/60)
|
||||
# #
|
||||
# # @!attribute hostname
|
||||
# # @return [String] Server hostname used for SNI (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLSocket.html#method-i-hostname-3D)
|
||||
# #
|
||||
# # @!attribute ca_file
|
||||
# # @return [String] CA file
|
||||
# #
|
||||
# # @!attribute ca_path
|
||||
# # @return [String] CA path
|
||||
# #
|
||||
# # @!attribute verify_mode
|
||||
# # @return [Integer] Any `OpenSSL::SSL::` constant (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL.html)
|
||||
# #
|
||||
# # @!attribute cert_store
|
||||
# # @return [OpenSSL::X509::Store] certificate store
|
||||
# #
|
||||
# # @!attribute client_cert
|
||||
# # @return [String, OpenSSL::X509::Certificate] client certificate
|
||||
# #
|
||||
# # @!attribute client_key
|
||||
# # @return [String, OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] client key
|
||||
# #
|
||||
# # @!attribute certificate
|
||||
# # @return [OpenSSL::X509::Certificate] certificate (Excon only)
|
||||
# #
|
||||
# # @!attribute private_key
|
||||
# # @return [OpenSSL::PKey::RSA, OpenSSL::PKey::DSA] private key (Excon only)
|
||||
# #
|
||||
# # @!attribute verify_depth
|
||||
# # @return [Integer] maximum depth for the certificate chain verification
|
||||
# #
|
||||
# # @!attribute version
|
||||
# # @return [String, Symbol] SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D)
|
||||
# #
|
||||
# # @!attribute min_version
|
||||
# # @return [String, Symbol] minimum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D)
|
||||
# #
|
||||
# # @!attribute max_version
|
||||
# # @return [String, Symbol] maximum SSL version (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)
|
||||
# #
|
||||
# # @!attribute ciphers
|
||||
# # @return [String] cipher list in OpenSSL format (see https://ruby-doc.org/stdlib-2.5.1/libdoc/openssl/rdoc/OpenSSL/SSL/SSLContext.html#method-i-ciphers-3D)
|
||||
# class SSLOptions < Options; end
|
||||
SSLOptions = Options.new(:verify, :verify_hostname, :hostname,
|
||||
:ca_file, :ca_path, :verify_mode,
|
||||
:cert_store, :client_cert, :client_key,
|
||||
:certificate, :private_key, :verify_depth,
|
||||
:version, :min_version, :max_version, :ciphers) do
|
||||
# @return [Boolean] true if should verify
|
||||
def verify?
|
||||
verify != false
|
||||
|
@ -1,6 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'ruby2_keywords'
|
||||
require 'faraday/adapter_registry'
|
||||
|
||||
module Faraday
|
||||
@ -28,10 +27,11 @@ module Faraday
|
||||
|
||||
attr_reader :name
|
||||
|
||||
ruby2_keywords def initialize(klass, *args, &block)
|
||||
def initialize(klass, *args, **kwargs, &block)
|
||||
@name = klass.to_s
|
||||
REGISTRY.set(klass) if klass.respond_to?(:name)
|
||||
@args = args
|
||||
@kwargs = kwargs
|
||||
@block = block
|
||||
end
|
||||
|
||||
@ -54,7 +54,7 @@ module Faraday
|
||||
end
|
||||
|
||||
def build(app = nil)
|
||||
klass.new(app, *@args, &@block)
|
||||
klass.new(app, *@args, **@kwargs, &@block)
|
||||
end
|
||||
end
|
||||
|
||||
@ -89,52 +89,52 @@ module Faraday
|
||||
@handlers.frozen?
|
||||
end
|
||||
|
||||
ruby2_keywords def use(klass, *args, &block)
|
||||
def use(klass, ...)
|
||||
if klass.is_a? Symbol
|
||||
use_symbol(Faraday::Middleware, klass, *args, &block)
|
||||
use_symbol(Faraday::Middleware, klass, ...)
|
||||
else
|
||||
raise_if_locked
|
||||
raise_if_adapter(klass)
|
||||
@handlers << self.class::Handler.new(klass, *args, &block)
|
||||
@handlers << self.class::Handler.new(klass, ...)
|
||||
end
|
||||
end
|
||||
|
||||
ruby2_keywords def request(key, *args, &block)
|
||||
use_symbol(Faraday::Request, key, *args, &block)
|
||||
def request(key, ...)
|
||||
use_symbol(Faraday::Request, key, ...)
|
||||
end
|
||||
|
||||
ruby2_keywords def response(key, *args, &block)
|
||||
use_symbol(Faraday::Response, key, *args, &block)
|
||||
def response(...)
|
||||
use_symbol(Faraday::Response, ...)
|
||||
end
|
||||
|
||||
ruby2_keywords def adapter(klass = NO_ARGUMENT, *args, &block)
|
||||
def adapter(klass = NO_ARGUMENT, *args, **kwargs, &block)
|
||||
return @adapter if klass == NO_ARGUMENT || klass.nil?
|
||||
|
||||
klass = Faraday::Adapter.lookup_middleware(klass) if klass.is_a?(Symbol)
|
||||
@adapter = self.class::Handler.new(klass, *args, &block)
|
||||
@adapter = self.class::Handler.new(klass, *args, **kwargs, &block)
|
||||
end
|
||||
|
||||
## methods to push onto the various positions in the stack:
|
||||
|
||||
ruby2_keywords def insert(index, *args, &block)
|
||||
def insert(index, ...)
|
||||
raise_if_locked
|
||||
index = assert_index(index)
|
||||
handler = self.class::Handler.new(*args, &block)
|
||||
handler = self.class::Handler.new(...)
|
||||
@handlers.insert(index, handler)
|
||||
end
|
||||
|
||||
alias insert_before insert
|
||||
|
||||
ruby2_keywords def insert_after(index, *args, &block)
|
||||
def insert_after(index, ...)
|
||||
index = assert_index(index)
|
||||
insert(index + 1, *args, &block)
|
||||
insert(index + 1, ...)
|
||||
end
|
||||
|
||||
ruby2_keywords def swap(index, *args, &block)
|
||||
def swap(index, ...)
|
||||
raise_if_locked
|
||||
index = assert_index(index)
|
||||
@handlers.delete_at(index)
|
||||
insert(index, *args, &block)
|
||||
insert(index, ...)
|
||||
end
|
||||
|
||||
def delete(handler)
|
||||
@ -221,7 +221,7 @@ module Faraday
|
||||
end
|
||||
|
||||
def raise_if_adapter(klass)
|
||||
return unless is_adapter?(klass)
|
||||
return unless klass <= Faraday::Adapter
|
||||
|
||||
raise 'Adapter should be set using the `adapter` method, not `use`'
|
||||
end
|
||||
@ -234,12 +234,8 @@ module Faraday
|
||||
!@adapter.nil?
|
||||
end
|
||||
|
||||
def is_adapter?(klass) # rubocop:disable Naming/PredicateName
|
||||
klass <= Faraday::Adapter
|
||||
end
|
||||
|
||||
ruby2_keywords def use_symbol(mod, key, *args, &block)
|
||||
use(mod.lookup_middleware(key), *args, &block)
|
||||
def use_symbol(mod, key, ...)
|
||||
use(mod.lookup_middleware(key), ...)
|
||||
end
|
||||
|
||||
def assert_index(index)
|
||||
|
@ -24,13 +24,14 @@ module Faraday
|
||||
# @return [String] body
|
||||
# @!attribute options
|
||||
# @return [RequestOptions] options
|
||||
#
|
||||
# rubocop:disable Style/StructInheritance
|
||||
class Request < Struct.new(:http_method, :path, :params, :headers, :body, :options)
|
||||
# rubocop:enable Style/StructInheritance
|
||||
|
||||
Request = Struct.new(:http_method, :path, :params, :headers, :body, :options) do
|
||||
extend MiddlewareRegistry
|
||||
|
||||
alias_method :member_get, :[]
|
||||
private :member_get
|
||||
alias_method :member_set, :[]=
|
||||
private :member_set
|
||||
|
||||
# @param request_method [String]
|
||||
# @yield [request] for block customization, if block given
|
||||
# @yieldparam request [Request]
|
||||
@ -41,6 +42,7 @@ module Faraday
|
||||
end
|
||||
end
|
||||
|
||||
remove_method :params=
|
||||
# Replace params, preserving the existing hash type.
|
||||
#
|
||||
# @param hash [Hash] new params
|
||||
@ -48,10 +50,11 @@ module Faraday
|
||||
if params
|
||||
params.replace hash
|
||||
else
|
||||
super
|
||||
member_set(:params, hash)
|
||||
end
|
||||
end
|
||||
|
||||
remove_method :headers=
|
||||
# Replace request headers, preserving the existing hash type.
|
||||
#
|
||||
# @param hash [Hash] new headers
|
||||
@ -59,7 +62,7 @@ module Faraday
|
||||
if headers
|
||||
headers.replace hash
|
||||
else
|
||||
super
|
||||
member_set(:headers, hash)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -5,12 +5,14 @@ module Faraday
|
||||
# Middleware for instrumenting Requests.
|
||||
class Instrumentation < Faraday::Middleware
|
||||
# Options class used in Request::Instrumentation class.
|
||||
class Options < Faraday::Options.new(:name, :instrumenter)
|
||||
Options = Faraday::Options.new(:name, :instrumenter) do
|
||||
remove_method :name
|
||||
# @return [String]
|
||||
def name
|
||||
self[:name] ||= 'request.faraday'
|
||||
end
|
||||
|
||||
remove_method :instrumenter
|
||||
# @return [Class]
|
||||
def instrumenter
|
||||
self[:instrumenter] ||= ActiveSupport::Notifications
|
||||
|
@ -13,7 +13,7 @@ module Faraday
|
||||
# Doesn't try to encode bodies that already are in string form.
|
||||
class Json < Middleware
|
||||
MIME_TYPE = 'application/json'
|
||||
MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}.freeze
|
||||
MIME_TYPE_REGEX = %r{^application/(vnd\..+\+)?json$}
|
||||
|
||||
def on_request(env)
|
||||
match_content_type(env) do |data|
|
||||
@ -24,7 +24,13 @@ module Faraday
|
||||
private
|
||||
|
||||
def encode(data)
|
||||
::JSON.generate(data)
|
||||
if options[:encoder].is_a?(Array) && options[:encoder].size >= 2
|
||||
options[:encoder][0].public_send(options[:encoder][1], data)
|
||||
elsif options[:encoder].respond_to?(:dump)
|
||||
options[:encoder].dump(data)
|
||||
else
|
||||
::JSON.generate(data)
|
||||
end
|
||||
end
|
||||
|
||||
def match_content_type(env)
|
||||
@ -40,7 +46,16 @@ module Faraday
|
||||
end
|
||||
|
||||
def body?(env)
|
||||
(body = env[:body]) && !(body.respond_to?(:to_str) && body.empty?)
|
||||
body = env[:body]
|
||||
case body
|
||||
when true, false
|
||||
true
|
||||
when nil
|
||||
# NOTE: nil can be converted to `"null"`, but this middleware doesn't process `nil` for the compatibility.
|
||||
false
|
||||
else
|
||||
!(body.respond_to?(:to_str) && body.empty?)
|
||||
end
|
||||
end
|
||||
|
||||
def request_type(env)
|
||||
|
@ -61,7 +61,8 @@ module Faraday
|
||||
def to_hash
|
||||
{
|
||||
status: env.status, body: env.body,
|
||||
response_headers: env.response_headers
|
||||
response_headers: env.response_headers,
|
||||
url: env.url
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -11,6 +11,8 @@ module Faraday
|
||||
@parser_options = parser_options
|
||||
@content_types = Array(content_type)
|
||||
@preserve_raw = preserve_raw
|
||||
|
||||
process_parser_options
|
||||
end
|
||||
|
||||
def on_complete(env)
|
||||
@ -27,7 +29,11 @@ module Faraday
|
||||
end
|
||||
|
||||
def parse(body)
|
||||
::JSON.parse(body, @parser_options || {}) unless body.strip.empty?
|
||||
return if body.strip.empty?
|
||||
|
||||
decoder, method_name = @decoder_options
|
||||
|
||||
decoder.public_send(method_name, body, @parser_options || {})
|
||||
end
|
||||
|
||||
def parse_response?(env)
|
||||
@ -47,6 +53,20 @@ module Faraday
|
||||
type = type.split(';', 2).first if type.index(';')
|
||||
type
|
||||
end
|
||||
|
||||
def process_parser_options
|
||||
@decoder_options = @parser_options&.delete(:decoder)
|
||||
|
||||
@decoder_options =
|
||||
if @decoder_options.is_a?(Array) && @decoder_options.size >= 2
|
||||
@decoder_options.slice(0, 2)
|
||||
elsif @decoder_options&.respond_to?(:load) # rubocop:disable Lint/RedundantSafeNavigation
|
||||
# In some versions of Rails, `nil` responds to `load` - hence the safe navigation check above
|
||||
[@decoder_options, :load]
|
||||
else
|
||||
[::JSON, :parse]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -10,11 +10,13 @@ module Faraday
|
||||
# lifecycle to a given Logger object. By default, this logs to STDOUT. See
|
||||
# Faraday::Logging::Formatter to see specifically what is logged.
|
||||
class Logger < Middleware
|
||||
DEFAULT_OPTIONS = { formatter: Logging::Formatter }.merge(Logging::Formatter::DEFAULT_OPTIONS).freeze
|
||||
|
||||
def initialize(app, logger = nil, options = {})
|
||||
super(app)
|
||||
super(app, options)
|
||||
logger ||= ::Logger.new($stdout)
|
||||
formatter_class = options.delete(:formatter) || Logging::Formatter
|
||||
@formatter = formatter_class.new(logger: logger, options: options)
|
||||
formatter_class = @options.delete(:formatter)
|
||||
@formatter = formatter_class.new(logger: logger, options: @options)
|
||||
yield @formatter if block_given?
|
||||
end
|
||||
|
||||
|
@ -6,28 +6,32 @@ module Faraday
|
||||
# client or server error responses.
|
||||
class RaiseError < Middleware
|
||||
# rubocop:disable Naming/ConstantName
|
||||
ClientErrorStatuses = (400...500).freeze
|
||||
ServerErrorStatuses = (500...600).freeze
|
||||
ClientErrorStatuses = (400...500)
|
||||
ServerErrorStatuses = (500...600)
|
||||
ClientErrorStatusesWithCustomExceptions = {
|
||||
400 => Faraday::BadRequestError,
|
||||
401 => Faraday::UnauthorizedError,
|
||||
403 => Faraday::ForbiddenError,
|
||||
404 => Faraday::ResourceNotFound,
|
||||
408 => Faraday::RequestTimeoutError,
|
||||
409 => Faraday::ConflictError,
|
||||
422 => Faraday::UnprocessableEntityError,
|
||||
429 => Faraday::TooManyRequestsError
|
||||
}.freeze
|
||||
# rubocop:enable Naming/ConstantName
|
||||
|
||||
DEFAULT_OPTIONS = { include_request: true, allowed_statuses: [] }.freeze
|
||||
|
||||
def on_complete(env)
|
||||
return if Array(options[:allowed_statuses]).include?(env[:status])
|
||||
|
||||
case env[:status]
|
||||
when 400
|
||||
raise Faraday::BadRequestError, response_values(env)
|
||||
when 401
|
||||
raise Faraday::UnauthorizedError, response_values(env)
|
||||
when 403
|
||||
raise Faraday::ForbiddenError, response_values(env)
|
||||
when 404
|
||||
raise Faraday::ResourceNotFound, response_values(env)
|
||||
when *ClientErrorStatusesWithCustomExceptions.keys
|
||||
raise ClientErrorStatusesWithCustomExceptions[env[:status]], response_values(env)
|
||||
when 407
|
||||
# mimic the behavior that we get with proxy requests with HTTPS
|
||||
msg = %(407 "Proxy Authentication Required")
|
||||
raise Faraday::ProxyAuthError.new(msg, response_values(env))
|
||||
when 409
|
||||
raise Faraday::ConflictError, response_values(env)
|
||||
when 422
|
||||
raise Faraday::UnprocessableEntityError, response_values(env)
|
||||
when ClientErrorStatuses
|
||||
raise Faraday::ClientError, response_values(env)
|
||||
when ServerErrorStatuses
|
||||
@ -37,11 +41,26 @@ module Faraday
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a hash of response data with the following keys:
|
||||
# - status
|
||||
# - headers
|
||||
# - body
|
||||
# - request
|
||||
#
|
||||
# The `request` key is omitted when the middleware is explicitly
|
||||
# configured with the option `include_request: false`.
|
||||
def response_values(env)
|
||||
{
|
||||
response = {
|
||||
status: env.status,
|
||||
headers: env.response_headers,
|
||||
body: env.body,
|
||||
body: env.body
|
||||
}
|
||||
|
||||
# Include the request data by default. If the middleware was explicitly
|
||||
# configured to _not_ include request data, then omit it.
|
||||
return response unless options[:include_request]
|
||||
|
||||
response.merge(
|
||||
request: {
|
||||
method: env.method,
|
||||
url: env.url,
|
||||
@ -50,7 +69,7 @@ module Faraday
|
||||
headers: env.request_headers,
|
||||
body: env.request_body
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
def query_params(env)
|
||||
|
@ -1,6 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'base64'
|
||||
require 'uri'
|
||||
require 'faraday/utils/headers'
|
||||
require 'faraday/utils/params_hash'
|
||||
@ -26,7 +25,7 @@ module Faraday
|
||||
attr_writer :default_space_encoding
|
||||
end
|
||||
|
||||
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/.freeze
|
||||
ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
|
||||
|
||||
def escape(str)
|
||||
str.to_s.gsub(ESCAPE_RE) do |match|
|
||||
@ -38,7 +37,7 @@ module Faraday
|
||||
CGI.unescape str.to_s
|
||||
end
|
||||
|
||||
DEFAULT_SEP = /[&;] */n.freeze
|
||||
DEFAULT_SEP = /[&;] */n
|
||||
|
||||
# Adapted from Rack
|
||||
def parse_query(query)
|
||||
@ -54,7 +53,7 @@ module Faraday
|
||||
end
|
||||
|
||||
def basic_header_from(login, pass)
|
||||
value = Base64.encode64("#{login}:#{pass}")
|
||||
value = ["#{login}:#{pass}"].pack('m') # Base64 encoding
|
||||
value.delete!("\n")
|
||||
"Basic #{value}"
|
||||
end
|
||||
|