Compare commits
1913 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 | ||
|
20a5d573f9 | ||
|
86aa000b2d | ||
|
c02a104469 | ||
|
9a16831147 | ||
|
1914d594c4 | ||
|
3ee9c81335 | ||
|
c6668ef8a8 | ||
|
4ad9c1e921 | ||
|
8af0c875bc | ||
|
2862ae6212 | ||
|
12bab73ec3 | ||
|
93affec586 | ||
|
5d28006566 | ||
|
ebdcee2b6e | ||
|
26a35fd76a | ||
|
36916f0759 | ||
|
4024f4d402 | ||
|
fe6e71bbe0 | ||
|
e1dbdf065e | ||
|
7926e3aa89 | ||
|
4816043101 | ||
|
b3b922949d | ||
|
89b1fe3da9 | ||
|
43d6797770 | ||
|
5e01af3828 | ||
|
646c30b8cb | ||
|
591b58fc06 | ||
|
1ddf062331 | ||
|
1a5b794b88 | ||
|
8a6a12100d | ||
|
6799f5852d | ||
|
5d4c1fb962 | ||
|
af116aa0a3 | ||
|
7f444c3971 | ||
|
69e88b4d52 | ||
|
cfbea91a69 | ||
|
fecc0a24cb | ||
|
607a725719 | ||
|
9d4486616a | ||
|
d420a12a57 | ||
|
fcb2003178 | ||
|
71a70f1d8c | ||
|
6ea010be93 | ||
|
257268e0c8 | ||
|
a2b5f7f3a8 | ||
|
56d58442e5 | ||
|
24eafaa99a | ||
|
24b3fd36e6 | ||
|
8f00640dd5 | ||
|
fc11225474 | ||
|
f003443acc | ||
|
b2390ec2b3 | ||
|
3fc35dac23 | ||
|
948274e25e | ||
|
46f3b678f0 | ||
|
85dfdf824c | ||
|
33563e3d63 | ||
|
26d47ca5bb | ||
|
816d824bc1 | ||
|
9f80f875f7 | ||
|
ae55a744d1 | ||
|
7f004913eb | ||
|
577f0d3e75 | ||
|
996028a165 | ||
|
026e9569a1 | ||
|
295f112356 | ||
|
9825916400 | ||
|
309797d573 | ||
|
5f88f8ff85 | ||
|
565f463fdc | ||
|
50e3ebb805 | ||
|
0a5714e8c8 | ||
|
88d16b7e2d | ||
|
2d60ce0ac7 | ||
|
cb47eca810 | ||
|
93a693b9ef | ||
|
c9b8490bb7 | ||
|
1c3e27616d | ||
|
b1165eac65 | ||
|
a555580f5f | ||
|
5b8c8a6f2e | ||
|
23e2495636 | ||
|
9ef407a028 | ||
|
65b8d3904f | ||
|
64058b983c | ||
|
cc749c612d | ||
|
746ab737b5 | ||
|
3caf118cbc | ||
|
d159fac7b5 | ||
|
c6bff34abb | ||
|
498f6027a1 | ||
|
de1e9f82d2 | ||
|
b3f417dc20 | ||
|
de78dff26d | ||
|
c010031269 | ||
|
ceeaa7476f | ||
|
1d567df2ee | ||
|
77a464344a | ||
|
5366029282 | ||
|
506e97afe6 | ||
|
04c43898ee | ||
|
fdf797bb28 | ||
|
0f9626c48d | ||
|
dd1552f3ff | ||
|
94b7bf4bf8 | ||
|
7de0a389dc | ||
|
1dd6d98a28 | ||
|
abe78b5df5 | ||
|
fa26357178 | ||
|
120c56988e | ||
|
f1f36be663 | ||
|
f0bc54fecb | ||
|
b79a36f65f | ||
|
4f6d2f830e | ||
|
babfdeaf04 | ||
|
c95038abb5 | ||
|
9fa6a7e2aa | ||
|
c0d97cefa5 | ||
|
33235f482e | ||
|
b183095c52 | ||
|
01e840e151 | ||
|
cc5391bc0d | ||
|
e640be9866 | ||
|
59c32867b3 | ||
|
0b719c18a6 | ||
|
d143b2d4f0 | ||
|
006940a359 | ||
|
fe600c72ad | ||
|
7d727f4f02 | ||
|
fa612a060a | ||
|
ce0fdf8bca | ||
|
da878eaf38 | ||
|
456191715a | ||
|
4b6162d364 | ||
|
83a97b72df | ||
|
08b7d4d3b7 | ||
|
ca37a9420e | ||
|
a1ee375bcd | ||
|
6333b73d13 | ||
|
a089fb83fa | ||
|
fd1007ae33 | ||
|
66d5dae371 | ||
|
6ed220cabf | ||
|
8727853801 | ||
|
0e8bfa592f | ||
|
bc4eb877c9 | ||
|
274f5fa6ba | ||
|
8ca6a7eb58 | ||
|
4dd36897af | ||
|
830b696ae7 | ||
|
63c33d234d | ||
|
1f496a7f17 | ||
|
11d793dd7e | ||
|
53e4baedc7 | ||
|
67c5f7b239 | ||
|
a582495311 | ||
|
048718fb79 | ||
|
e82694793b | ||
|
41d87d6225 | ||
|
bf7d855598 | ||
|
38d9398559 | ||
|
0787548639 | ||
|
952dac0b4c | ||
|
a0c4a1cd03 | ||
|
21ac595919 | ||
|
fbb441eca2 | ||
|
33ee1c4eed | ||
|
20be816bc8 | ||
|
8d379a16fe | ||
|
99afc0ff6f | ||
|
506954e5fe | ||
|
14d0dd70c4 | ||
|
850155eb41 | ||
|
118f896ac3 | ||
|
e7ab94f169 | ||
|
0ed6480f6e | ||
|
d22dd00856 | ||
|
086bb62217 | ||
|
18b9bd5e0c | ||
|
f68a821dde | ||
|
023cc41335 | ||
|
453b26f1f0 | ||
|
687108bb4d | ||
|
d67d012dd5 | ||
|
968dfc18ba | ||
|
e41668ee59 | ||
|
2457525702 | ||
|
97a3bc2386 | ||
|
e111db34c3 | ||
|
47411d8154 | ||
|
ebbbab5c2e | ||
|
b766d1f848 | ||
|
1595e6fc05 | ||
|
8c746f68e3 | ||
|
01c25e880c | ||
|
95667f848a | ||
|
4e9f4b06f1 | ||
|
0977306827 | ||
|
d56742a174 | ||
|
7326cd8bfd | ||
|
680407daa9 | ||
|
571dc11c2f | ||
|
0461c4b321 | ||
|
85b46bef6c | ||
|
a5b7a6bc81 | ||
|
8ee406d788 | ||
|
868fe9bb18 | ||
|
9af091f9c4 | ||
|
6521a1606e | ||
|
5acab36239 | ||
|
e02a8c1009 | ||
|
f6130995eb | ||
|
90b4564cec | ||
|
722821fab7 | ||
|
019e1a8417 | ||
|
41c0dc7edf | ||
|
87ecaf9682 | ||
|
680b9f8a6c | ||
|
c26df87b86 | ||
|
3b3de79e3d | ||
|
3ca146de4d | ||
|
e33c559345 | ||
|
f0c76f826f | ||
|
5547e9131a | ||
|
864a7e52f3 | ||
|
b4ad6e386e | ||
|
f6401225a4 | ||
|
5d24afc373 | ||
|
e2c56e90c9 | ||
|
52e30bf8e8 | ||
|
284d920cf3 | ||
|
e80b829976 | ||
|
1042a45618 | ||
|
a4837c8509 | ||
|
d77c9efee9 | ||
|
8560572d16 | ||
|
614c26fe9d | ||
|
099dd45f63 | ||
|
47dcf037a7 | ||
|
008321b24f | ||
|
e346b8bdb4 | ||
|
b30ec73e99 | ||
|
6015ae97d6 | ||
|
024bfebd1a | ||
|
8a43a47919 | ||
|
ff9dc1d121 | ||
|
cd9f83d86c | ||
|
9f8e13753b | ||
|
94e4e48ef4 | ||
|
bf454e6369 | ||
|
688d5706b4 | ||
|
41bc84e15f | ||
|
dac0eee62d | ||
|
df5fc690df | ||
|
5b6f9d87d3 | ||
|
61d466df87 | ||
|
a0fd827dc5 | ||
|
d2372224f2 | ||
|
aea390a67c | ||
|
cbedb826c9 | ||
|
b4b328b720 | ||
|
5a2bb2e5e6 | ||
|
b322331923 | ||
|
7ba417400f | ||
|
6833062a5c | ||
|
5efd43f749 | ||
|
24b99b8231 | ||
|
e4aee9f7f2 | ||
|
3301356e7d | ||
|
9726bc2029 | ||
|
15595d0370 | ||
|
a53a4360e2 | ||
|
465b1db267 | ||
|
7856e18a31 | ||
|
073bc25cd4 | ||
|
970a1922c0 | ||
|
f1768df507 | ||
|
475ddeab13 | ||
|
0ebc233513 | ||
|
69f3078825 | ||
|
4d49b031cd | ||
|
0b9552e707 | ||
|
9379c716d4 | ||
|
e7ddc2d298 | ||
|
108a0a7b74 | ||
|
5a9644937e | ||
|
0c873c1500 | ||
|
f33e2fa1f8 | ||
|
d5eaa4ffe0 | ||
|
b5f9936dbe | ||
|
e4b4b97307 | ||
|
27a6509797 | ||
|
c638dbcb4b | ||
|
4e358a6741 | ||
|
2c4087d78f | ||
|
520e3578d4 | ||
|
c82135056b | ||
|
b1b617e53f | ||
|
0294fed33c | ||
|
43cd327f19 | ||
|
9e4fa74515 | ||
|
68c53c448f | ||
|
48e6c3f820 | ||
|
e331955fd8 | ||
|
b5bbf1f2d2 | ||
|
9de70e0184 | ||
|
9c3b8f40cb | ||
|
993a483ec3 | ||
|
c844e4c53e | ||
|
e78a1e022a | ||
|
708ca5b9ab | ||
|
f7f91b7077 | ||
|
232ec3892e | ||
|
a91581a04e | ||
|
296f336b62 | ||
|
07ddcb93f5 | ||
|
72b917c8be | ||
|
83d933a61e | ||
|
75b9804e33 | ||
|
18c204e3a5 | ||
|
edcbd9f8e4 | ||
|
b85589c72a | ||
|
0aea513d72 | ||
|
6394bc08a5 | ||
|
711c257295 | ||
|
55c0b742ec | ||
|
cccc145e2f | ||
|
940e01ce38 | ||
|
6c7bf772b9 | ||
|
dcae077607 | ||
|
9882080737 | ||
|
435e45b154 | ||
|
872a249fa7 | ||
|
f8615d5255 | ||
|
ba7b82d9a2 | ||
|
80ba52c721 | ||
|
bfe360a0a7 | ||
|
bfdb66ab71 | ||
|
86d3cf2e8b | ||
|
36ff4a5c3e | ||
|
b35f6a2480 | ||
|
8dae875906 | ||
|
a5a398c231 | ||
|
6c117f47b9 | ||
|
24cf49c216 | ||
|
abade0d453 | ||
|
dbe4fae178 | ||
|
956c53f99a | ||
|
3575ad6485 | ||
|
b863c167d6 | ||
|
c0c911d0ee | ||
|
c3bbb16dd3 | ||
|
df8b15e720 | ||
|
6fdc48e163 | ||
|
249835f8b6 | ||
|
1418454f04 | ||
|
7dded89b33 | ||
|
f4ae96ea9b | ||
|
e8792e43d5 | ||
|
f825112db3 | ||
|
e1c71d4d57 | ||
|
ffbc597146 | ||
|
d0ed8f891f | ||
|
8e31ce1684 | ||
|
90122910d1 | ||
|
25c4791791 | ||
|
8d2eb3c079 | ||
|
fff3227cee | ||
|
52b260112a | ||
|
25979431e4 | ||
|
4a2d1e6b3d | ||
|
d8ccce2bc0 | ||
|
8b71ba3320 | ||
|
bbb015b500 | ||
|
657b0231f8 | ||
|
869cf338de | ||
|
ac27ce502e | ||
|
8fa2b46196 | ||
|
8585604508 | ||
|
432fcd267d | ||
|
0ec66e8959 | ||
|
70764fb34e | ||
|
7eae1cbf23 | ||
|
2a2a0ebfe5 | ||
|
a285d7a8b6 | ||
|
26c542d9e1 | ||
|
6cd8c5dc35 | ||
|
0826817f4e | ||
|
5d3e667c1a | ||
|
9070f6bf2e | ||
|
275d1e3daa | ||
|
62cbdb9d78 | ||
|
08cd1b3be9 | ||
|
caf7545906 | ||
|
75605b9c36 | ||
|
2f96d9b3a7 | ||
|
cf078cf20b | ||
|
94a78e7eba | ||
|
637c81196b | ||
|
5cf6753e60 | ||
|
a50fcc413b | ||
|
32be8ce245 | ||
|
a8295c2c4a | ||
|
5ca8630d9b | ||
|
112331a325 | ||
|
32eacfd4ee | ||
|
a963c12a82 | ||
|
f851834ccd | ||
|
8434b225a1 | ||
|
f1d72a4cdf | ||
|
79da580283 | ||
|
681b6baa33 | ||
|
31d03bec46 | ||
|
78fd49eecf | ||
|
069b422750 | ||
|
9f99e11596 | ||
|
1c3bf50d8a | ||
|
caa877dc83 | ||
|
4400acfd2b | ||
|
a2becaaf6a | ||
|
1a2970eba0 | ||
|
86fd91cf04 | ||
|
eb9660a818 | ||
|
eb80f9aa26 | ||
|
d5906c100a | ||
|
ad9a3965ee | ||
|
d76489a062 | ||
|
06e364d998 | ||
|
75f3ca2443 | ||
|
f97523dbf1 | ||
|
c17f918425 | ||
|
3acd1c8d31 | ||
|
f0b780f822 | ||
|
1818542ab7 | ||
|
bd8d044507 | ||
|
1a9312ce82 | ||
|
d4f062a56b | ||
|
9214d0b8ee | ||
|
11f27d2214 | ||
|
06b4fac02b | ||
|
91ba32e9d2 | ||
|
43233f5766 | ||
|
7af105dabf | ||
|
66384d32dd | ||
|
8e89cab029 | ||
|
7ad1bcd2ed | ||
|
272de2e480 | ||
|
a217471dc6 | ||
|
3abe9d1eea | ||
|
9265247834 | ||
|
be097190e9 | ||
|
4b63db64a0 | ||
|
8a65a58de8 | ||
|
b4a27f3734 | ||
|
980a9514e0 | ||
|
84200628ee | ||
|
07788ab977 | ||
|
d9ec3a5f48 | ||
|
03a0217354 | ||
|
71c78694d4 | ||
|
c8cf1ac535 | ||
|
b9a7560f72 | ||
|
7c9a322b0d | ||
|
7280a3803d | ||
|
d91a5f4567 | ||
|
def850b619 | ||
|
e74dbad644 | ||
|
f6ee27d0c7 | ||
|
aa125e1bc1 | ||
|
39058484d4 | ||
|
522b0d595d | ||
|
913583ff11 | ||
|
5d62e6f70f | ||
|
e4f41053a0 | ||
|
17b576ef12 | ||
|
bd684191dd | ||
|
a8cd68e18f | ||
|
474b2b690f | ||
|
e54c86a6ce | ||
|
8230d11214 | ||
|
1fa623b35a | ||
|
cda3a0171d | ||
|
34bf23ae40 | ||
|
7aa80e21b9 | ||
|
342c2e4eb8 | ||
|
ef8d9eb2bc | ||
|
4656ca4283 | ||
|
d986824c32 | ||
|
e2c07759de | ||
|
5063ffe963 | ||
|
ffc6911ed3 | ||
|
e500ccd42d | ||
|
d16ea15626 | ||
|
9e29d03f63 | ||
|
6394090d1b | ||
|
6167052fc7 | ||
|
a2161e10a8 | ||
|
5b924852fb | ||
|
6f0168b20b | ||
|
f50de1c6ed | ||
|
88698363fd | ||
|
224dd98ebe | ||
|
14b596fd30 | ||
|
6ff61c0ecc | ||
|
c8c9a0217c | ||
|
0a47c7b115 | ||
|
a1f698ef44 | ||
|
c5632466d9 | ||
|
752b67cad4 | ||
|
e955e55cb7 | ||
|
94a26eb1a8 | ||
|
bd322bba6b | ||
|
4102f3add4 | ||
|
626d68f6ff | ||
|
82c82a9a8f | ||
|
c58723d028 | ||
|
490ee83ddf | ||
|
4d88475295 | ||
|
609d1768cb | ||
|
385203be78 | ||
|
780222e1ec | ||
|
e84d1653b7 | ||
|
5b97393d2b | ||
|
96047c855b | ||
|
0d3e566f68 | ||
|
ea1142a4a2 | ||
|
338de4d938 | ||
|
148b99ee0f | ||
|
726de4a186 | ||
|
2730f1247a | ||
|
f6e8d7ad58 | ||
|
907c5190b1 | ||
|
73747c66b7 | ||
|
c801391d18 | ||
|
1d3d675821 | ||
|
04dff67cfa | ||
|
28e995d9c6 | ||
|
b9ea1d92ce | ||
|
00cbb94f3d | ||
|
5f276b36fa | ||
|
dcb7030289 | ||
|
e8382421aa | ||
|
04b90eb43e | ||
|
ab33c8ace1 | ||
|
ded4f6ef9a | ||
|
e342992751 | ||
|
dbba0734d6 | ||
|
c5ceb2679c | ||
|
6b0eb71322 | ||
|
ef4044281d | ||
|
8ba22d5da2 | ||
|
2a04aeadb3 | ||
|
f3e9adba3b | ||
|
5b49e6c9b2 | ||
|
c209c41da6 | ||
|
fb21e2db1a | ||
|
1a7ce3e367 | ||
|
8140cba74c | ||
|
af1c1b5bb6 | ||
|
f8f5985f3e | ||
|
376eb2307a | ||
|
49073081c8 | ||
|
cfb80e55af | ||
|
5d4ed5e401 | ||
|
b80111bed5 | ||
|
1fe9763c19 | ||
|
1c0f43b9fd | ||
|
801f7d517e | ||
|
d657e3d3c3 | ||
|
ea61fd7e44 | ||
|
06a7c74162 | ||
|
f1d0b3933d | ||
|
7931f07d6f | ||
|
228df3da71 | ||
|
15303a3415 | ||
|
a41b6ac8d9 | ||
|
096a1c8bac | ||
|
61baf2b466 | ||
|
2c9a288046 | ||
|
c8bee3ed22 | ||
|
4d97cc13ac | ||
|
5f55e62e88 | ||
|
4171215da7 | ||
|
f46a17573d | ||
|
c1febd329f | ||
|
c9fcbcc048 | ||
|
4c7e0a7717 | ||
|
5bbf4eb3ef | ||
|
c2719dc5cb | ||
|
9e23ce0be5 | ||
|
3396561021 | ||
|
5d469d7ece | ||
|
0af6142f47 | ||
|
2aa4d808a7 | ||
|
2102b439c4 | ||
|
398f2cb236 | ||
|
e35c49efa8 | ||
|
127555abcf | ||
|
fd7dbc709b | ||
|
03ff75a189 | ||
|
6a62558687 | ||
|
8c66270eb5 | ||
|
190467254e | ||
|
cc703af68d | ||
|
08c107516d | ||
|
558746a776 | ||
|
d3fc6ec365 | ||
|
5adaaab45c | ||
|
94f4175059 | ||
|
afd5e634cc | ||
|
f3cf995140 | ||
|
f9ffeb26f2 | ||
|
ff2a6d7eda | ||
|
768c5151d2 | ||
|
5e462f6ce6 | ||
|
bbf937dd44 | ||
|
64d9002b75 | ||
|
420b5a433f | ||
|
7bfac0cf29 | ||
|
9005f4fbff | ||
|
2fa4043f21 | ||
|
9e25ee6adc | ||
|
769efe6af6 | ||
|
5525c49529 | ||
|
25b1055dc0 | ||
|
553b20ac1a | ||
|
9c25d73db7 | ||
|
615ad218b4 | ||
|
e511d2fb29 | ||
|
b000eff257 | ||
|
2cfe8bd4b2 | ||
|
28504a14c2 | ||
|
5ea9b5d709 | ||
|
cf60074f52 | ||
|
7ddccdbd1e | ||
|
ba6247eb35 | ||
|
52a513d232 | ||
|
a68ebbcb72 | ||
|
8cb5865a78 | ||
|
7e6b5c6be2 | ||
|
52833e2f2b | ||
|
7c823b042c | ||
|
62cfe5bed1 | ||
|
3adf411dcd | ||
|
6634449ca0 | ||
|
aa4e33ff50 | ||
|
d579431fa0 | ||
|
cbbce86940 | ||
|
7af02185d5 | ||
|
9cd764b2e7 | ||
|
281e3f4add | ||
|
8abc9e6942 | ||
|
00765145a3 | ||
|
a4ee8a7704 | ||
|
d9c696ff2c | ||
|
dc4d8f620e | ||
|
0abe56597e | ||
|
7f593f6d5f | ||
|
39dd9549f8 | ||
|
627ed8775e | ||
|
f55382880e | ||
|
fc53cc5d2d | ||
|
ff92c9ea04 | ||
|
3a11286c87 | ||
|
e00e900486 | ||
|
de0e96054d | ||
|
b427091f0e | ||
|
fcc4c4043e | ||
|
91ddced8b2 | ||
|
ec709b3974 | ||
|
671e9de604 | ||
|
5cad588227 | ||
|
b2857e6e20 | ||
|
f98c3e92fd | ||
|
b585462dd5 | ||
|
1054fcdf92 | ||
|
03d05a25a9 | ||
|
c8248ba2a4 | ||
|
d3cbcd8825 | ||
|
848251420a | ||
|
9939ba6234 | ||
|
6e3c40f159 | ||
|
6e4a2aaa05 | ||
|
3fbf2d5261 | ||
|
0474e03d2d | ||
|
5ace0ea5ab | ||
|
1f8aae53ae | ||
|
e14fb01063 | ||
|
793e6ba9c9 | ||
|
0e07aaee76 | ||
|
dad29566dc | ||
|
f1b2657523 | ||
|
554780253f | ||
|
f18a248c36 | ||
|
a37f8c5c37 | ||
|
ec3ccb128c | ||
|
3ff3dbc43e | ||
|
e77ac3d9e8 | ||
|
88de44ebe3 | ||
|
afe53e9020 | ||
|
81274c93a2 | ||
|
e306bd6d6b | ||
|
9478d597bb | ||
|
7974c7a95c | ||
|
3b2b86a8e1 | ||
|
8309a875bb | ||
|
f26a8d675b | ||
|
82a8a0bd13 | ||
|
f08a985bd1 | ||
|
8ba0797af9 | ||
|
aafbb24771 | ||
|
fe379511f0 | ||
|
c63efc6472 | ||
|
e8589f9db6 | ||
|
5ee253a9a3 | ||
|
ac661139fe | ||
|
5f1687abbe | ||
|
632ea6c96d | ||
|
1fa90511f1 | ||
|
49112d2d69 | ||
|
e7c79907f3 | ||
|
4381c2d62d | ||
|
89b3365896 | ||
|
6b564ee8e8 | ||
|
0aebd35d9f | ||
|
0989bf1f76 | ||
|
d9661c4567 | ||
|
77fc3ecee9 | ||
|
6ddf9c3b80 | ||
|
c6a2098e08 | ||
|
5f9314fba6 | ||
|
452f04d342 | ||
|
c0e65d5e4e | ||
|
162932650f | ||
|
1793f9fd47 | ||
|
33f771d0c4 | ||
|
5cdb17cf10 | ||
|
f2e0e22442 | ||
|
7c1c1caecb | ||
|
2f91e9da6e | ||
|
e6311bd286 | ||
|
8635eaf69f | ||
|
397166eda0 | ||
|
60b862ddc5 | ||
|
ea9309d7d3 | ||
|
50d03b8c14 | ||
|
ab94d2d384 | ||
|
8503baea4b | ||
|
74941bdd46 | ||
|
817a1759ef | ||
|
5f8b09e843 | ||
|
69932412cb | ||
|
b302d708ea | ||
|
53b5e087e9 | ||
|
4ef728230c | ||
|
32ab739d2e | ||
|
0fa3ae14d7 | ||
|
43336d45b5 | ||
|
c8abc30488 | ||
|
86ee246840 | ||
|
936e57879b | ||
|
e63ef9b830 | ||
|
2b580148fd | ||
|
4d1ddc130b | ||
|
7acc82b6a5 | ||
|
ff94676369 | ||
|
29feeb92e3 | ||
|
b09c6db315 | ||
|
ead6e342f6 | ||
|
d1b05653a5 | ||
|
ad7a8e5e26 | ||
|
f8892b1301 | ||
|
923f90558c | ||
|
d18cc04be5 | ||
|
e04b6d0b61 | ||
|
5ba7c30a7b | ||
|
cf6f2b10ac | ||
|
debc8e938b | ||
|
4e6296a6ee | ||
|
33547461d2 | ||
|
94097a7f66 | ||
|
375ea71a9f | ||
|
4206d48f54 | ||
|
8049a5feb2 | ||
|
067be864a6 | ||
|
b2e8bcc04f | ||
|
e1e50f5eb4 | ||
|
81c72afd95 | ||
|
ecd8841f51 | ||
|
e29ec08545 | ||
|
a5f027e1ce | ||
|
68e88a90b1 | ||
|
86f3092b93 | ||
|
617c909c9c | ||
|
3268aca539 | ||
|
565b2a49af | ||
|
4adc385c76 | ||
|
1d3faf2a2f | ||
|
fa54064ae3 | ||
|
378350d5c4 | ||
|
3e60d2ed7b | ||
|
dea7726423 | ||
|
6b3add2056 | ||
|
f33cf73daa | ||
|
8a273bf93f | ||
|
e74f24d852 | ||
|
f994b054f9 | ||
|
f58f251051 | ||
|
04a6f3b759 | ||
|
bc419bddc9 | ||
|
0e7089a8fb | ||
|
389c503a39 | ||
|
e66210bd9d | ||
|
d2f000be48 | ||
|
1b9e16e53a | ||
|
0f17254d4a | ||
|
347d15b2ec | ||
|
3e0feb2f1b | ||
|
a7f4a5a4b5 | ||
|
378ab13384 | ||
|
82b47f4225 | ||
|
974e0e122c | ||
|
4841852afd | ||
|
bfc1563c2c | ||
|
fb6ef2014a | ||
|
6f923c2d16 | ||
|
f440c44c6e | ||
|
e61dfeafe4 | ||
|
e8899e0bcd | ||
|
80245b0bef | ||
|
3167d038d2 | ||
|
d5eab44f30 | ||
|
3e5e1dd5af | ||
|
a712938071 | ||
|
1647251ec7 | ||
|
a38ba7a13b | ||
|
05293e1950 | ||
|
8de4d963f5 | ||
|
a239d6ba1e | ||
|
a945af3f01 | ||
|
09cdff22e0 | ||
|
46e0375745 | ||
|
360b79e8ee | ||
|
27233b4bbc | ||
|
d9bd9e542b | ||
|
efa07ccde6 | ||
|
00d7ac208c | ||
|
2ed4908308 | ||
|
4f0e5455d5 | ||
|
df798f2ba0 | ||
|
1735907954 | ||
|
2151a0dd67 | ||
|
61cbd3eb6d | ||
|
be21e7849a | ||
|
986e557e9d | ||
|
a1520b6c92 | ||
|
80c0e66102 | ||
|
b681452bbf | ||
|
3579225fd1 | ||
|
7f9b9041d9 | ||
|
877d78bcdd | ||
|
956d671e51 | ||
|
5343d4c2e2 | ||
|
e10ef5ea89 | ||
|
9efca4778b | ||
|
f97f1874e4 | ||
|
fba7ce503d | ||
|
f73d13ee09 | ||
|
c94890c499 | ||
|
c12810be89 | ||
|
f737e4199c | ||
|
a410d77110 | ||
|
eac07e5d87 | ||
|
b1964f7758 | ||
|
1f29d62e21 | ||
|
ac73c46a78 | ||
|
b3e49cba6c | ||
|
c79150889f | ||
|
9e035a65b3 | ||
|
f78d51da17 | ||
|
5a98819e22 | ||
|
5fde154ffd | ||
|
6ffc244823 | ||
|
1ffc29eb54 | ||
|
915d7fae2f | ||
|
257b9a82b8 | ||
|
e11151ded9 | ||
|
a921a30f8b | ||
|
ad52344e63 | ||
|
d98b6408e3 | ||
|
2d8bc89535 | ||
|
94c2dae1e7 | ||
|
658862c355 | ||
|
541eb49889 | ||
|
95c574b0dd | ||
|
d92295c70e | ||
|
b4095c67be | ||
|
6c0a03419c | ||
|
bda8300553 | ||
|
f8165970fc | ||
|
4bd8c2f679 | ||
|
dedd377c8a | ||
|
033e7b420d | ||
|
ab9a0fb4ad | ||
|
bc453746c1 | ||
|
86e5fa4ee3 | ||
|
7578f0828d | ||
|
441d8ac979 | ||
|
ad226f75ab | ||
|
5bfa4bf948 | ||
|
03f373a1fb | ||
|
b5533f4563 | ||
|
cc9772bfdf | ||
|
faf501b277 | ||
|
7b4e048a58 | ||
|
74f00f0ea5 | ||
|
3cd10fd4e4 | ||
|
1b106889a0 | ||
|
2c97f3ca58 | ||
|
c613e5d8b3 | ||
|
c69813cfae | ||
|
a39183e3ef | ||
|
5d40163dd9 | ||
|
b43e0682f5 | ||
|
1316f4cbd9 | ||
|
ecfa6ccd46 | ||
|
a8529feda2 | ||
|
4090b396fc | ||
|
245873dad1 | ||
|
e36f029d39 | ||
|
4615ec4eaf | ||
|
fc4cec558e | ||
|
01b434dbaf | ||
|
81f16593a0 | ||
|
761930f3ae | ||
|
15a045d348 | ||
|
280f08a3b2 | ||
|
576e372b3b | ||
|
db265ba441 | ||
|
7cd86a10e3 | ||
|
1d71fcab65 | ||
|
c877521697 | ||
|
fc40d53ca2 | ||
|
dc4797eb8b | ||
|
6262970390 | ||
|
626c7f1cf7 | ||
|
7ea98de998 | ||
|
c2f325a329 | ||
|
26992f031c | ||
|
731fb68e90 | ||
|
13e12528c5 | ||
|
66ee3f23aa | ||
|
dd45710b29 | ||
|
baa537d89c | ||
|
416a91bd8c | ||
|
9ad0fcd38a | ||
|
b1948c994b | ||
|
12fdb93ba8 | ||
|
458f319c0d | ||
|
71973e57cc | ||
|
ac7cc3a7bf | ||
|
88f1020c3b | ||
|
e3e9c19a43 | ||
|
cd5601706f | ||
|
8601c97190 | ||
|
731f49039d | ||
|
998d26cd62 | ||
|
df9d5c31c5 | ||
|
76c90bfdc0 | ||
|
80c8b62de1 | ||
|
cf549f4d88 | ||
|
b88c4846fa | ||
|
c396060bd8 | ||
|
d8b9eda2d2 | ||
|
022afd8334 | ||
|
6e37316372 | ||
|
4fe046e6ba | ||
|
edacd5eb57 | ||
|
27d1fac7b1 | ||
|
3dc615a72e | ||
|
d4942876c8 | ||
|
07c30a20b9 | ||
|
62a5781b92 | ||
|
b5ea93a0cc | ||
|
625116c888 | ||
|
90faa57440 | ||
|
ee6aba99be | ||
|
65b5e8a440 | ||
|
4d0938343a | ||
|
03d92dd203 | ||
|
0dcbe1023e | ||
|
220b6dc02e | ||
|
cd3040cbe5 | ||
|
b8d90a59ba | ||
|
c8b03d9241 | ||
|
99d8bb8be4 | ||
|
cf544f8650 | ||
|
40bee8fed7 | ||
|
87950cd38c | ||
|
d30873c9fe | ||
|
362baf728e | ||
|
7660b4a2d7 | ||
|
1fc287815d | ||
|
7ec31061b1 | ||
|
aad50e0bb2 | ||
|
77c040476a | ||
|
edcd7d61f3 | ||
|
93164c9c9e | ||
|
026edcac09 | ||
|
48d9304780 | ||
|
d96ca17662 | ||
|
d62559eac6 | ||
|
816ff76d11 | ||
|
1311a096f1 | ||
|
66d5678ad6 | ||
|
09202e298a | ||
|
2f64974b55 | ||
|
c0b7fd3496 | ||
|
6243f16b9b | ||
|
94d7611f18 | ||
|
f6ab4dea06 | ||
|
02f8d48bb5 | ||
|
932a590a91 | ||
|
e5a3cc6caa | ||
|
d52c4996aa | ||
|
677bb46453 | ||
|
80eacae193 | ||
|
1b94e96022 | ||
|
eb34dc31c5 | ||
|
1b43a1c16a | ||
|
67d3cbf454 | ||
|
12e1cef7aa | ||
|
7d0defae6e | ||
|
212278c339 | ||
|
57ed5bdeff | ||
|
0188ada941 | ||
|
7a23e7b877 | ||
|
a9a4aa4873 | ||
|
f3959d25c6 | ||
|
0c945d13d1 | ||
|
5db941bb1e | ||
|
c9c1c4c23e | ||
|
7dff04854e | ||
|
6453976f89 | ||
|
86663ca8fb | ||
|
5cb4a38495 | ||
|
bc82276414 | ||
|
37bd330979 | ||
|
3e6654667f | ||
|
a8afbdb306 | ||
|
c7b412beae | ||
|
0adc6c69b2 | ||
|
f39b4e664e | ||
|
010f81976f | ||
|
4ac193943b | ||
|
63cf47c95b | ||
|
5b6f89d528 | ||
|
9e488a1375 | ||
|
2aec848a7c | ||
|
7380ffc137 | ||
|
605dd37e32 | ||
|
9427063d97 | ||
|
d0b01a9b31 | ||
|
0a1deaf173 | ||
|
eed09439f7 | ||
|
9c7eb9c643 | ||
|
cf2574b5e4 | ||
|
ee3d8e62e8 | ||
|
934567f11d | ||
|
4e3ff091b7 | ||
|
54af0ba0c7 | ||
|
2f1ff93a70 | ||
|
6b152a0689 | ||
|
477a5e463c | ||
|
f28bf056cf | ||
|
715e1af7d1 | ||
|
cff8dded75 | ||
|
42d6018566 | ||
|
4488ce523e | ||
|
6615769ed2 | ||
|
853b874480 | ||
|
fb6ed87714 | ||
|
0e5c87ee43 | ||
|
7ee49ebff2 | ||
|
2e806e4caa | ||
|
e837ffdc69 | ||
|
bf973245b5 | ||
|
8080e54f55 | ||
|
872b45a781 | ||
|
26ab2a7f2c | ||
|
4f826047a7 | ||
|
a23124ea7b | ||
|
08257d9deb | ||
|
cad28c0ef3 | ||
|
c328e5285a | ||
|
2c5bf666b3 | ||
|
60842fac4e | ||
|
045c42eed4 | ||
|
e42a21a697 | ||
|
f4e3fc3440 | ||
|
ab13cac5d5 | ||
|
50023ad11d | ||
|
cf5e8a691f | ||
|
615516bbb5 | ||
|
2911204870 | ||
|
eb2a411cd0 | ||
|
7cdff322cf | ||
|
3a9e033d00 | ||
|
e35819bbec | ||
|
01b9fd390a | ||
|
7aeda79760 | ||
|
5af44c90e1 | ||
|
4066203474 | ||
|
f94f30f1bf | ||
|
c24a02c73b | ||
|
63b8a1d977 | ||
|
c26a060acd | ||
|
baac8f0aab | ||
|
4e0b1537c6 | ||
|
2c7da3bea6 | ||
|
0d5123f9ae | ||
|
7b9d583b62 | ||
|
5bc8d62a92 | ||
|
9a077510b2 | ||
|
c4d835493a | ||
|
50f853048e | ||
|
150b5e6c7d | ||
|
c2adfbf918 | ||
|
505587954f | ||
|
d164dae907 | ||
|
7a38f177dc | ||
|
76701fddb4 | ||
|
c5006e8d88 | ||
|
c395b0c463 | ||
|
69096c0f1a | ||
|
ca8c169072 | ||
|
36379e01af | ||
|
e9164b9bfe | ||
|
28653690c6 | ||
|
89af3915f7 | ||
|
f619a5d334 | ||
|
8775c07495 | ||
|
ebbf413b36 | ||
|
f6831b53b3 | ||
|
970ab0c5be | ||
|
75ab43c2ea | ||
|
40481b49f7 | ||
|
a6059bb5af | ||
|
4ff67eae74 | ||
|
e156a0d6f3 | ||
|
31e7fae0bd | ||
|
4f37e8c0ef | ||
|
8f6a14685f | ||
|
0819790251 | ||
|
b9e89c7562 | ||
|
abccadd2be | ||
|
d5afe2b4c0 | ||
|
7d581322b1 | ||
|
c99350905d | ||
|
2793e2ae91 | ||
|
df484dfd1a | ||
|
f978e771b5 | ||
|
4b30bf8b5e | ||
|
5d6faf7671 | ||
|
a27c952f5e | ||
|
f24b1b373f | ||
|
cc27ff9fb4 | ||
|
c17a3c6adb | ||
|
e63f490693 | ||
|
ac1d9e4f36 | ||
|
03504db53b | ||
|
aa8b420739 | ||
|
c482e11ab2 | ||
|
b38bc47b72 | ||
|
b035bcb990 | ||
|
af73cdddc5 | ||
|
86c43e5b83 | ||
|
26e9869f0b | ||
|
0431614bfe | ||
|
7e57b5eb2b | ||
|
b25adc0ca6 | ||
|
da5ae304a6 | ||
|
7849079be4 | ||
|
9bcad6fb20 | ||
|
306ed766dc | ||
|
6faf149398 | ||
|
6403d7ba4d | ||
|
d704d6f287 | ||
|
5f1d2bf19e | ||
|
1aed2d1b17 | ||
|
0d48d70c3f | ||
|
55c03addc2 | ||
|
d905caa967 | ||
|
758ad95587 | ||
|
f9b8fe8546 | ||
|
55c2418031 | ||
|
faab1b08cd | ||
|
0185e4e304 | ||
|
d6beb1826e | ||
|
c0246c0a9b | ||
|
8d6d3bcf33 | ||
|
1aaa3b875c | ||
|
76b53a6c2c | ||
|
77d7546d6d | ||
|
a2cd2339c8 | ||
|
5faf2a741b | ||
|
209b0aaa3f | ||
|
c46283cc17 | ||
|
94036467d3 | ||
|
d0437a1cb4 | ||
|
6b671fc9ab | ||
|
d10c01f111 | ||
|
64892e2e9c | ||
|
bbc69369e5 | ||
|
db46c5243c | ||
|
e8c2ba0c0e | ||
|
14d75a2c01 | ||
|
f0dd53a53f | ||
|
772d33e011 | ||
|
691ff3c8e3 | ||
|
a18d7a856d | ||
|
04a8514cba | ||
|
dc383b0999 | ||
|
fa58f794c5 | ||
|
b2eded6f1c | ||
|
c532711a5c | ||
|
61546520ef | ||
|
a5e9485db1 | ||
|
d4ef6f4e6e | ||
|
37e2c05776 | ||
|
4d63592675 | ||
|
a57c45f3b4 | ||
|
18ff062f8d | ||
|
cc21bf7abc | ||
|
31dc6cfb92 | ||
|
fa397470e2 | ||
|
f3c1d06ad6 | ||
|
1f6808e61d | ||
|
9befdfaafe | ||
|
223b160954 | ||
|
497caa3c5e | ||
|
a6c6f1612c | ||
|
14b458a9c9 | ||
|
91dfa11d20 | ||
|
1e5284c5ad | ||
|
412844ac1d | ||
|
ab2a855658 | ||
|
263a35bfcc | ||
|
9619f80341 | ||
|
7ad6d024e5 | ||
|
9abe1ffbbd | ||
|
b0d9a14459 | ||
|
e8a50ed068 | ||
|
88c76087c8 | ||
|
79022619ac | ||
|
9b3923f62c | ||
|
b9373a2187 | ||
|
db73953d3b | ||
|
43f8079f24 | ||
|
cc918842fb | ||
|
924cf57e7c | ||
|
7f9df7a08b | ||
|
bd59f511fa | ||
|
62958fa955 | ||
|
3bfadea707 | ||
|
b3e727619b | ||
|
66633ec1be | ||
|
87acca69ac | ||
|
a6d90f3506 | ||
|
7ff78a3eec | ||
|
d9f2b894bf | ||
|
30009a1d1d | ||
|
48ff65b764 | ||
|
e15b51b3bb | ||
|
a922b6534e | ||
|
d83cc69159 | ||
|
790a3c6535 | ||
|
76dec1e506 | ||
|
0ae3789bf4 | ||
|
d407323ec1 | ||
|
3ac2e3f7bc | ||
|
bcdd1f3edb | ||
|
049ff25ddc | ||
|
f41ffaabb7 | ||
|
55b4c73313 | ||
|
a2ac8e1acf | ||
|
95e81711bc | ||
|
6062767799 | ||
|
b6a636bddd | ||
|
42ac0e50a0 | ||
|
d1f177b30b | ||
|
2274fe2105 | ||
|
ffa01e253a | ||
|
ac1b537f49 | ||
|
aa300717d2 | ||
|
d8aba7a99a | ||
|
a69357d8cf | ||
|
e6787c144b | ||
|
47af24c4d9 | ||
|
dfc704db50 | ||
|
326d507800 | ||
|
55a98d7d9f | ||
|
6d63ec52a6 | ||
|
f9eead9552 | ||
|
e27df4e915 | ||
|
24ced570ed | ||
|
6c0b3b1bbe | ||
|
c65d058969 | ||
|
e8c98c4323 | ||
|
98ba9137f6 | ||
|
4e14ce5831 | ||
|
95a0cd03d0 | ||
|
747cc15ff5 | ||
|
8fe63e1651 | ||
|
b0c6cbd24d | ||
|
5ddf788a71 | ||
|
b87aa9eacd | ||
|
f75de51d9a | ||
|
a694190c4d | ||
|
58c8ca8356 | ||
|
75d974f1f1 | ||
|
3564695965 | ||
|
d4d88a1de8 | ||
|
884ca38b4a | ||
|
5b91734495 | ||
|
371ad26466 | ||
|
e42b439f14 | ||
|
c2d88ffd3e | ||
|
d0f07ea7b3 | ||
|
aa1440aada | ||
|
add19ff55f | ||
|
89453d754e | ||
|
5685065d9a | ||
|
e8ca21f21f | ||
|
bb69f7b927 | ||
|
f18b4d9df5 | ||
|
479940d62c | ||
|
416016f31e | ||
|
e09cf33644 | ||
|
18dae1447d | ||
|
52a951aed2 | ||
|
b6410720b8 | ||
|
8f78b836c6 | ||
|
7e5222fbc5 | ||
|
fb0b88350a | ||
|
ebf201c8bf | ||
|
4aadd1a582 | ||
|
3d473ceccd | ||
|
d1338aabfc | ||
|
b93bf82934 | ||
|
f54bb91274 | ||
|
1ba6bf95d2 | ||
|
44e53b140a | ||
|
a0772a3391 | ||
|
ec6ef38713 | ||
|
2d290f63da | ||
|
30c430b02a | ||
|
e8118055d5 | ||
|
a019a11946 | ||
|
2ced228f71 | ||
|
96ba8eb2d7 | ||
|
1dcfad746d | ||
|
7c714f7f13 | ||
|
5d7a50cf0a | ||
|
96490b351d | ||
|
9c49b0b4c4 | ||
|
7ecb7fa211 | ||
|
265c02e771 | ||
|
e0bdfd2803 | ||
|
b59c9099cf | ||
|
d68d862310 | ||
|
9f1181c846 | ||
|
3034642270 | ||
|
3768ed463d | ||
|
489abd66c1 | ||
|
5159a64db2 | ||
|
206b088ce8 | ||
|
ad93848e87 | ||
|
7f61f22cab | ||
|
9f1e4e0b80 | ||
|
31b7ce5488 | ||
|
5cbfd2cdbf | ||
|
8d7ccc1ecb | ||
|
204d7c46a8 | ||
|
cf8c969782 | ||
|
d89952cebc | ||
|
5facefebd9 | ||
|
69d845cf02 | ||
|
b470b42758 | ||
|
3cb8fb21ff | ||
|
ede22a7cf8 | ||
|
648ea69074 | ||
|
b5b9cdfc9e | ||
|
f70e6bf067 | ||
|
28842e042e | ||
|
a1c2dcf1c5 | ||
|
5f635a293d | ||
|
b0b1e38607 | ||
|
1d6e2ace2f | ||
|
4ce1bc511a | ||
|
aec0723735 | ||
|
562fb16ff9 | ||
|
f33fe5dda1 | ||
|
3a2420389b | ||
|
0a9413839a | ||
|
97b24b9878 | ||
|
2c1f8b1d7f | ||
|
b047064568 | ||
|
4878051c30 | ||
|
32ecd07c27 | ||
|
085ee735dc | ||
|
8e5fd53926 | ||
|
b179fb5ed1 | ||
|
394c62cd36 | ||
|
86925fd5d5 | ||
|
0891299330 | ||
|
95b987feb6 | ||
|
c32df7ca97 | ||
|
14ee662e25 | ||
|
7f0cd872e6 | ||
|
445152b164 | ||
|
c78cb61ef1 | ||
|
a81b7052cc | ||
|
6124a6aedc | ||
|
cfbe729321 | ||
|
58170cd3eb | ||
|
66fba21722 | ||
|
d91b0517b1 | ||
|
6aecc5de6c | ||
|
8f7f6694da | ||
|
125c50fded | ||
|
9830fdb137 | ||
|
f8b578ee53 | ||
|
db554b2926 | ||
|
497ccdec8a | ||
|
bb507e4578 | ||
|
8806c4ff46 | ||
|
0d517cda5f | ||
|
6d889e20fc | ||
|
fdabbc1b29 | ||
|
2690c6f972 | ||
|
3a30518104 | ||
|
d0df9abfb0 | ||
|
e641735706 | ||
|
ed64a91de4 | ||
|
49e47274d5 | ||
|
43343e971f | ||
|
a928ee53ad | ||
|
fea22ff5eb | ||
|
7bc86911b9 | ||
|
004a43416f | ||
|
c926020259 | ||
|
1842d58246 | ||
|
2d4a8789c5 | ||
|
58e4276e4b | ||
|
2d8c3619d8 | ||
|
790d8d3b18 | ||
|
1e53edf46a | ||
|
b7df22ccde | ||
|
b166a80c27 | ||
|
0e4a05242d | ||
|
86767d2dbd | ||
|
bbcc7389f4 | ||
|
813ebe709f | ||
|
c28a0ec328 | ||
|
5520bf4171 | ||
|
70487dc017 | ||
|
4524cfafdc | ||
|
94cbe2e875 | ||
|
4f73743aa0 | ||
|
ad94eab506 | ||
|
0aac991a7b | ||
|
04a698f442 | ||
|
14e872b4b1 | ||
|
5e95e70293 | ||
|
3a52825d57 | ||
|
b1d9256e7c | ||
|
5a4468bc25 | ||
|
a919c4d177 | ||
|
a516a9da3c | ||
|
21bbebaf1c | ||
|
882ded46c1 | ||
|
e3c1091de8 | ||
|
50b6e2841d | ||
|
3475865928 | ||
|
1a529e3b18 | ||
|
faee9997e8 | ||
|
16257218d3 | ||
|
42da55ecf9 | ||
|
78b51aff85 | ||
|
ec4ac456f9 | ||
|
22495617d2 | ||
|
e262523d08 | ||
|
a267870cfd | ||
|
151d224b81 | ||
|
05b9425ea9 | ||
|
9933b8f2d6 | ||
|
2b9b798d07 | ||
|
209f07cb01 | ||
|
e7ccce1c8c | ||
|
8aed4aa23c | ||
|
5dddbfb1fd | ||
|
a81b61a76f | ||
|
70f3eb3962 | ||
|
3cb26e8d4f | ||
|
f64828cb75 | ||
|
3ed12d9e42 | ||
|
622da2b3aa | ||
|
6f779ae7e8 | ||
|
29bfde444b | ||
|
194f06be24 | ||
|
029dda261e | ||
|
de55e23f98 | ||
|
a8b9125d4a | ||
|
db6766e3c5 | ||
|
55f78ee269 | ||
|
daf48b7414 | ||
|
331528fc76 | ||
|
c3dc7fd43d | ||
|
86fe2603b8 | ||
|
0efca1c878 | ||
|
f87ea6e636 | ||
|
264492a4a8 | ||
|
e966cc6e5a | ||
|
91df303b86 | ||
|
51f115d43e | ||
|
538a794bdd | ||
|
e61e92b9ee | ||
|
2ce28e9a81 | ||
|
11011acf43 | ||
|
354626c80a | ||
|
41c3e49e67 | ||
|
8519c11e98 | ||
|
8dbca3cc42 | ||
|
006824f4ed | ||
|
adcb73704a | ||
|
179fef4cbc | ||
|
8ae879e4d5 | ||
|
ddc5bd7dbe | ||
|
751d64d9ed | ||
|
886bb3880f | ||
|
01ba2af1ef | ||
|
faea5fd748 | ||
|
21a92c6eb3 | ||
|
bbfcc2539f | ||
|
706468e70f | ||
|
ec7c74442b | ||
|
c269074ada | ||
|
1360e78019 | ||
|
1f0537f7d6 | ||
|
7f17195482 | ||
|
4a9405d216 | ||
|
9fc7d7e4c1 | ||
|
ba81f4c243 | ||
|
cd055241bf | ||
|
d55a9425c5 | ||
|
f7e80bf4ce | ||
|
accd07fa20 | ||
|
62a70cee57 | ||
|
d7c23bc2e8 | ||
|
e778d39935 | ||
|
b9cf9347dc | ||
|
2343a4e98f | ||
|
7fa58f2009 | ||
|
b47fb2922f | ||
|
9ae912b2c8 | ||
|
2e24f7005c | ||
|
3254837891 | ||
|
6411855dce | ||
|
539ab74462 | ||
|
71c101b0eb | ||
|
b85e0a18df | ||
|
74c8201857 | ||
|
82055b63b4 | ||
|
c683c444e5 | ||
|
737981f9aa | ||
|
b9e54dbe0f | ||
|
f739002d0b | ||
|
ce861bbc14 | ||
|
217f76c57e | ||
|
8dccfc6117 | ||
|
50da50306e | ||
|
3552d345d6 | ||
|
7d277ef563 | ||
|
41afcc86e7 | ||
|
3e4628c35f | ||
|
5b4c6251b0 | ||
|
a461c234cb | ||
|
ddc41641b2 | ||
|
121080f7e2 | ||
|
419ba4eaa4 | ||
|
2d535cbd93 | ||
|
53fa18eee6 | ||
|
a82f3d7a50 | ||
|
d5b6b9ce5b | ||
|
49efcc321f | ||
|
3731010e26 | ||
|
6611bd49bc | ||
|
d6d86dd043 | ||
|
862c98344f | ||
|
37542f953a | ||
|
401d712987 | ||
|
6db4a1d697 | ||
|
692199bd08 | ||
|
198546900c | ||
|
c3e5b69507 | ||
|
8c5701f8ea | ||
|
20eee87486 | ||
|
78de603906 | ||
|
1f69367509 | ||
|
8eaebc246c | ||
|
1ad9b6b235 | ||
|
84e7c8df0e | ||
|
cb03cd2eb9 | ||
|
d2ce9aad20 | ||
|
7d12ed5ddb | ||
|
65b818e1af | ||
|
73f7473d32 | ||
|
dddc8697e0 | ||
|
98883a42e7 | ||
|
a302f8e734 | ||
|
1507503f68 | ||
|
fa8c3f1674 | ||
|
deb794faad | ||
|
d9a1be15e9 | ||
|
da851efb64 | ||
|
db7b0940d8 | ||
|
825c86d4c4 | ||
|
00530a1352 | ||
|
9e6e0d3e62 | ||
|
6f56137fe4 | ||
|
1ea8b6c24c | ||
|
1089686d65 | ||
|
9857e66424 | ||
|
cef0152af8 | ||
|
87baca6cf5 | ||
|
67f5c0b580 | ||
|
14ffe542f7 | ||
|
ed6a9579b6 | ||
|
aad642b1a7 | ||
|
223155a40e | ||
|
ceba00bd5d | ||
|
6ad87c114a | ||
|
c2caf12c1a | ||
|
9f89200169 | ||
|
7ff6f56701 | ||
|
53d4244e97 | ||
|
0522003c4c | ||
|
161acded12 | ||
|
f96338ea2a | ||
|
badabdd79c | ||
|
53397635da | ||
|
66e15342bd | ||
|
07e898f26b | ||
|
54d4813c98 | ||
|
67054db3b0 | ||
|
d3d31a719b | ||
|
8eaa62b30b | ||
|
d197c18573 | ||
|
bc4f92bc83 | ||
|
2f3ecbe3f0 | ||
|
97bc3bc736 | ||
|
0543c6dbd6 | ||
|
6034a5b0a1 | ||
|
04a61c25be | ||
|
05783a21f8 | ||
|
101c55f10b | ||
|
703f2cb0c3 | ||
|
618d5f6f9d | ||
|
835b9f6037 | ||
|
9629bfba9a | ||
|
c2c1179c1c | ||
|
241d2ed9ea | ||
|
dbeab13512 | ||
|
09692fc5f1 | ||
|
9c0e62bd5f | ||
|
bfae62f840 | ||
|
ca5ec52b71 | ||
|
176c647bdf | ||
|
647c8fe6e1 | ||
|
4f7ba6624d | ||
|
44dfe35f02 | ||
|
fbf2536168 | ||
|
61de2b9db6 | ||
|
c342bd9606 | ||
|
5918164275 | ||
|
5bd3b864a6 | ||
|
62366a2c69 | ||
|
a290789179 | ||
|
f8886065ad | ||
|
d54f03d8b4 | ||
|
87d346e6f5 | ||
|
6007c76efa | ||
|
c4fbe32533 | ||
|
a13e6a2bf9 | ||
|
bc2cd52f70 | ||
|
cc64429c76 | ||
|
e0fa90659a | ||
|
12b519aedc | ||
|
e5a9bdcddb | ||
|
a2a6d0e7fa | ||
|
51a004ebc2 | ||
|
c1fdbe3e02 | ||
|
48cbbac378 | ||
|
264759385f | ||
|
99245ddeac | ||
|
cd18a2328b | ||
|
683146fba1 | ||
|
000a0dda93 | ||
|
59ed57fa41 | ||
|
2e4c9b66f6 | ||
|
3aa840d012 | ||
|
a49d732b6e | ||
|
c64401d00b | ||
|
b0941f3dc2 | ||
|
71248dea7e | ||
|
60e142a928 | ||
|
53f6a1a9c3 | ||
|
1e4fa3dab1 | ||
|
3bcf5cc467 | ||
|
a8716e5509 | ||
|
1dd86c34d2 | ||
|
62394118de | ||
|
c3dc7bb2ec | ||
|
32ee0dcd7f | ||
|
990a28d2da | ||
|
66d532207d | ||
|
67e2f47cc2 | ||
|
0b9e64798e | ||
|
0937c2eeaf | ||
|
11fc5adf9d | ||
|
d37e86263c | ||
|
c87a3842d4 | ||
|
ef8453a07c | ||
|
a09b8369dc | ||
|
0098b041c0 | ||
|
bbab11d606 | ||
|
031d5e1c56 | ||
|
1c301a412b | ||
|
c252c0ec8a | ||
|
3cbff597d3 | ||
|
f033ad4730 | ||
|
6220abd088 | ||
|
a43e0896a6 | ||
|
7739157945 | ||
|
403ea34529 | ||
|
4d266f42b6 | ||
|
d16bd74cc2 | ||
|
c438392a6c | ||
|
3313836ae9 | ||
|
5d8c4be5b3 | ||
|
4f9660213f | ||
|
b7eabb95f7 | ||
|
415b82c89b | ||
|
43c3284927 | ||
|
9a8649b6d6 | ||
|
d43d1f3a9f | ||
|
f3c927c451 | ||
|
57dfcfcb11 | ||
|
a47c6066fa | ||
|
bf5f00ceac | ||
|
06c4f3921c | ||
|
ab2f7bc976 | ||
|
42d1c8710a | ||
|
14621781c4 | ||
|
2fe15f5d35 | ||
|
b5eb49a058 | ||
|
ca2c65edf2 | ||
|
67052df2d2 | ||
|
ba31dbf39a | ||
|
366e2750e4 | ||
|
dc978aba3c | ||
|
6d26256062 | ||
|
4715140d65 | ||
|
2db6886d74 | ||
|
0f41196129 | ||
|
67d619d780 |
9
.editorconfig
Normal file
@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
46
.github/CODE_OF_CONDUCT.md
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer at giuffrida.mattia AT gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
79
.github/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
## Contributing
|
||||
|
||||
In Faraday we always welcome new ideas and features, however we also have to ensure
|
||||
that the overall code quality stays on reasonable levels.
|
||||
For this reason, before adding any contribution to Faraday, we highly recommend reading this
|
||||
quick guide to ensure your PR can be reviewed and approved as quickly as possible.
|
||||
|
||||
We are past our 1.0 release, and follow [Semantic Versioning][semver]. If your
|
||||
patch includes changes that break compatibility, note that in the Pull Request, so we can add it to
|
||||
the [Changelog][].
|
||||
|
||||
|
||||
### Policy on inclusive language
|
||||
|
||||
You have read our [Code of Conduct][], which includes a note about **inclusive language**. This section tries to make that actionable.
|
||||
|
||||
Faraday has a large and diverse userbase. To make Faraday a pleasant and effective experience for everyone, we use inclusive language.
|
||||
|
||||
These resources can help:
|
||||
|
||||
- Google's tutorial [Writing inclusive documentation](https://developers.google.com/style/inclusive-documentation) teaches by example, how to reword non-inclusive things.
|
||||
- Linux kernel mailing list's [Coding Style: Inclusive Terminology](https://lkml.org/lkml/2020/7/4/229) said "Add no new instances of non-inclusive words, here is a list of words not include new ones of."
|
||||
- Linguistic Society of America published [Guidelines for Inclusive Language](https://www.linguisticsociety.org/resource/guidelines-inclusive-language) which concluded: "We encourage all linguists to consider the possible reactions of their potential audience to their writing and, in so doing, to choose expository practices and content that is positive, inclusive, and respectful."
|
||||
|
||||
This project attempts to improve in these areas. Join us in doing that important work.
|
||||
|
||||
If you want to privately raise any breach to this policy with the Faraday team, feel free to reach out to [@iMacTia](https://ruby.social/@iMacTia) and [@olleolleolle](https://ruby.social/@olleolleolle) on the Mastodon instance ruby.social.
|
||||
|
||||
|
||||
### Required Checks
|
||||
|
||||
Before pushing your code and opening a PR, we recommend you run the following checks to avoid
|
||||
our GitHub Actions Workflow to block your contribution.
|
||||
|
||||
```bash
|
||||
# Run unit tests and check code coverage
|
||||
$ bundle exec rspec
|
||||
|
||||
# Check code style
|
||||
$ bundle exec rubocop
|
||||
```
|
||||
|
||||
|
||||
### New Features
|
||||
|
||||
When adding a feature in Faraday:
|
||||
|
||||
1. also add tests to cover your new feature.
|
||||
2. if the feature is for an adapter, the **attempt** must be made to add the same feature to all other adapters as well.
|
||||
3. start opening an issue describing how the new feature will work, and only after receiving
|
||||
the green light by the core team start working on the PR.
|
||||
|
||||
|
||||
### New Middleware & Adapters
|
||||
|
||||
We prefer new adapters and middlewares to be added **as separate gems**. We can link to such gems from this project.
|
||||
|
||||
This goes for the [faraday_middleware][] project as well.
|
||||
|
||||
We encourage adapters that:
|
||||
|
||||
1. support SSL & streaming;
|
||||
1. are proven and may have better performance than existing ones; or
|
||||
1. have features not present in included adapters.
|
||||
|
||||
|
||||
### Changes to the Faraday Docs
|
||||
|
||||
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.
|
||||
|
||||
|
||||
[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
|
18
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
## Basic Info
|
||||
|
||||
* Faraday Version:
|
||||
* Ruby Version:
|
||||
|
||||
## Issue description
|
||||
Please provide a description of the issue you're experiencing.
|
||||
Please also provide the exception message/stacktrace or any other useful detail.
|
||||
|
||||
## Steps to reproduce
|
||||
If possible, please provide the steps to reproduce the issue.
|
||||
|
||||
## CHECKLIST (delete before creating the issue)
|
||||
* If you're not reporting a bug/issue, you can ignore this whole template.
|
||||
* Are you using the latest Faraday version? If not, please check the [Releases](https://github.com/lostisland/faraday/releases) page to see if the issue has already been fixed.
|
||||
* Provide the Faraday and Ruby Version you're using while experiencing the issue.
|
||||
* Fill the `Issue description` and `Steps to Reproduce` sections.
|
||||
* Delete this checklist before posting the issue.
|
11
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
## Description
|
||||
A few sentences describing the overall goals of the pull request's commits.
|
||||
Link to related issues if any. (As `Fixes #XXX`)
|
||||
|
||||
## Todos
|
||||
List any remaining work that needs to be done, i.e:
|
||||
- [ ] Tests
|
||||
- [ ] Documentation
|
||||
|
||||
## Additional Notes
|
||||
Optional section
|
14
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "bundler"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: "npm"
|
||||
directory: /
|
||||
schedule:
|
||||
interval: "weekly"
|
69
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
push:
|
||||
branches: [ main, 1.x, 0.1x ]
|
||||
|
||||
env:
|
||||
GIT_COMMIT_SHA: ${{ github.sha }}
|
||||
GIT_BRANCH: ${{ github.ref }}
|
||||
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
|
||||
|
||||
permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
linting:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
BUNDLE_WITH: lint
|
||||
BUNDLE_WITHOUT: development:test
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Ruby 3.x
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 3
|
||||
bundler-cache: true
|
||||
|
||||
- name: Rubocop
|
||||
run: bundle exec rubocop --format progress
|
||||
|
||||
- name: Yard-Junk
|
||||
run: bundle exec yard-junk --path lib
|
||||
|
||||
build:
|
||||
needs: [ linting ]
|
||||
runs-on: ubuntu-latest
|
||||
name: build ${{ matrix.ruby }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ruby: [ '3.0', '3.1', '3.2', '3.3', '3.4' ]
|
||||
experimental: [false]
|
||||
include:
|
||||
- ruby: head
|
||||
experimental: true
|
||||
- ruby: truffleruby-head
|
||||
experimental: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
bundler-cache: true
|
||||
|
||||
- name: RSpec
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
run: bundle exec rake
|
||||
|
||||
- name: Test External Adapters
|
||||
continue-on-error: ${{ matrix.experimental }}
|
||||
run: bundle exec bake test:external
|
||||
|
||||
|
26
.github/workflows/publish.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Publish to Rubygems
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Ruby 3.x
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
ruby-version: 3
|
||||
|
||||
- name: Publish to RubyGems
|
||||
uses: rubygems/release-gem@v1
|
15
.github/workflows/refresh_team_page.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
name: Refresh Team Page
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
build:
|
||||
name: Refresh Contributors Stats
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Call GitHub API
|
||||
run: |
|
||||
curl "https://api.github.com/repos/${{ github.repository }}/stats/contributors"
|
39
.gitignore
vendored
@ -1,21 +1,28 @@
|
||||
## MAC OS
|
||||
.DS_Store
|
||||
|
||||
## TEXTMATE
|
||||
*.tmproj
|
||||
tmtags
|
||||
|
||||
## EMACS
|
||||
*~
|
||||
\#*
|
||||
.\#*
|
||||
|
||||
## VIM
|
||||
*.swp
|
||||
|
||||
## PROJECT::GENERAL
|
||||
coverage
|
||||
rdoc
|
||||
pkg
|
||||
doc
|
||||
log
|
||||
pkg/*
|
||||
tmp
|
||||
.rvmrc
|
||||
.ruby-version
|
||||
.yardoc
|
||||
.DS_Store
|
||||
|
||||
## BUNDLER
|
||||
*.gem
|
||||
.bundle
|
||||
Gemfile.lock
|
||||
vendor/bundle
|
||||
external
|
||||
|
||||
## NPM
|
||||
node_modules
|
||||
|
||||
## PROJECT::SPECIFIC
|
||||
.rbx
|
||||
|
||||
## IDEs
|
||||
.idea/
|
||||
.yardoc/
|
||||
|
200
.rubocop.yml
Normal file
@ -0,0 +1,200 @@
|
||||
inherit_from: .rubocop_todo.yml
|
||||
|
||||
require:
|
||||
- rubocop-packaging
|
||||
- rubocop-performance
|
||||
|
||||
AllCops:
|
||||
DisplayCopNames: true
|
||||
DisplayStyleGuide: true
|
||||
TargetRubyVersion: 3.0
|
||||
|
||||
# Custom config
|
||||
Gemspec/RequireMFA: # we don't know if this works with auto-deployments yet
|
||||
Enabled: false
|
||||
Layout/LineLength:
|
||||
Exclude:
|
||||
- spec/**/*.rb
|
||||
- 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/**/*'
|
||||
- 'examples/**/*'
|
||||
Style/DoubleNegation:
|
||||
Enabled: false
|
||||
Style/IfUnlessModifier:
|
||||
Enabled: false
|
||||
|
||||
# New cops
|
||||
Gemspec/DeprecatedAttributeAssignment: # new in 1.30
|
||||
Enabled: true
|
||||
Layout/LineContinuationLeadingSpace: # new in 1.31
|
||||
Enabled: true
|
||||
Layout/LineContinuationSpacing: # new in 1.31
|
||||
Enabled: true
|
||||
Layout/LineEndStringConcatenationIndentation: # new in 1.18
|
||||
Enabled: true
|
||||
Layout/SpaceBeforeBrackets: # new in 1.7
|
||||
Enabled: true
|
||||
Lint/AmbiguousAssignment: # new in 1.7
|
||||
Enabled: true
|
||||
Lint/AmbiguousOperatorPrecedence: # new in 1.21
|
||||
Enabled: true
|
||||
Lint/AmbiguousRange: # new in 1.19
|
||||
Enabled: true
|
||||
Lint/ConstantOverwrittenInRescue: # new in 1.31
|
||||
Enabled: true
|
||||
Lint/DeprecatedConstants: # new in 1.8
|
||||
Enabled: true
|
||||
Lint/DuplicateBranch: # new in 1.3
|
||||
Enabled: true
|
||||
Lint/DuplicateRegexpCharacterClassElement: # new in 1.1
|
||||
Enabled: true
|
||||
Lint/EmptyBlock: # new in 1.1
|
||||
Enabled: true
|
||||
Lint/EmptyClass: # new in 1.3
|
||||
Enabled: true
|
||||
Lint/EmptyInPattern: # new in 1.16
|
||||
Enabled: true
|
||||
Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21
|
||||
Enabled: true
|
||||
Lint/LambdaWithoutLiteralBlock: # new in 1.8
|
||||
Enabled: true
|
||||
Lint/NoReturnInBeginEndBlocks: # new in 1.2
|
||||
Enabled: true
|
||||
Lint/NonAtomicFileOperation: # new in 1.31
|
||||
Enabled: true
|
||||
Lint/NumberedParameterAssignment: # new in 1.9
|
||||
Enabled: true
|
||||
Lint/OrAssignmentToConstant: # new in 1.9
|
||||
Enabled: true
|
||||
Lint/RedundantDirGlobSort: # new in 1.8
|
||||
Enabled: true
|
||||
Lint/RefinementImportMethods: # new in 1.27
|
||||
Enabled: true
|
||||
Lint/RequireRangeParentheses: # new in 1.32
|
||||
Enabled: true
|
||||
Lint/RequireRelativeSelfPath: # new in 1.22
|
||||
Enabled: true
|
||||
Lint/SymbolConversion: # new in 1.9
|
||||
Enabled: true
|
||||
Lint/ToEnumArguments: # new in 1.1
|
||||
Enabled: true
|
||||
Lint/TripleQuotes: # new in 1.9
|
||||
Enabled: true
|
||||
Lint/UnexpectedBlockArity: # new in 1.5
|
||||
Enabled: true
|
||||
Lint/UnmodifiedReduceAccumulator: # new in 1.1
|
||||
Enabled: true
|
||||
Lint/UselessRuby2Keywords: # new in 1.23
|
||||
Enabled: true
|
||||
Naming/BlockForwarding: # new in 1.24
|
||||
Enabled: true
|
||||
Security/CompoundHash: # new in 1.28
|
||||
Enabled: true
|
||||
Security/IoMethods: # new in 1.22
|
||||
Enabled: true
|
||||
Style/ArgumentsForwarding: # new in 1.1
|
||||
Enabled: true
|
||||
Style/CollectionCompact: # new in 1.2
|
||||
Enabled: true
|
||||
Style/DocumentDynamicEvalDefinition: # new in 1.1
|
||||
Enabled: true
|
||||
Style/EmptyHeredoc: # new in 1.32
|
||||
Enabled: true
|
||||
Style/EndlessMethod: # new in 1.8
|
||||
Enabled: true
|
||||
Style/EnvHome: # new in 1.29
|
||||
Enabled: true
|
||||
Style/FetchEnvVar: # new in 1.28
|
||||
Enabled: true
|
||||
Style/FileRead: # new in 1.24
|
||||
Enabled: true
|
||||
Style/FileWrite: # new in 1.24
|
||||
Enabled: true
|
||||
Style/HashConversion: # new in 1.10
|
||||
Enabled: true
|
||||
Style/HashExcept: # new in 1.7
|
||||
Enabled: true
|
||||
Style/IfWithBooleanLiteralBranches: # new in 1.9
|
||||
Enabled: true
|
||||
Style/InPatternThen: # new in 1.16
|
||||
Enabled: true
|
||||
Style/MapCompactWithConditionalBlock: # new in 1.30
|
||||
Enabled: true
|
||||
Style/MapToHash: # new in 1.24
|
||||
Enabled: true
|
||||
Style/MultilineInPatternThen: # new in 1.16
|
||||
Enabled: true
|
||||
Style/NegatedIfElseCondition: # new in 1.2
|
||||
Enabled: true
|
||||
Style/NestedFileDirname: # new in 1.26
|
||||
Enabled: true
|
||||
Style/NilLambda: # new in 1.3
|
||||
Enabled: true
|
||||
Style/NumberedParameters: # new in 1.22
|
||||
Enabled: true
|
||||
Style/NumberedParametersLimit: # new in 1.22
|
||||
Enabled: true
|
||||
Style/ObjectThen: # new in 1.28
|
||||
Enabled: true
|
||||
Style/OpenStructUse: # new in 1.23
|
||||
Enabled: true
|
||||
Style/QuotedSymbols: # new in 1.16
|
||||
Enabled: true
|
||||
Style/RedundantArgument: # new in 1.4
|
||||
Enabled: true
|
||||
Style/RedundantInitialize: # new in 1.27
|
||||
Enabled: true
|
||||
Style/RedundantSelfAssignmentBranch: # new in 1.19
|
||||
Enabled: true
|
||||
Style/SelectByRegexp: # new in 1.22
|
||||
Enabled: true
|
||||
Style/StringChars: # new in 1.12
|
||||
Enabled: true
|
||||
Style/SwapValues: # new in 1.1
|
||||
Enabled: true
|
||||
Performance/AncestorsInclude: # new in 1.7
|
||||
Enabled: true
|
||||
Performance/BigDecimalWithNumericArgument: # new in 1.7
|
||||
Enabled: true
|
||||
Performance/BlockGivenWithExplicitBlock: # new in 1.9
|
||||
Enabled: true
|
||||
Performance/CollectionLiteralInLoop: # new in 1.8
|
||||
Enabled: true
|
||||
Performance/ConcurrentMonotonicTime: # new in 1.12
|
||||
Enabled: true
|
||||
Performance/ConstantRegexp: # new in 1.9
|
||||
Enabled: true
|
||||
Performance/MapCompact: # new in 1.11
|
||||
Enabled: true
|
||||
Performance/MethodObjectAsBlock: # new in 1.9
|
||||
Enabled: true
|
||||
Performance/RedundantEqualityComparisonBlock: # new in 1.10
|
||||
Enabled: true
|
||||
Performance/RedundantSortBlock: # new in 1.7
|
||||
Enabled: true
|
||||
Performance/RedundantSplitRegexpArgument: # new in 1.10
|
||||
Enabled: true
|
||||
Performance/RedundantStringChars: # new in 1.7
|
||||
Enabled: true
|
||||
Performance/ReverseFirst: # new in 1.7
|
||||
Enabled: true
|
||||
Performance/SortReverse: # new in 1.7
|
||||
Enabled: true
|
||||
Performance/Squeeze: # new in 1.7
|
||||
Enabled: true
|
||||
Performance/StringIdentifierArgument: # new in 1.13
|
||||
Enabled: true
|
||||
Performance/StringInclude: # new in 1.7
|
||||
Enabled: true
|
||||
Performance/Sum: # new in 1.8
|
||||
Enabled: true
|
71
.rubocop_todo.yml
Normal file
@ -0,0 +1,71 @@
|
||||
# This configuration was generated by
|
||||
# `rubocop --auto-gen-config`
|
||||
# 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
|
||||
# versions of RuboCop, may require this file to be generated again.
|
||||
|
||||
# Offense count: 6
|
||||
# Configuration parameters: AllowedMethods.
|
||||
# AllowedMethods: enums
|
||||
Lint/ConstantDefinitionInBlock:
|
||||
Exclude:
|
||||
- 'spec/faraday/options/options_spec.rb'
|
||||
- 'spec/faraday/rack_builder_spec.rb'
|
||||
- 'spec/faraday/request/instrumentation_spec.rb'
|
||||
|
||||
# Offense count: 11
|
||||
# Configuration parameters: AllowComments, AllowEmptyLambdas.
|
||||
Lint/EmptyBlock:
|
||||
Exclude:
|
||||
- 'spec/faraday/connection_spec.rb'
|
||||
- 'spec/faraday/rack_builder_spec.rb'
|
||||
- 'spec/faraday/response_spec.rb'
|
||||
|
||||
# Offense count: 13
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
|
||||
Metrics/AbcSize:
|
||||
Max: 42
|
||||
|
||||
# Offense count: 3
|
||||
# Configuration parameters: CountComments, CountAsOne.
|
||||
Metrics/ClassLength:
|
||||
Max: 230
|
||||
|
||||
# Offense count: 9
|
||||
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
||||
Metrics/CyclomaticComplexity:
|
||||
Max: 13
|
||||
|
||||
# Offense count: 27
|
||||
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
||||
Metrics/MethodLength:
|
||||
Max: 33
|
||||
|
||||
# Offense count: 1
|
||||
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
|
||||
Metrics/ParameterLists:
|
||||
Max: 6
|
||||
|
||||
# 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:
|
||||
- 'lib/faraday/connection.rb'
|
||||
- 'lib/faraday/options.rb'
|
12
.yardopts
Normal file
@ -0,0 +1,12 @@
|
||||
--no-private
|
||||
--exclude test
|
||||
--exclude .github
|
||||
--exclude coverage
|
||||
--exclude doc
|
||||
--exclude script
|
||||
--markup markdown
|
||||
--readme README.md
|
||||
|
||||
lib/**/*.rb
|
||||
-
|
||||
CHANGELOG.md
|
574
CHANGELOG.md
Normal file
@ -0,0 +1,574 @@
|
||||
# Faraday Changelog
|
||||
|
||||
## The changelog has moved!
|
||||
|
||||
This file is not being updated anymore. Instead, please check the [Releases](https://github.com/lostisland/faraday/releases) page.
|
||||
|
||||
## [2.2.0](https://github.com/lostisland/faraday/compare/v2.1.0...v2.2.0) (2022-02-03)
|
||||
|
||||
* Reintroduce the possibility to register middleware with symbols, strings or procs in [#1391](https://github.com/lostisland/faraday/pull/1391)
|
||||
|
||||
## [2.1.0](https://github.com/lostisland/faraday/compare/v2.0.1...v2.1.0) (2022-01-15)
|
||||
|
||||
* Fix test adapter thread safety by @iMacTia in [#1380](https://github.com/lostisland/faraday/pull/1380)
|
||||
* Add default adapter options by @hirasawayuki in [#1382](https://github.com/lostisland/faraday/pull/1382)
|
||||
* CI: Add Ruby 3.1 to matrix by @petergoldstein in [#1374](https://github.com/lostisland/faraday/pull/1374)
|
||||
* docs: fix regex pattern in logger.md examples by @hirasawayuki in [#1378](https://github.com/lostisland/faraday/pull/1378)
|
||||
|
||||
## [2.0.1](https://github.com/lostisland/faraday/compare/v2.0.0...v2.0.1) (2022-01-05)
|
||||
|
||||
* Re-add `faraday-net_http` as default adapter by @iMacTia in [#1366](https://github.com/lostisland/faraday/pull/1366)
|
||||
* Updated sample format in UPGRADING.md by @vimutter in [#1361](https://github.com/lostisland/faraday/pull/1361)
|
||||
* docs: Make UPGRADING examples more copyable by @olleolleolle in [#1363](https://github.com/lostisland/faraday/pull/1363)
|
||||
|
||||
## [2.0.0](https://github.com/lostisland/faraday/compare/v1.8.0...v2.0.0) (2022-01-04)
|
||||
|
||||
The next major release is here, and it comes almost 2 years after the release of v1.0!
|
||||
|
||||
This release changes the way you use Faraday and embraces a new paradigm of Faraday as an ecosystem, rather than a library.
|
||||
|
||||
What does that mean? It means that Faraday is less of a bundled tool and more of a framework for the community to build on top of.
|
||||
|
||||
As a result, all adapters and some middleware have moved out and are now shipped as standalone gems 🙌!
|
||||
|
||||
But this doesn't mean that upgrading from Faraday 1.x to Faraday 2.0 should be hard, in fact we've listed everything you need to do in the [UPGRADING.md](https://github.com/lostisland/faraday/blob/main/UPGRADING.md) doc.
|
||||
|
||||
Moreover, we've setup a new [awesome-faraday](https://github.com/lostisland/awesome-faraday) repository that will showcase a curated list of adapters and middleware 😎.
|
||||
|
||||
This release was the result of the efforts of the core team and all the contributors, new and old, that have helped achieve this milestone 👏.
|
||||
|
||||
## What's Changed
|
||||
|
||||
* Autoloading, dependency loading and middleware registry cleanup by @iMacTia in [#1301](https://github.com/lostisland/faraday/pull/1301)
|
||||
* Move JSON middleware (request and response) from faraday_middleware by @iMacTia in [#1300](https://github.com/lostisland/faraday/pull/1300)
|
||||
* Remove deprecated `Faraday::Request#method` by @olleolleolle in [#1303](https://github.com/lostisland/faraday/pull/1303)
|
||||
* Remove deprecated `Faraday::UploadIO` by @iMacTia in [#1307](https://github.com/lostisland/faraday/pull/1307)
|
||||
* [1.x] Deprecate Authorization helpers in `Faraday::Connection` by @iMacTia in [#1306](https://github.com/lostisland/faraday/pull/1306)
|
||||
* Drop deprecated auth helpers from Connection and refactor auth middleware by @iMacTia in [#1308](https://github.com/lostisland/faraday/pull/1308)
|
||||
* Add Faraday 1.x examples in authentication.md docs by @iMacTia in [#1320](https://github.com/lostisland/faraday/pull/1320)
|
||||
* Fix passing a URL with embedded basic auth by @iMacTia in [#1324](https://github.com/lostisland/faraday/pull/1324)
|
||||
* Register JSON middleware by @mollerhoj in [#1331](https://github.com/lostisland/faraday/pull/1331)
|
||||
* Retry middleware should handle string exception class name consistently by @jrochkind in [#1334](https://github.com/lostisland/faraday/pull/1334)
|
||||
* Improve request info in exceptions raised by RaiseError Middleware by @willianzocolau in [#1335](https://github.com/lostisland/faraday/pull/1335)
|
||||
* Remove net-http adapter and update docs by @iMacTia in [#1336](https://github.com/lostisland/faraday/pull/1336)
|
||||
* Explain plan for faraday_middleware in UPGRADING.md by @iMacTia in [#1339](https://github.com/lostisland/faraday/pull/1339)
|
||||
* Scripts folder cleanup by @iMacTia in [#1340](https://github.com/lostisland/faraday/pull/1340)
|
||||
* Replace `Hash#merge` with `Utils#deep_merge` for connection options by @xkwd in [#1343](https://github.com/lostisland/faraday/pull/1343)
|
||||
* Callable authorizers by @sled in [#1345](https://github.com/lostisland/faraday/pull/1345)
|
||||
* Default value for exc error by @DariuszMusielak in [#1351](https://github.com/lostisland/faraday/pull/1351)
|
||||
* Don't call `retry_block` unless a retry is going to happen by @jrochkind in [#1350](https://github.com/lostisland/faraday/pull/1350)
|
||||
* Improve documentation for v2 by @iMacTia in [#1353](https://github.com/lostisland/faraday/pull/1353)
|
||||
* Remove default `default_adapter` (yes, you read that right) by @iMacTia in [#1354](https://github.com/lostisland/faraday/pull/1354)
|
||||
* Remove retry middleware by @iMacTia in [#1356](https://github.com/lostisland/faraday/pull/1356)
|
||||
* Remove multipart middleware and all its documentation and tests by @iMacTia in [#1357](https://github.com/lostisland/faraday/pull/1357)
|
||||
|
||||
## [1.9.3](https://github.com/lostisland/faraday/compare/v1.9.2...v1.9.3) (2022-01-06)
|
||||
|
||||
* Re-add support for Ruby 2.4+ by @iMacTia in [#1371](https://github.com/lostisland/faraday/pull/1371)
|
||||
|
||||
## [1.9.2](https://github.com/lostisland/faraday/compare/v1.9.1...v1.9.2) (2022-01-06)
|
||||
|
||||
* Add alias with legacy name to gemified middleware by @iMacTia in [#1372](https://github.com/lostisland/faraday/pull/1372)
|
||||
|
||||
## [1.9.1](https://github.com/lostisland/faraday/compare/v1.9.0...v1.9.1) (2022-01-06)
|
||||
|
||||
* Update adapter dependencies in Gemspec by @iMacTia in [#1370](https://github.com/lostisland/faraday/pull/1370)
|
||||
|
||||
## [1.9.0](https://github.com/lostisland/faraday/compare/v1.8.0...v1.9.0) (2022-01-06)
|
||||
|
||||
* Use external multipart and retry middleware by @iMacTia in [#1367](https://github.com/lostisland/faraday/pull/1367)
|
||||
|
||||
## [1.8.0](https://github.com/lostisland/faraday/releases/tag/v1.8.0) (2021-09-18)
|
||||
|
||||
### Features
|
||||
|
||||
* Backport authorization procs (#1322, @jarl-dk)
|
||||
|
||||
## [v1.7.0](https://github.com/lostisland/faraday/releases/tag/v1.7.0) (2021-08-09)
|
||||
|
||||
### Features
|
||||
|
||||
* Add strict_mode to Test::Stubs (#1298, @yykamei)
|
||||
|
||||
## [v1.6.0](https://github.com/lostisland/faraday/releases/tag/v1.6.0) (2021-08-01)
|
||||
|
||||
### Misc
|
||||
|
||||
* Use external Rack adapter (#1296, @iMacTia)
|
||||
|
||||
## [v1.5.1](https://github.com/lostisland/faraday/releases/tag/v1.5.1) (2021-07-11)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix JRuby incompatibility after moving out EM adapters (#1294, @ahorek)
|
||||
|
||||
### Documentation
|
||||
|
||||
* Update YARD to follow RackBuilder (#1292, @kachick)
|
||||
|
||||
## [v1.5.0](https://github.com/lostisland/faraday/releases/tag/v1.5.0) (2021-07-04)
|
||||
|
||||
### Misc
|
||||
|
||||
* Use external httpclient adapter (#1289, @iMacTia)
|
||||
* Use external patron adapter (#1290, @iMacTia)
|
||||
|
||||
## [v1.4.3](https://github.com/lostisland/faraday/releases/tag/v1.4.3) (2021-06-24)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Silence warning (#1286, @gurgeous)
|
||||
* Always dup url_prefix in Connection#build_exclusive_url (#1288, @alexeyds)
|
||||
|
||||
## [v1.4.2](https://github.com/lostisland/faraday/releases/tag/v1.4.2) (2021-05-22)
|
||||
|
||||
### Fixes
|
||||
* Add proxy setting when url_prefix is changed (#1276, @ci)
|
||||
* Default proxy scheme to http:// if necessary, fixes #1282 (#1283, @gurgeous)
|
||||
|
||||
### Documentation
|
||||
* Improve introduction page (#1273, @gurgeous)
|
||||
* Docs: add more middleware examples (#1277, @gurgeous)
|
||||
|
||||
### Misc
|
||||
* Use external `em_http` and `em_synchrony` adapters (#1274, @iMacTia)
|
||||
|
||||
## [v1.4.1](https://github.com/lostisland/faraday/releases/tag/v1.4.1) (2021-04-18)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix dependencies from external adapter gems (#1269, @iMacTia)
|
||||
|
||||
## [v1.4.0](https://github.com/lostisland/faraday/releases/tag/v1.4.0) (2021-04-16)
|
||||
|
||||
### Highlights
|
||||
|
||||
With this release, we continue the work of gradually moving out adapters into their own gems 🎉
|
||||
Thanks to @MikeRogers0 for helping the Faraday team in progressing with this quest 👏
|
||||
|
||||
And thanks to @olleolleolle efforts, Faraday is becoming more inclusive than ever 🤗
|
||||
Faraday's `master` branch has been renamed into `main`, we have an official policy on inclusive language and even a rubocop plugin to check for non-inclusive words ❤️!
|
||||
Checkout the "Misc" section below for more details 🙌 !
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix NoMethodError undefined method 'coverage' (#1255, @Maroo-b)
|
||||
|
||||
### Documentation
|
||||
|
||||
* Some docs on EventMachine adapters. (#1232, @damau)
|
||||
* CONTRIBUTING: Fix grammar and layout (#1261, @olleolleolle)
|
||||
|
||||
### Misc
|
||||
|
||||
* Replacing Net::HTTP::Persistent with faraday-net_http_persistent (#1250, @MikeRogers0)
|
||||
* CI: Configure the regenerated Coveralls token (#1256, @olleolleolle)
|
||||
* Replace Excon adapter with Faraday::Excon gem, and fix autoloading issue with Faraday::NetHttpPersistent (#1257, @iMacTia)
|
||||
* Drop CodeClimate (#1259, @olleolleolle)
|
||||
* CI: Rename default branch to main (#1263, @olleolleolle)
|
||||
* Drop RDoc support file .document (#1264, @olleolleolle, @iMacTia)
|
||||
* CONTRIBUTING: add a policy on inclusive language (#1262, @olleolleolle)
|
||||
* Add rubocop-inclusivity (#1267, @olleolleolle, @iMacTia)
|
||||
|
||||
## [v1.3.1](https://github.com/lostisland/faraday/releases/tag/v1.3.1) (2021-04-16)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Escape colon in path segment (#1237, @yarafan)
|
||||
* Handle IPv6 address String on Faraday::Connection#proxy_from_env (#1252, @cosmo0920)
|
||||
|
||||
### Documentation
|
||||
|
||||
* Fix broken Rubydoc.info links (#1236, @nickcampbell18)
|
||||
* Add httpx to list of external adapters (#1246, @HoneyryderChuck)
|
||||
|
||||
### Misc
|
||||
|
||||
* Refactor CI to remove duplicated line (#1230, @tricknotes)
|
||||
* Gemspec: Pick a good ruby2_keywords release (#1241, @olleolleolle)
|
||||
|
||||
## [v1.3.0](https://github.com/lostisland/faraday/releases/tag/v1.3.0) (2020-12-31)
|
||||
|
||||
### Highlights
|
||||
Faraday v1.3.0 is the first release to officially support Ruby 3.0 in the CI pipeline 🎉 🍾!
|
||||
|
||||
This is also the first release with a previously "included" adapter (Net::HTTP) being isolated into a [separate gem](https://github.com/lostisland/faraday-net_http) 🎊!
|
||||
The new adapter is added to Faraday as a dependency for now, so that means full backwards-compatibility, but just to be safe be careful when upgrading!
|
||||
|
||||
This is a huge step towards are Faraday v2.0 objective of pushing adapters and middleware into separate gems.
|
||||
Many thanks to the Faraday Team, @JanDintel and everyone who attended the [ROSS Conf remote event](https://www.rossconf.io/event/remote/)
|
||||
|
||||
### Features
|
||||
|
||||
* Improves consistency with Faraday::Error and Faraday::RaiseError (#1229, @qsona, @iMacTia)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Don't assign to global ::Timer (#1227, @bpo)
|
||||
|
||||
### Documentation
|
||||
|
||||
* CHANGELOG: add releases after 1.0 (#1225, @olleolleolle)
|
||||
* Improves retry middleware documentation. (#1228, @iMacTia)
|
||||
|
||||
### Misc
|
||||
|
||||
* Move out Net::HTTP adapter (#1222, @JanDintel, @iMacTia)
|
||||
* Adds Ruby 3.0 to CI Matrix (#1226, @iMacTia)
|
||||
|
||||
|
||||
## [v1.2.0](https://github.com/lostisland/faraday/releases/tag/v1.2.0) (2020-12-23)
|
||||
|
||||
### Features
|
||||
|
||||
* Introduces `on_request` and `on_complete` methods in `Faraday::Middleware`. (#1194, @iMacTia)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Require 'date' to avoid retry exception (#1206, @rustygeldmacher)
|
||||
* Fix rdebug recursion issue (#1205, @native-api)
|
||||
* Update call to `em_http_ssl_patch` (#1202, @kylekeesling)
|
||||
* `EmHttp` adapter: drop superfluous loaded? check (#1213, @olleolleolle)
|
||||
* Avoid 1 use of keyword hackery (#1211, @grosser)
|
||||
* Fix #1219 `Net::HTTP` still uses env proxy (#1221, @iMacTia)
|
||||
|
||||
### Documentation
|
||||
|
||||
* Add comment in gemspec to explain exposure of `examples` and `spec` folders. (#1192, @iMacTia)
|
||||
* Adapters, how to create them (#1193, @olleolleolle)
|
||||
* Update documentation on using the logger (#1196, @tijmenb)
|
||||
* Adjust the retry documentation and spec to align with implementation (#1198, @nbeyer)
|
||||
|
||||
### Misc
|
||||
|
||||
* Test against ruby head (#1208, @grosser)
|
||||
|
||||
## [v1.1.0](https://github.com/lostisland/faraday/releases/tag/v1.1.0) (2020-10-17)
|
||||
|
||||
### Features
|
||||
|
||||
* Makes parameters sorting configurable (#1162 @wishdev)
|
||||
* Introduces `flat_encode` option for multipart adapter. (#1163 @iMacTia)
|
||||
* Include request info in exceptions raised by RaiseError Middleware (#1181 @SandroDamilano)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Avoid `last arg as keyword param` warning when building user middleware on Ruby 2.7 (#1153 @dgholz)
|
||||
* Limits net-http-persistent version to < 4.0 (#1156 @iMacTia)
|
||||
* Update `typhoeus` to new stable version (`1.4`) (#1159 @AlexWayfer)
|
||||
* Properly fix test failure with Rack 2.1+. (#1171 @voxik)
|
||||
|
||||
### Documentation
|
||||
|
||||
* Improves documentation on how to contribute to the site by using Docker. (#1175 @iMacTia)
|
||||
* Remove retry_change_requests from documentation (#1185 @stim371)
|
||||
|
||||
### Misc
|
||||
|
||||
* Link from GitHub Actions badge to CI workflow (#1141 @olleolleolle)
|
||||
* Return tests of `Test` adapter (#1147 @AlexWayfer)
|
||||
* Add 1.0 release to wording in CONTRIBUTING (#1155 @olleolleolle)
|
||||
* Fix linting bumping Rubocop to 0.90.0 (#1182 @iMacTia)
|
||||
* Drop `git ls-files` in gemspec (#1183 @utkarsh2102)
|
||||
* Upgrade CI to ruby/setup-ruby (#1187 @gogainda)
|
||||
|
||||
## [v1.0.1](https://github.com/lostisland/faraday/releases/tag/v1.0.1) (2020-03-29)
|
||||
|
||||
### Fixes
|
||||
|
||||
* Use Net::HTTP#start(&block) to ensure closed TCP connections (#1117)
|
||||
* Fully qualify constants to be checked (#1122)
|
||||
* Allows `parse` method to be private/protected in response middleware (#1123)
|
||||
* Encode Spaces in Query Strings as '%20' Instead of '+' (#1125)
|
||||
* Limits rack to v2.0.x (#1127)
|
||||
* Adapter Registry reads also use mutex (#1136)
|
||||
|
||||
### Documentation
|
||||
|
||||
* Retry middleware documentation fix (#1109)
|
||||
* Docs(retry): precise usage of retry-after (#1111)
|
||||
* README: Link the logo to the website (#1112)
|
||||
* Website: add search bar (#1116)
|
||||
* Fix request/response mix-up in docs text (#1132)
|
||||
|
||||
## [v1.0](https://github.com/lostisland/faraday/releases/tag/v1.0.0) (2020-01-22)
|
||||
|
||||
Features:
|
||||
|
||||
* Add #trace support to Faraday::Connection #861 (@technoweenie)
|
||||
* Add the log formatter that is easy to override and safe to inherit #889 (@prikha)
|
||||
* Support standalone adapters #941 (@iMacTia)
|
||||
* Introduce Faraday::ConflictError for 409 response code #979 (@lucasmoreno)
|
||||
* Add support for setting `read_timeout` option separately #1003 (@springerigor)
|
||||
* Refactor and cleanup timeout settings across adapters #1022 (@technoweenie)
|
||||
* Create ParamPart class to allow multipart posts with JSON content and file upload at the same time #1017 (@jeremy-israel)
|
||||
* Copy UploadIO const -> FilePart for consistency with ParamPart #1018, #1021 (@technoweenie)
|
||||
* Implement streaming responses in the Excon adapter #1026 (@technoweenie)
|
||||
* Add default implementation of `Middleware#close`. #1069 (@ioquatix)
|
||||
* Add `Adapter#close` so that derived classes can call super. #1091 (@ioquatix)
|
||||
* Add log_level option to logger default formatter #1079 (@amrrbakry)
|
||||
* Fix empty array for FlatParamsEncoder `{key: []} -> "key="` #1084 (@mrexox)
|
||||
|
||||
Bugs:
|
||||
|
||||
* Explicitly require date for DateTime library in Retry middleware #844 (@nickpresta)
|
||||
* Refactor Adapter as final endpoints #846 (@iMacTia)
|
||||
* Separate Request and Response bodies in Faraday::Env #847 (@iMacTia)
|
||||
* Implement Faraday::Connection#options to make HTTP requests with the OPTIONS verb. #857 (@technoweenie)
|
||||
* Multipart: Drop Ruby 1.8 String behavior compat #892 (@olleolleolle)
|
||||
* Fix Ruby warnings in Faraday::Options.memoized #962 (@technoweenie)
|
||||
* Allow setting min/max SSL version for a Net::HTTP::Persistent connection #972, #973 (@bdewater, @olleolleolle)
|
||||
* Fix instances of frozen empty string literals #1040 (@BobbyMcWho)
|
||||
* remove temp_proxy and improve proxy tests #1063 (@technoweenie)
|
||||
* improve error initializer consistency #1095 (@technoweenie)
|
||||
|
||||
Misc:
|
||||
|
||||
* Convert minitest suite to RSpec #832 (@iMacTia, with help from @gaynetdinov, @Insti, @technoweenie)
|
||||
* Major effort to update code to RuboCop standards. #854 (@olleolleolle, @iMacTia, @technoweenie, @htwroclau, @jherdman, @Drenmi, @Insti)
|
||||
* Rubocop #1044, #1047 (@BobbyMcWho, @olleolleolle)
|
||||
* Documentation tweaks (@adsteel, @Hubro, @iMacTia, @olleolleolle, @technoweenie)
|
||||
* Update license year #981 (@Kevin-Kawai)
|
||||
* Configure Jekyll plugin jekyll-remote-theme to support Docker usage #999 (@Lewiscowles1986)
|
||||
* Fix Ruby 2.7 warnings #1009 (@tenderlove)
|
||||
* Cleanup adapter connections #1023 (@technoweenie)
|
||||
* Describe clearing cached stubs #1045 (@viraptor)
|
||||
* Add project metadata to the gemspec #1046 (@orien)
|
||||
|
||||
## v0.17.4
|
||||
|
||||
Fixes:
|
||||
|
||||
* NetHttp adapter: wrap Errno::EADDRNOTAVAIL (#1114, @embs)
|
||||
* Fix === for subclasses of deprecated classes (#1243, @mervync)
|
||||
|
||||
## v0.17.3
|
||||
|
||||
Fixes:
|
||||
|
||||
* Reverts changes in error classes hierarchy. #1092 (@iMacTia)
|
||||
* Fix Ruby 1.9 syntax errors and improve Error class testing #1094 (@BanzaiMan,
|
||||
@mrexox, @technoweenie)
|
||||
|
||||
Misc:
|
||||
|
||||
* Stops using `&Proc.new` for block forwarding. #1083 (@olleolleolle)
|
||||
* Update CI to test against ruby 2.0-2.7 #1087, #1099 (@iMacTia, @olleolleolle,
|
||||
@technoweenie)
|
||||
* require FARADAY_DEPRECATE=warn to show Faraday v1.0 deprecation warnings
|
||||
#1098 (@technoweenie)
|
||||
|
||||
## v0.17.1
|
||||
|
||||
Final release before Faraday v1.0, with important fixes for Ruby 2.7.
|
||||
|
||||
Fixes:
|
||||
|
||||
* RaiseError response middleware raises exception if HTTP client returns a nil
|
||||
status. #1042 (@jonnyom, @BobbyMcWho)
|
||||
|
||||
Misc:
|
||||
|
||||
* Fix Ruby 2.7 warnings (#1009)
|
||||
* Add `Faraday::Deprecate` to warn about upcoming v1.0 changes. (#1054, #1059,
|
||||
#1076, #1077)
|
||||
* Add release notes up to current in CHANGELOG.md (#1066)
|
||||
* Port minimal rspec suite from main branch to run backported tests. (#1058)
|
||||
|
||||
## v0.17.0
|
||||
|
||||
This release is the same as v0.15.4. It was pushed to cover up releases
|
||||
v0.16.0-v0.16.2.
|
||||
|
||||
## v0.15.4
|
||||
|
||||
* Expose `pool_size` as a option for the NetHttpPersistent adapter (#834)
|
||||
|
||||
## v0.15.3
|
||||
|
||||
* Make Faraday::Request serialisable with Marshal. (#803)
|
||||
* Add DEFAULT_EXCEPTIONS constant to Request::Retry (#814)
|
||||
* Add support for Ruby 2.6 Net::HTTP write_timeout (#824)
|
||||
|
||||
## v0.15.2
|
||||
|
||||
* Prevents `Net::HTTP` adapters to retry request internally by setting `max_retries` to 0 if available (Ruby 2.5+). (#799)
|
||||
* Fixes `NestedParamsEncoder` handling of empty array values (#801)
|
||||
|
||||
## v0.15.1
|
||||
|
||||
* NetHttpPersistent adapter better reuse of SSL connections (#793)
|
||||
* Refactor: inline cached_connection (#797)
|
||||
* Logger middleware: use $stdout instead of STDOUT (#794)
|
||||
* Fix: do not memoize/reuse Patron session (#796)
|
||||
|
||||
Also in this release:
|
||||
|
||||
* Allow setting min/max ssl version for Net::HTTP (#792)
|
||||
* Allow setting min/max ssl version for Excon (#795)
|
||||
|
||||
## v0.15.0
|
||||
|
||||
Features:
|
||||
|
||||
* Added retry block option to retry middleware. (#770)
|
||||
* Retry middleware improvements (honour Retry-After header, retry statuses) (#773)
|
||||
* Improve response logger middleware output (#784)
|
||||
|
||||
Fixes:
|
||||
|
||||
* Remove unused class error (#767)
|
||||
* Fix minor typo in README (#760)
|
||||
* Reuse persistent connections when using net-http-persistent (#778)
|
||||
* Fix Retry middleware documentation (#781)
|
||||
* Returns the http response when giving up on retrying by status (#783)
|
||||
|
||||
## v0.14.0
|
||||
|
||||
Features:
|
||||
|
||||
* Allow overriding env proxy #754 (@iMacTia)
|
||||
* Remove legacy Typhoeus adapter #715 (@olleolleolle)
|
||||
* External Typhoeus Adapter Compatibility #748 (@iMacTia)
|
||||
* Warn about missing adapter when making a request #743 (@antstorm)
|
||||
* Faraday::Adapter::Test stubs now support entire urls (with host) #741 (@erik-escobedo)
|
||||
|
||||
Fixes:
|
||||
|
||||
* If proxy is manually provided, this takes priority over `find_proxy` #724 (@iMacTia)
|
||||
* Fixes the behaviour for Excon's open_timeout (not setting write_timeout anymore) #731 (@apachelogger)
|
||||
* Handle all connection timeout messages in Patron #687 (@stayhero)
|
||||
|
||||
## v0.13.1
|
||||
|
||||
* Fixes an incompatibility with Addressable::URI being used as uri_parser
|
||||
|
||||
## v0.13.0
|
||||
|
||||
Features:
|
||||
|
||||
* Dynamically reloads the proxy when performing a request on an absolute domain (#701)
|
||||
* Adapter support for Net::HTTP::Persistent v3.0.0 (#619)
|
||||
|
||||
Fixes:
|
||||
|
||||
* Prefer #hostname over #host. (#714)
|
||||
* Fixes an edge-case issue with response headers parsing (missing HTTP header) (#719)
|
||||
|
||||
## v0.12.2
|
||||
|
||||
* Parse headers from aggregated proxy requests/responses (#681)
|
||||
* Guard against invalid middleware configuration with warning (#685)
|
||||
* Do not use :insecure option by default in Patron (#691)
|
||||
* Fixes an issue with HTTPClient not raising a `Faraday::ConnectionFailed` (#702)
|
||||
* Fixes YAML serialization/deserialization for `Faraday::Utils::Headers` (#690)
|
||||
* Fixes an issue with Options having a nil value (#694)
|
||||
* Fixes an issue with Faraday.default_connection not using Faraday.default_connection_options (#698)
|
||||
* Fixes an issue with Options.merge! and Faraday instrumentation middleware (#710)
|
||||
|
||||
## v0.12.1
|
||||
|
||||
* Fix an issue with Patron tests failing on jruby
|
||||
* Fix an issue with new `rewind_files` feature that was causing an exception when the body was not an Hash
|
||||
* Expose wrapped_exception in all client errors
|
||||
* Add Authentication Section to the ReadMe
|
||||
|
||||
## v0.12.0.1
|
||||
|
||||
* Hotfix release to address an issue with TravisCI deploy on Rubygems
|
||||
|
||||
## v0.12.0
|
||||
|
||||
Features:
|
||||
|
||||
* Proxy feature now relies on Ruby `URI::Generic#find_proxy` and can use `no_proxy` ENV variable (not compatible with ruby < 2.0)
|
||||
* Adds support for `context` request option to pass arbitrary information to middlewares
|
||||
|
||||
Fixes:
|
||||
|
||||
* Fix an issue with options that was causing new options to override defaults ones unexpectedly
|
||||
* Rewind `UploadIO`s on retry to fix a compatibility issue
|
||||
* Make multipart boundary unique
|
||||
* Improvements in `README.md`
|
||||
|
||||
## v0.11.0
|
||||
|
||||
Features:
|
||||
|
||||
* Add `filter` method to Logger middleware
|
||||
* Add support for Ruby2.4 and Minitest 6
|
||||
* Introduce block syntax to customise the adapter
|
||||
|
||||
Fixes:
|
||||
|
||||
* Fix an issue that was allowing to override `default_connection_options` from a connection instance
|
||||
* Fix a bug that was causing newline escape characters ("\n") to be used when building the Authorization header
|
||||
|
||||
## v0.10.1
|
||||
|
||||
- Fix an issue with HTTPClient adapter that was causing the SSL to be reset on every request
|
||||
- Rescue `IOError` instead of specific subclass
|
||||
- `Faraday::Utils::Headers` can now be successfully serialised in YAML
|
||||
- Handle `default_connection_options` set with hash
|
||||
|
||||
## v0.10.0
|
||||
|
||||
Breaking changes:
|
||||
- Drop support for Ruby 1.8
|
||||
|
||||
Features:
|
||||
- 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
|
||||
|
||||
Fixes:
|
||||
- Add `Faraday.respond_to?` to find methods managed by `method_missing`
|
||||
- em-http: `request.host` instead of `connection.host` should be taken for SSL validations
|
||||
- Allow `default_connection_options` to be merged when options are passed as url parameter
|
||||
- Improve splitting key-value pairs in raw HTTP headers
|
||||
|
||||
## v0.9.2
|
||||
|
||||
Adapters:
|
||||
- Enable gzip compression for httpclient
|
||||
- Fixes default certificate store for httpclient not having default paths.
|
||||
- Make excon adapter compatible with 0.44 excon version
|
||||
- Add compatibility with Patron 0.4.20
|
||||
- Determine default port numbers in Net::HTTP adapters (Addressable compatibility)
|
||||
- em-http: wrap "connection closed by server" as ConnectionFailed type
|
||||
- Wrap Errno::ETIMEDOUT in Faraday::Error::TimeoutError
|
||||
|
||||
Utils:
|
||||
- Add Rack-compatible support for parsing `a[][b]=c` nested queries
|
||||
- Encode nil values in queries different than empty strings. Before: `a=`; now: `a`.
|
||||
- Have `Faraday::Utils::Headers#replace` clear internal key cache
|
||||
- Dup the internal key cache when a Headers hash is copied
|
||||
|
||||
Env and middleware:
|
||||
- Ensure `env` stored on middleware response has reference to the response
|
||||
- Ensure that Response properties are initialized during `on_complete` (VCR compatibility)
|
||||
- Copy request options in Faraday::Connection#dup
|
||||
- Env custom members should be copied by Env.from(env)
|
||||
- Honour per-request `request.options.params_encoder`
|
||||
- Fix `interval_randomness` data type for Retry middleware
|
||||
- Add maximum interval option for Retry middleware
|
||||
|
||||
## v0.9.1
|
||||
|
||||
* Refactor Net:HTTP adapter so that with_net_http_connection can be overridden to allow pooled connections. (@Ben-M)
|
||||
* Add configurable methods that bypass `retry_if` in the Retry request middleware. (@mike-bourgeous)
|
||||
|
||||
## v0.9.0
|
||||
|
||||
* Add HTTPClient adapter (@hakanensari)
|
||||
* Improve Retry handler (@mislav)
|
||||
* Remove autoloading by default (@technoweenie)
|
||||
* Improve internal docs (@technoweenie, @mislav)
|
||||
* Respect user/password in http proxy string (@mislav)
|
||||
* Adapter options are structs. Reinforces consistent options across adapters
|
||||
(@technoweenie)
|
||||
* Stop stripping trailing / off base URLs in a Faraday::Connection. (@technoweenie)
|
||||
* Add a configurable URI parser. (@technoweenie)
|
||||
* Remove need to manually autoload when using the authorization header helpers on `Faraday::Connection`. (@technoweenie)
|
||||
* `Faraday::Adapter::Test` respects the `Faraday::RequestOptions#params_encoder` option. (@technoweenie)
|
29
Gemfile
Normal file
@ -0,0 +1,29 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# Even though we don't officially support JRuby, this dependency makes Faraday
|
||||
# compatible with it, so we're leaving it in for jruby users to use it.
|
||||
gem 'jruby-openssl', '~> 0.11.0', platforms: :jruby
|
||||
|
||||
group :development, :test do
|
||||
gem 'bake-test-external'
|
||||
gem 'coveralls_reborn', require: false
|
||||
gem 'pry'
|
||||
gem 'rack', '~> 3.0'
|
||||
gem 'rake'
|
||||
gem 'rspec', '~> 3.7'
|
||||
gem 'rspec_junit_formatter', '~> 0.4'
|
||||
gem 'simplecov'
|
||||
gem 'webmock', '~> 3.4'
|
||||
end
|
||||
|
||||
group :development, :lint do
|
||||
gem 'racc', '~> 1.7' # for RuboCop, on Ruby 3.3
|
||||
gem 'rubocop'
|
||||
gem 'rubocop-packaging', '~> 0.5'
|
||||
gem 'rubocop-performance', '~> 1.0'
|
||||
gem 'yard-junk'
|
||||
end
|
||||
|
||||
gemspec
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2009 rick
|
||||
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
|
67
README.md
Normal file
@ -0,0 +1,67 @@
|
||||
# [][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.
|
||||
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, 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 3.0+
|
||||
|
||||
If something doesn't work on one of these Ruby versions, it's a bug.
|
||||
|
||||
This library may inadvertently work (or seem to work) on other Ruby
|
||||
implementations and versions, however support will only be provided for the versions listed
|
||||
above.
|
||||
|
||||
If you would like this library to support another Ruby version, you may
|
||||
volunteer to be a maintainer. Being a maintainer entails making sure all tests
|
||||
run and pass on that implementation. When something breaks on your
|
||||
implementation, you will be responsible for providing patches in a timely
|
||||
fashion. If critical issues for a particular implementation exist at the time
|
||||
of a major release, support for that Ruby version may be dropped.
|
||||
|
||||
## Contribute
|
||||
|
||||
Do you want to contribute to Faraday?
|
||||
Open the issues page and check for the `help wanted` label!
|
||||
But before you start coding, please read our [Contributing Guide][contributing]
|
||||
|
||||
## Copyright
|
||||
|
||||
© 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
|
90
README.rdoc
@ -1,90 +0,0 @@
|
||||
= faraday
|
||||
|
||||
Experiments in a REST API lib
|
||||
|
||||
Super alpha! Don't use it if you mind throwing away all the code when I change
|
||||
the API on a whim.
|
||||
|
||||
This mess is gonna get raw, like sushi. So, haters to the left.
|
||||
|
||||
== Usage
|
||||
|
||||
# uses Net/HTTP, no response parsing
|
||||
conn = Faraday::Connection.new("http://sushi.com")
|
||||
conn.extend Faraday::Adapter::NetHttp
|
||||
resp = conn.get("/sake.json")
|
||||
resp.body # => %({"name":"Sake"})
|
||||
|
||||
# uses Net/HTTP, Yajl parsing
|
||||
conn = Faraday::Connection.new("http://sushi.com")
|
||||
conn.extend Faraday::Adapter::NetHttp
|
||||
conn.response_class = Faraday::Response::YajlResponse
|
||||
resp = conn.get("/sake.json")
|
||||
resp.body # => {"name": "Sake"}
|
||||
|
||||
# uses Typhoeus, no response parsing
|
||||
conn = Faraday::Connection.new("http://sushi.com")
|
||||
conn.extend Faraday::Adapter::Typhoeus
|
||||
resp = conn.get("/sake.json")
|
||||
resp.body # => %({"name":"Sake"})
|
||||
|
||||
# uses Typhoeus, Yajl parsing, performs requests in parallel
|
||||
conn = Faraday::Connection.new("http://sushi.com")
|
||||
conn.extend Faraday::Adapter::Typhoeus
|
||||
conn.response_class = Faraday::Response::YajlResponse
|
||||
resp1, resp2 = nil, nil
|
||||
conn.in_parallel do
|
||||
resp1 = conn.get("/sake.json")
|
||||
resp2 = conn.get("/unagi.json")
|
||||
|
||||
# requests have not been made yet
|
||||
resp1.body # => nil
|
||||
resp2.body # => nil
|
||||
end
|
||||
resp1.body # => {"name": "Sake"}
|
||||
resp2.body # => {"name": "Unagi"}
|
||||
|
||||
== Testing
|
||||
|
||||
* Yajl is needed for tests :(
|
||||
* Live Sinatra server is required for tests: `ruby test/live_server.rb` to start it.
|
||||
|
||||
=== Writing tests based on faraday
|
||||
|
||||
Using the MockRequest connection adapter you can implement your own test
|
||||
connection class:
|
||||
|
||||
# customize your own TestConnection or just use Faraday::TestConnection
|
||||
class TestConnection < Faraday::Connection
|
||||
include Faraday::Adapter::MockRequest
|
||||
end
|
||||
|
||||
conn = TestConnection.new do |stub|
|
||||
# response mimics a rack response
|
||||
stub.get('/hello.json') { [200, {}, 'hi world'] }
|
||||
end
|
||||
resp = conn.get '/hello.json'
|
||||
resp.body # => 'hi world'
|
||||
resp = conn.get '/whatever' # => <not stubbed, raises connection error>
|
||||
|
||||
== TODO
|
||||
|
||||
* other HTTP methods besides just GET
|
||||
* gracefully skip tests for Yajl and other optional libraries if they don't exist.
|
||||
* gracefully skip live http server tests if the sinatra server is not running.
|
||||
* use Typhoeus' request mocking facilities in the Typhoeus adapter test
|
||||
* lots of other crap
|
||||
|
||||
== Note on Patches/Pull Requests
|
||||
|
||||
* Fork the project.
|
||||
* Make your feature addition or bug fix.
|
||||
* Add tests for it. This is important so I don't break it in a
|
||||
future version unintentionally.
|
||||
* Commit, do not mess with rakefile, version, or history.
|
||||
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
||||
* Send me a pull request. Bonus points for topic branches.
|
||||
|
||||
== Copyright
|
||||
|
||||
Copyright (c) 2009 rick. See LICENSE for details.
|
57
Rakefile
@ -1,51 +1,12 @@
|
||||
require 'rubygems'
|
||||
require 'rake'
|
||||
# frozen_string_literal: true
|
||||
|
||||
begin
|
||||
require 'jeweler'
|
||||
Jeweler::Tasks.new do |gem|
|
||||
gem.name = "faraday"
|
||||
gem.summary = "HTTP/REST API client library"
|
||||
gem.description = "HTTP/REST API client library with pluggable components"
|
||||
gem.email = "technoweenie@gmail.com"
|
||||
gem.homepage = "http://github.com/technoweenie/faraday"
|
||||
gem.authors = ["rick"]
|
||||
end
|
||||
Jeweler::GemcutterTasks.new
|
||||
rescue LoadError
|
||||
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
||||
require 'rspec/core/rake_task'
|
||||
require 'bundler'
|
||||
|
||||
Bundler::GemHelper.install_tasks
|
||||
|
||||
RSpec::Core::RakeTask.new(:spec) do |task|
|
||||
task.ruby_opts = %w[-W]
|
||||
end
|
||||
|
||||
require 'rake/testtask'
|
||||
Rake::TestTask.new(:test) do |test|
|
||||
test.libs << 'lib' << 'test'
|
||||
test.pattern = 'test/**/*_test.rb'
|
||||
test.verbose = true
|
||||
end
|
||||
|
||||
begin
|
||||
require 'rcov/rcovtask'
|
||||
Rcov::RcovTask.new do |test|
|
||||
test.libs << 'test'
|
||||
test.pattern = 'test/**/test_*.rb'
|
||||
test.verbose = true
|
||||
end
|
||||
rescue LoadError
|
||||
task :rcov do
|
||||
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
||||
end
|
||||
end
|
||||
|
||||
task :test => :check_dependencies
|
||||
|
||||
task :default => :test
|
||||
|
||||
require 'rake/rdoctask'
|
||||
Rake::RDocTask.new do |rdoc|
|
||||
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
||||
|
||||
rdoc.rdoc_dir = 'rdoc'
|
||||
rdoc.title = "faraday #{version}"
|
||||
rdoc.rdoc_files.include('README*')
|
||||
rdoc.rdoc_files.include('lib/**/*.rb')
|
||||
end
|
||||
task default: :spec
|
||||
|
185
UPGRADING.md
Normal file
@ -0,0 +1,185 @@
|
||||
## Faraday 2.0
|
||||
|
||||
### Adapters have moved!
|
||||
|
||||
With this release, we've officially moved all adapters, except for the `net_http` one, out of Faraday.
|
||||
What that means, is that they won't be available out-of-the-box anymore,
|
||||
and you'll instead need to add them to your Gemfile.
|
||||
|
||||
**NOTE: the `net_http` adapter was originally removed as well in version `2.0`, but quickly reintroduced in version `2.0.1`.
|
||||
We strongly suggest you to skip version `2.0` and instead use version `2.0.1` or greater.**
|
||||
|
||||
#### Why was this decision made?
|
||||
|
||||
We've taken this decision for the following technical reasons:
|
||||
|
||||
* We wanted the Faraday gem to focus on providing a clean, standardized, middleware-stack-based API.
|
||||
* We wanted to free the core team from maintaining all the different adapters, relying more on the community to
|
||||
maintain them based on the broad interest. This will allow the core team to focus on implementing features
|
||||
focused on the Faraday API more quickly, without having to push it on all adapters immediately.
|
||||
* With the community creating more and more adapters, we wanted to avoid having first and second-class adapters
|
||||
by having some of them included with the gem and others available externally.
|
||||
* Moving adapters into separate gems solve the dependency issues once and for all.
|
||||
Faraday will remain a dependency-free gem, while adapter gems will be able to automatically pull
|
||||
any necessary dependency, without having to rely on the developer to do so.
|
||||
|
||||
#### So what will this mean for me?
|
||||
|
||||
We did our best to make this transition as painless as possible for you, so here is what we did.
|
||||
|
||||
* All adapters, except for the `net_http` one, have already been moved out and released as separate gems.
|
||||
They've then been re-added into Faraday's v1 dependencies so that you wouldn't notice.
|
||||
This means that immediately after v2.0 will be released, all the adapters that were previously available will be
|
||||
already compatible with Faraday 2.0!
|
||||
* We've setup an [Awesome Faraday](https://github.com/lostisland/awesome-faraday) repository, where you can find and discover adapters.
|
||||
We also highlighted their unique features and level of compliance with Faraday's features.
|
||||
|
||||
#### That's great! What should I change in my code immediately after upgrading?
|
||||
|
||||
* If you just use the default `net_http` adapter, then you don't need to do anything!
|
||||
* Otherwise, add the corresponding adapter gem to your Gemfile (e.g. `faraday-net_http_persistent`). Then, simply require them after you require `faraday`.
|
||||
```ruby
|
||||
# Gemfile
|
||||
gem 'faraday'
|
||||
gem 'faraday-net_http_persistent'
|
||||
|
||||
# Code
|
||||
require 'faraday'
|
||||
require 'faraday/net_http_persistent'
|
||||
```
|
||||
|
||||
### Faraday Middleware Deprecation
|
||||
|
||||
In case you never used it, [Faraday Middleware](https://github.com/lostisland/faraday_middleware) is a handy collection of middleware that have so far been maintained by the Faraday core team.
|
||||
With Faraday 2.0 focusing on becoming an ecosystem, rather than an out-of-the-box solution, it only made sense to take the same approach for middleware as we did for adapters. For this reason, `faraday_middleware` will not be updated to support Faraday 2.0.
|
||||
Instead, we'll support the transition from centralised-repo collection of middleware to individual middleware gems, effectively replicating what we did with adapters. Each middleware will have its own repository and gem, and it will allow developers to only add the ones they require to their Gemfile.
|
||||
|
||||
So don't fret yet! We're doing our best to support our `faraday_middleware` users out there, so here are the steps we've taken to make this work:
|
||||
* We've promoted the highly popular JSON middleware (both request encoding and response parsing) to a core middleware and it will now be shipped together with Faraday. We expect many `faraday_middleware` users will be able to just stop using the extra dependency thanks to this.
|
||||
* We've created a [faraday-middleware-template](https://github.com/lostisland/faraday-middleware-template) repository that will make creating a new middleware gem simple and straightforward.
|
||||
* We've added a middleware section to the [awesome-faraday](https://github.com/lostisland/awesome-faraday) repo, so you can easily find available middleware when you need it.
|
||||
|
||||
It will obviously take time for some of the middleware in `faraday_middleware` to make its way into a separate gem, so we appreciate this might be an upgrade obstacle for some. However this is part of an effort to focus the core team resources tackling the most requested features.
|
||||
We'll be listening to the community and prioritize middleware that are most used, and will be welcoming contributors who want to become owners of the middleware when these become separate from the `faraday_middleware` repo.
|
||||
|
||||
### Bundled middleware moved out
|
||||
|
||||
Moving middleware into its own gem makes sense not only for `faraday_middleware`, but also for middleware bundled with Faraday.
|
||||
As of v2.0, the `retry` and `multipart` middleware have been moved to separate `faraday-retry` and `faraday-multipart` gems.
|
||||
These have been identified as good candidates due to their complexity and external dependencies.
|
||||
Thanks to this change, we were able to make Faraday 2.0 completely dependency free 🎉 (the only exception being `ruby2_keywords`, which will be necessary only while we keep supporting Ruby 2.6).
|
||||
|
||||
#### So what should I do if I currently use the `retry` or `multipart` middleware?
|
||||
|
||||
Upgrading is pretty simple, because the middleware was simply moved out to external gems.
|
||||
All you need to do is to add them to your gemfile (either `faraday-retry` or `faraday-multipart` and require them before usage:
|
||||
|
||||
```ruby
|
||||
# Gemfile
|
||||
gem 'faraday-multipart'
|
||||
gem 'faraday-retry'
|
||||
|
||||
# Connection initializer
|
||||
require 'faraday/retry'
|
||||
require 'faraday/multipart'
|
||||
|
||||
conn = Faraday.new(url) do |f|
|
||||
f.request :multipart
|
||||
f.request :retry
|
||||
# ...
|
||||
end
|
||||
```
|
||||
|
||||
### Autoloading and dependencies
|
||||
|
||||
Faraday has until now provided and relied on a complex dynamic dependencies system.
|
||||
This would allow to reference classes and require dependencies only when needed (e.g. when running the first request) based
|
||||
on the middleware/adapters used.
|
||||
As part of Faraday v2.0, we've removed all external dependencies, which means we don't really need this anymore.
|
||||
This change should not affect you directly, but if you're registering middleware then be aware of the new syntax:
|
||||
|
||||
```ruby
|
||||
# `name` here can be anything you want.
|
||||
# `klass` is your custom middleware class.
|
||||
# This method can also be called on `Faraday::Adapter`, `Faraday::Request` and `Faraday::Response`
|
||||
Faraday::Middleware.register_middleware(name: klass)
|
||||
```
|
||||
|
||||
The `register_middleware` method also previously allowed to provide a symbol, string, proc, or array
|
||||
but this has been removed from the v2.0 release to simplify the interface.
|
||||
(EDIT: symbol/string/proc have subsequently been reintroduced in v2.2, but not the array).
|
||||
|
||||
### Authentication helper methods in Connection have been removed
|
||||
|
||||
You were previously able to call `authorization`, `basic_auth` and `token_auth` against the `Connection` object, but this helper methods have now been dropped.
|
||||
Instead, you should be using the equivalent middleware, as explained in [this page](https://lostisland.github.io/faraday/#/middleware/included/authentication).
|
||||
|
||||
For more details, see https://github.com/lostisland/faraday/pull/1306
|
||||
|
||||
### The `dependency` method in middlewares has been removed
|
||||
|
||||
In Faraday 1, a deferred require was used with the `dependency` method.
|
||||
|
||||
In Faraday 2, that method has been removed. In your middleware gems, use a regular `require` at the top of the file,
|
||||
|
||||
### Others
|
||||
|
||||
* Rename `Faraday::Request#method` to `#http_method`.
|
||||
* Remove `Faraday::Response::Middleware`. You can now use the new `on_complete` callback provided by `Faraday::Middleware`.
|
||||
* `Faraday.default_connection_options` will now be deep-merged into new connections to avoid overriding them (e.g. headers).
|
||||
* `Faraday::Builder#build` method is not exposed through `Faraday::Connection` anymore and does not reset the handlers if called multiple times. This method should be used internally only.
|
||||
|
||||
## Faraday 1.0
|
||||
|
||||
### Errors
|
||||
* Removes sub-class constants definition from `Faraday::Error`. A sub-class (e.g. `ClientError`) was previously accessible
|
||||
either through the `Faraday` module (e.g. `Faraday::ClientError`) or through the `Faraday::Error` class (e.g. `Faraday::Error::ClientError`).
|
||||
The latter is no longer available and the former should be used instead, so check your `rescue`s.
|
||||
* Introduces a new `Faraday::ServerError` (5xx status codes) alongside the existing `Faraday::ClientError` (4xx status codes).
|
||||
Please note `Faraday::ClientError` was previously used for both.
|
||||
* Introduces new Errors that describe the most common REST status codes:
|
||||
* Faraday::BadRequestError (400)
|
||||
* Faraday::UnauthorizedError (401)
|
||||
* Faraday::ForbiddenError (403)
|
||||
* Faraday::ProxyAuthError (407). Please note this raised a `Faraday::ConnectionFailed` before.
|
||||
* Faraday::ConflictError (409)
|
||||
* Faraday::UnprocessableEntityError (422)
|
||||
* The following error classes have changed the hierarchy to better mirror their real-world usage and semantic meaning:
|
||||
* TimeoutError < ServerError (was < ClientError)
|
||||
* ConnectionFailed < Error (was < ClientError)
|
||||
* SSLError < Error (was < ClientError)
|
||||
* ParsingError < Error (was < ClientError)
|
||||
* RetriableResponse < Error (was < ClientError)
|
||||
|
||||
### Custom adapters
|
||||
If you have written a custom adapter, please be aware that `env.body` is now an alias to the two new properties `request_body` and `response_body`.
|
||||
This should work without you noticing if your adapter inherits from `Faraday::Adapter` and calls `save_response`, but if it doesn't, then please ensure you set the `status` BEFORE the `body` while processing the response.
|
||||
|
||||
### Others
|
||||
* Dropped support for jruby and Rubinius.
|
||||
* Officially supports Ruby 2.4+
|
||||
* In order to specify the adapter you now MUST use the `#adapter` method on the connection builder. If you don't do so and your adapter inherits from `Faraday::Adapter` then Faraday will raise an exception. Otherwise, Faraday will automatically push the default adapter at the end of the stack causing your request to be executed twice.
|
||||
```ruby
|
||||
class OfficialAdapter < Faraday::Adapter
|
||||
...
|
||||
end
|
||||
|
||||
class MyAdapter
|
||||
...
|
||||
end
|
||||
|
||||
# This will raise an exception
|
||||
conn = Faraday.new(...) do |f|
|
||||
f.use OfficialAdapter
|
||||
end
|
||||
|
||||
# This will cause Faraday inserting the default adapter at the end of the stack
|
||||
conn = Faraday.new(...) do |f|
|
||||
f.use MyAdapter
|
||||
end
|
||||
|
||||
# You MUST use `adapter` method
|
||||
conn = Faraday.new(...) do |f|
|
||||
f.adapter AnyAdapter
|
||||
end
|
||||
```
|
15
bin/console
Executable file
@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bundler/setup'
|
||||
require 'faraday'
|
||||
|
||||
# You can add fixtures and/or initialization code here to make experimenting
|
||||
# with your gem easier. You can also use a different console, if you like.
|
||||
|
||||
# (If you use this, don't forget to add pry to your Gemfile!)
|
||||
# require "pry"
|
||||
# Pry.start
|
||||
|
||||
require 'irb'
|
||||
IRB.start(__FILE__)
|
7
bin/setup
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
set -vx
|
||||
|
||||
gem install bundler
|
||||
bundle install --jobs 4
|
7
bin/test
Executable file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
set -vx
|
||||
|
||||
bundle exec rubocop -a --format progress
|
||||
bundle exec rspec
|
3
config/external.yaml
Normal file
@ -0,0 +1,3 @@
|
||||
faraday-net_http:
|
||||
url: https://github.com/lostisland/faraday-net_http.git
|
||||
command: bundle exec rspec
|
0
docs/.nojekyll
Normal file
21
docs/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Faraday Docs
|
||||
|
||||
Faraday Docs are powered by [Docsify](https://docsify.js.org/#/).
|
||||
|
||||
## Development
|
||||
|
||||
### Setup
|
||||
|
||||
From the Faraday project root, run the following:
|
||||
|
||||
```bash
|
||||
npm install # this will install the necessary dependencies
|
||||
```
|
||||
|
||||
### Running the Docs Locally
|
||||
|
||||
To preview your changes locally, run the following:
|
||||
|
||||
```bash
|
||||
npm run docs
|
||||
```
|
BIN
docs/_media/favicon.png
Normal file
After Width: | Height: | Size: 700 B |
34
docs/_media/home-logo.svg
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="1240px" height="300px" viewBox="0 0 1240 300" 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>Custom Preset</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs>
|
||||
<linearGradient x1="0%" y1="50.0025809%" x2="113.816993%" y2="50.0025809%" 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>
|
||||
<linearGradient x1="0%" y1="50%" x2="183.555899%" y2="50%" id="linearGradient-2">
|
||||
<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>
|
||||
</defs>
|
||||
<g id="Custom-Preset" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Group" transform="translate(20.000000, 19.000000)">
|
||||
<path d="M345.711907,185.953203 L345.711907,130.649668 L409.242825,130.649668 L409.242825,149.259747 L359.313152,149.259747 L359.313152,185.953203 L345.711907,185.953203 Z M345.711907,81.4653477 L345.711907,78.0438675 L409.242825,78.0438675 L409.242825,96.9280012 L359.313152,96.9280012 L345.711907,81.4653477 Z M475.145406,167.86656 L466.738303,185.953203 L448.442753,185.953203 L497.884985,78.0438675 L530.897775,150.066099 L512.352129,150.066099 L497.884985,118.376317 L483.302413,150.066099 L475.145406,167.86656 Z M547.118435,185.953203 L528.384219,185.953203 L520.084002,167.069069 L538.700762,167.069069 L547.118435,185.953203 Z M622.335524,147.242759 L641.738952,185.953203 L623.642586,185.953203 L596.194284,131.865422 L610.277417,131.998972 C614.530023,132.025452 618.575201,130.097023 621.323011,126.733283 C624.471929,123.282595 626.173163,118.679375 626.054208,113.931554 C626.151915,109.261236 624.44918,104.743216 621.323011,101.377847 C618.575201,98.0141076 614.530023,96.0856782 610.277417,96.1121585 L578.687017,96.1121585 L576.846084,94.0898288 L568.745982,78.0438675 L610.277417,78.0438675 C618.81961,77.9969308 626.950801,81.8406095 632.51588,88.5570399 C638.594115,95.489278 641.88686,104.566395 641.720542,113.931554 C641.851711,121.132427 639.930635,128.213452 636.197745,134.288402 C632.956055,139.967305 628.120172,144.486477 622.335524,147.242759 Z M721.15875,167.86656 L712.751646,185.953203 L694.456097,185.953203 L743.898329,78.0438675 L776.911118,150.066099 L758.365472,150.066099 L743.898329,118.376317 L729.315756,150.066099 L721.15875,167.86656 Z M791.780057,185.953203 L773.04584,185.953203 L764.745623,167.069069 L783.362384,167.069069 L791.780057,185.953203 Z M920.187937,132.046034 C920.259924,139.158187 918.908812,146.217512 916.207905,152.841002 C913.588118,159.130051 909.866145,164.94257 905.203116,170.026926 C900.598652,174.890932 895.040024,178.836169 888.845184,181.636972 C882.637851,184.502668 875.833361,185.978299 868.945023,185.953203 L835.03515,185.953203 L852.049787,167.850042 L869.044524,167.850042 C873.624514,167.867696 878.155982,166.94988 882.337831,165.157581 C886.48057,163.290008 890.178954,160.625132 893.203319,157.328438 C896.305844,153.940452 898.776475,150.064602 900.506678,145.871155 C904.056306,136.940326 904.042131,127.065943 900.466878,118.144531 C898.744294,113.921677 896.228329,110.0382 893.044118,106.687249 C889.978019,103.434133 886.289393,100.776289 882.17863,98.8581057 C878.034816,97.0756201 873.545852,96.1516664 869.004724,96.1465488 L852.009987,96.1465488 L835.03515,78.0438675 L869.044524,78.0438675 C875.931423,78.0308282 882.733032,79.5058345 888.944685,82.3596188 C895.081854,85.1969559 900.594424,89.1382728 905.183216,93.9696651 C909.934465,99.0482971 913.701736,104.900743 916.307405,111.251066 C918.974333,117.880965 920.291628,124.940081 920.187937,132.046034 Z M986.096196,167.86656 L977.689093,185.953203 L959.393543,185.953203 L1008.83578,78.0438675 L1041.84857,150.066099 L1023.30292,150.066099 L1008.83578,118.376317 L994.253203,150.066099 L986.096196,167.86656 Z M1058.06923,185.953203 L1039.3621,185.953203 L1031.03479,167.069069 L1049.62463,167.069069 L1058.06923,185.953203 Z M1150.72976,146.72732 L1150.72976,146.593769 L1122.56982,185.953203 L1101.32432,185.953203 L1140.28084,131.998535 L1101.34363,78.0438675 L1122.58913,78.0438675 L1150.74907,117.42238 L1179.0249,78.0438675 L1200,78.0438675 L1161.37182,131.998535 L1150.72976,146.72732 Z M1149.9863,146.987685 L1121.60014,185.953203 L1149.9863,146.836069 L1149.9863,146.987685 Z" id="Shape" fill="url(#linearGradient-1)" fill-rule="nonzero"></path>
|
||||
<path d="M126.085885,259.671354 L62.1555891,196.228083 L129.023404,129.824126 L195.924855,196.228083 L132.016984,259.671354 C131.230558,260.452312 130.163785,260.89107 129.051434,260.89107 C127.939083,260.89107 126.87231,260.452312 126.085885,259.671354 Z M1.18617949,134.865638 C0.416659626,134.073078 -0.00984556455,133.003843 0.000172577645,131.893267 C0.0109185487,130.782692 0.457336942,129.721798 1.24153626,128.944087 L127.510327,1.33410005 C128.285004,0.549370018 129.33657,0.108388479 130.433165,0.108388479 C131.529759,0.108388479 132.581326,0.549370018 133.356002,1.33410005 L259.569436,128.944087 C260.346251,129.729247 260.782681,130.794302 260.782681,131.904862 C260.782681,133.015423 260.346251,134.080478 259.569436,134.865638 L211.11012,183.861156 L130.388879,102.246734 L49.6565668,183.87235 L1.18617949,134.865638 L1.18617949,134.865638 Z" id="Shape" fill="url(#linearGradient-2)"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.1 KiB |
BIN
docs/_media/logo.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
docs/_media/middleware.png
Normal file
After Width: | Height: | Size: 225 KiB |
BIN
docs/_media/overview.png
Normal file
After Width: | Height: | Size: 225 KiB |
BIN
docs/_media/repo-card-slim.png
Normal file
After Width: | Height: | Size: 186 KiB |
BIN
docs/_media/repo-card.png
Normal file
After Width: | Height: | Size: 358 KiB |
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
|
46
docs/adapters/index.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Adapters
|
||||
|
||||
The Faraday Adapter interface determines how a Faraday request is turned into
|
||||
a Faraday response object. Adapters are typically implemented with common Ruby
|
||||
HTTP clients, but can have custom implementations. Adapters can be configured
|
||||
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 want to configure it globally, do the following:
|
||||
|
||||
```ruby
|
||||
require 'faraday'
|
||||
require 'faraday/httpclient'
|
||||
|
||||
Faraday.default_adapter = :httpclient
|
||||
```
|
||||
|
||||
If you want to configure it per Faraday Connection, do the following:
|
||||
|
||||
```ruby
|
||||
require 'faraday'
|
||||
require 'faraday/httpclient'
|
||||
|
||||
conn = Faraday.new do |f|
|
||||
f.adapter :httpclient
|
||||
end
|
||||
```
|
||||
|
||||
## Fantastic adapters and where to find them
|
||||
|
||||
Except for the default [Net::HTTP][net_http] adapter and the [Test Adapter][testing] adapter, which is for _test purposes only_,
|
||||
adapters are distributed separately from Faraday and need to be manually installed.
|
||||
They are usually available as gems, or bundled with HTTP clients.
|
||||
|
||||
While most adapters use a common Ruby HTTP client library, adapters can also
|
||||
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]!
|
||||
|
||||
|
||||
[testing]: /adapters/test-adapter.md
|
||||
[net_http]: /adapters/net-http.md
|
||||
[awesome]: https://github.com/lostisland/awesome-faraday/#adapters
|
||||
[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
|
117
docs/adapters/test-adapter.md
Normal file
@ -0,0 +1,117 @@
|
||||
# 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.
|
||||
|
||||
The easiest way to do this is to create the stubbed requests when initializing
|
||||
a `Faraday::Connection`. Stubbing a request by path yields a block with a
|
||||
`Faraday::Env` object. The stub block expects an Array return value with three
|
||||
values: an Integer HTTP status code, a Hash of key/value headers, and a
|
||||
response body.
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new do |builder|
|
||||
builder.adapter :test do |stub|
|
||||
# block returns an array with 3 items:
|
||||
# - Integer response status
|
||||
# - Hash HTTP headers
|
||||
# - String response body
|
||||
stub.get('/ebi') do |env|
|
||||
[
|
||||
200,
|
||||
{ 'Content-Type': 'text/plain', },
|
||||
'shrimp'
|
||||
]
|
||||
end
|
||||
|
||||
# test exceptions too
|
||||
stub.get('/boom') do
|
||||
raise Faraday::ConnectionFailed
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
You can define the stubbed requests outside of the test adapter block:
|
||||
|
||||
```ruby
|
||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
||||
stub.get('/tamago') { |env| [200, {}, 'egg'] }
|
||||
end
|
||||
```
|
||||
|
||||
This Stubs instance can be passed to a new Connection:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new do |builder|
|
||||
builder.adapter :test, stubs do |stub|
|
||||
stub.get('/ebi') { |env| [ 200, {}, 'shrimp' ]}
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
It's also possible to stub additional requests after the connection has been
|
||||
initialized. This is useful for testing.
|
||||
|
||||
```ruby
|
||||
stubs.get('/uni') { |env| [ 200, {}, 'urchin' ]}
|
||||
```
|
||||
|
||||
You can also stub the request body with a string or a proc.
|
||||
It would be useful to pass a proc if it's OK only to check the parts of the request body are passed.
|
||||
|
||||
```ruby
|
||||
stubs.post('/kohada', 'where=sea&temperature=24') { |env| [ 200, {}, 'spotted gizzard shad' ]}
|
||||
stubs.post('/anago', -> (request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'Wakamoto' } }) { |env| [200, {}, 'conger eel'] }
|
||||
```
|
||||
|
||||
If you want to stub requests that exactly match a path, parameters, and headers,
|
||||
`strict_mode` would be useful.
|
||||
|
||||
```ruby
|
||||
stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true) do |stub|
|
||||
stub.get('/ikura?nori=true', 'X-Soy-Sauce' => '5ml' ) { |env| [200, {}, 'ikura gunkan maki'] }
|
||||
end
|
||||
```
|
||||
|
||||
This stub expects the connection will be called like this:
|
||||
|
||||
```ruby
|
||||
conn.get('/ikura', { nori: 'true' }, { 'X-Soy-Sauce' => '5ml' } )
|
||||
```
|
||||
|
||||
If there are other parameters or headers included, the Faraday Test adapter
|
||||
will raise `Faraday::Test::Stubs::NotFound`. It also raises the error
|
||||
if the specified parameters (`nori`) or headers (`X-Soy-Sauce`) are omitted.
|
||||
|
||||
You can also enable `strict_mode` after initializing the connection.
|
||||
In this case, all requests, including ones that have been already stubbed,
|
||||
will be handled in a strict way.
|
||||
|
||||
```ruby
|
||||
stubs.strict_mode = true
|
||||
```
|
||||
|
||||
Finally, you can treat your stubs as mocks by verifying that all of the stubbed
|
||||
calls were made. NOTE: this feature is still fairly experimental. It will not
|
||||
verify the order or count of any stub.
|
||||
|
||||
```ruby
|
||||
stubs.verify_stubbed_calls
|
||||
```
|
||||
|
||||
After the test case is completed (possibly in an `after` hook), you should clear
|
||||
the default connection to prevent it from being cached between different tests.
|
||||
This allows for each test to have its own set of stubs
|
||||
|
||||
```ruby
|
||||
Faraday.default_connection = nil
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Working [RSpec] and [test/unit] examples for a fictional JSON API client are
|
||||
available.
|
||||
|
||||
[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
|
58
docs/advanced/parallel-requests.md
Normal file
@ -0,0 +1,58 @@
|
||||
# Parallel Requests
|
||||
|
||||
Some adapters support running requests in parallel.
|
||||
This can be achieved using the `#in_parallel` method on the connection object.
|
||||
|
||||
```ruby
|
||||
# Install the Typhoeus adapter with `gem install faraday-typhoeus` first.
|
||||
require 'faraday/typhoeus'
|
||||
|
||||
conn = Faraday.new('http://httpbingo.org') do |faraday|
|
||||
faraday.adapter :typhoeus
|
||||
end
|
||||
|
||||
now = Time.now
|
||||
|
||||
conn.in_parallel do
|
||||
conn.get('/delay/3')
|
||||
conn.get('/delay/3')
|
||||
end
|
||||
|
||||
# This should take about 3 seconds, not 6.
|
||||
puts "Time taken: #{Time.now - now}"
|
||||
```
|
||||
|
||||
## A note on Async
|
||||
|
||||
You might have heard about [Async] and its native integration with Ruby 3.0.
|
||||
The good news is that you can already use Async with Faraday (thanks to the [async-http-faraday] gem)
|
||||
and this does not require the use of `#in_parallel` to run parallel requests.
|
||||
Instead, you only need to wrap your Faraday code into an Async block:
|
||||
|
||||
```ruby
|
||||
# Install the Async adapter with `gem install async-http-faraday` first.
|
||||
require 'async/http/faraday'
|
||||
|
||||
conn = Faraday.new('http://httpbingo.org') do |faraday|
|
||||
faraday.adapter :async_http
|
||||
end
|
||||
|
||||
now = Time.now
|
||||
|
||||
# NOTE: This is not limited to a single connection anymore!
|
||||
# You can run parallel requests spanning multiple connections.
|
||||
Async do
|
||||
Async { conn.get('/delay/3') }
|
||||
Async { conn.get('/delay/3') }
|
||||
end
|
||||
|
||||
# This should take about 3 seconds, not 6.
|
||||
puts "Time taken: #{Time.now - now}"
|
||||
|
||||
```
|
||||
|
||||
The big advantage of using Async is that you can now run parallel requests *spanning multiple connections*,
|
||||
whereas the `#in_parallel` method only works for requests that are made through the same connection.
|
||||
|
||||
[Async]: https://github.com/socketry/async
|
||||
[async-http-faraday]: https://github.com/socketry/async-http-faraday
|
35
docs/advanced/streaming-responses.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Streaming Responses
|
||||
|
||||
Sometimes you might need to receive a streaming response.
|
||||
You can do this with the `on_data` request option.
|
||||
|
||||
The `on_data` callback is a receives tuples of chunk Strings, and the total
|
||||
of received bytes so far.
|
||||
|
||||
This example implements such a callback:
|
||||
|
||||
```ruby
|
||||
# A buffer to store the streamed data
|
||||
streamed = []
|
||||
|
||||
conn = Faraday.new('http://httpbingo.org')
|
||||
conn.get('/stream/100') do |req|
|
||||
# Set a callback which will receive tuples of chunk Strings,
|
||||
# the sum of characters received so far, and the response environment.
|
||||
# The latter will allow access to the response status, headers and reason, as well as the request info.
|
||||
req.options.on_data = Proc.new do |chunk, overall_received_bytes, env|
|
||||
puts "Received #{overall_received_bytes} characters"
|
||||
streamed << chunk
|
||||
end
|
||||
end
|
||||
|
||||
# Joins all response chunks together
|
||||
streamed.join
|
||||
```
|
||||
|
||||
The `on_data` streaming is currently only supported by some adapters.
|
||||
To see which ones, please refer to [Awesome Faraday][awesome] comparative table or check the adapter documentation.
|
||||
Moreover, the `env` parameter was only recently added, which means some adapters may only have partial support
|
||||
(i.e. only `chunk` and `overall_received_bytes` will be passed to your block).
|
||||
|
||||
[awesome]: https://github.com/lostisland/awesome-faraday/#adapters
|
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
|
105
docs/customization/index.md
Normal file
@ -0,0 +1,105 @@
|
||||
# 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:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new('http://httpbingo.org', request: { timeout: 5 })
|
||||
conn.get('/ip')
|
||||
```
|
||||
|
||||
Or as per-request options:
|
||||
|
||||
```ruby
|
||||
conn.get do |req|
|
||||
req.url '/ip'
|
||||
req.options.timeout = 5
|
||||
end
|
||||
```
|
||||
|
||||
You can also inject arbitrary data into the request using the `context` option.
|
||||
This will be available in the `env` on all middleware.
|
||||
|
||||
```ruby
|
||||
conn.get do |req|
|
||||
req.url '/get'
|
||||
req.options.context = {
|
||||
foo: 'foo',
|
||||
bar: 'bar'
|
||||
}
|
||||
end
|
||||
```
|
||||
|
||||
## Changing how parameters are serialized
|
||||
|
||||
Sometimes you need to send the same URL parameter multiple times with different values.
|
||||
This requires manually setting the parameter encoder and can be done on
|
||||
either per-connection or per-request basis.
|
||||
This applies to all HTTP verbs.
|
||||
|
||||
Per-connection setting:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new request: { params_encoder: Faraday::FlatParamsEncoder }
|
||||
conn.get('', { roll: ['california', 'philadelphia'] })
|
||||
```
|
||||
|
||||
Per-request setting:
|
||||
|
||||
```ruby
|
||||
conn.get do |req|
|
||||
req.options.params_encoder = Faraday::FlatParamsEncoder
|
||||
req.params = { roll: ['california', 'philadelphia'] }
|
||||
end
|
||||
```
|
||||
|
||||
### Custom serializers
|
||||
|
||||
You can build your custom encoder, if you like.
|
||||
|
||||
The value of Faraday `params_encoder` can be any object that responds to:
|
||||
|
||||
* `#encode(hash) #=> String`
|
||||
* `#decode(string) #=> Hash`
|
||||
|
||||
The encoder will affect both how Faraday processes query strings and how it
|
||||
serializes POST bodies.
|
||||
|
||||
The default encoder is `Faraday::NestedParamsEncoder`.
|
||||
|
||||
### Order of parameters
|
||||
|
||||
By default, parameters are sorted by name while being serialized.
|
||||
Since this is really useful to provide better cache management and most servers don't really care about parameters order, this is the default behaviour.
|
||||
However you might find yourself dealing with a server that requires parameters to be in a specific order.
|
||||
When that happens, you can configure the encoder to skip sorting them.
|
||||
This configuration is supported by both the default `Faraday::NestedParamsEncoder` and `Faraday::FlatParamsEncoder`:
|
||||
|
||||
```ruby
|
||||
Faraday::NestedParamsEncoder.sort_params = false
|
||||
# or
|
||||
Faraday::FlatParamsEncoder.sort_params = false
|
||||
```
|
||||
|
||||
## Proxy
|
||||
|
||||
Faraday will try to automatically infer the proxy settings from your system using [`URI#find_proxy`][ruby-find-proxy].
|
||||
This will retrieve them from environment variables such as http_proxy, ftp_proxy, no_proxy, etc.
|
||||
If for any reason you want to disable this behaviour, you can do so by setting the global variable `ignore_env_proxy`:
|
||||
|
||||
```ruby
|
||||
Faraday.ignore_env_proxy = true
|
||||
```
|
||||
|
||||
You can also specify a custom proxy when initializing the connection:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new('http://www.example.com', proxy: 'http://proxy.com')
|
||||
```
|
||||
|
||||
[ruby-find-proxy]: https://ruby-doc.org/stdlib-2.6.3/libdoc/uri/rdoc/URI/Generic.html#method-i-find_proxy
|
30
docs/customization/proxy-options.md
Normal file
@ -0,0 +1,30 @@
|
||||
# Proxy Options
|
||||
|
||||
Proxy options can be provided to the connection constructor or set on a per-request basis via [RequestOptions](/customization/request-options.md).
|
||||
All these options are optional.
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|-------------|-------------|---------|-----------------|
|
||||
| `:uri` | URI, String | nil | Proxy URL. |
|
||||
| `:user` | String | nil | Proxy user. |
|
||||
| `:password` | String | nil | Proxy password. |
|
||||
|
||||
## Example
|
||||
|
||||
```ruby
|
||||
# Proxy options can be passed to the connection constructor and will be applied to all requests.
|
||||
proxy_options = {
|
||||
uri: 'http://proxy.example.com:8080',
|
||||
user: 'username',
|
||||
password: 'password'
|
||||
}
|
||||
|
||||
conn = Faraday.new(proxy: proxy_options) do |faraday|
|
||||
# ...
|
||||
end
|
||||
|
||||
# You can then override them on a per-request basis.
|
||||
conn.get('/foo') do |req|
|
||||
req.options.proxy.update(uri: 'http://proxy2.example.com:8080')
|
||||
end
|
||||
```
|
39
docs/customization/request-options.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Request Options
|
||||
|
||||
Request options can be provided to the connection constructor or set on a per-request basis.
|
||||
All these options are optional.
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|-------------------|-------------------|----------------------------------------------------------------|-------------------------------------------------------------------------|
|
||||
| `:params_encoder` | Class | `Faraday::Utils.nested_params_encoder` (`NestedParamsEncoder`) | A custom class to use as the params encoder. |
|
||||
| `:proxy` | URI, String, Hash | nil | Proxy options, either as a URL or as a Hash of [ProxyOptions]. |
|
||||
| `:bind` | Hash | nil | Hash of bind options. Requires the `:host` and `:port` keys. |
|
||||
| `:timeout` | Integer, Float | nil (adapter default) | The max number of seconds to wait for the request to complete. |
|
||||
| `:open_timeout` | Integer, Float | nil (adapter default) | The max number of seconds to wait for the connection to be established. |
|
||||
| `:read_timeout` | Integer, Float | nil (adapter default) | The max number of seconds to wait for one block to be read. |
|
||||
| `:write_timeout` | Integer, Float | nil (adapter default) | The max number of seconds to wait for one block to be written. |
|
||||
| `:boundary` | String | nil | The boundary string for multipart requests. |
|
||||
| `:context` | Hash | nil | Arbitrary data that you can pass to your request. |
|
||||
| `:on_data` | Proc | nil | A callback that will be called when data is received. See [Streaming] |
|
||||
|
||||
## Example
|
||||
|
||||
```ruby
|
||||
# Request options can be passed to the connection constructor and will be applied to all requests.
|
||||
request_options = {
|
||||
params_encoder: Faraday::FlatParamsEncoder,
|
||||
timeout: 5
|
||||
}
|
||||
|
||||
conn = Faraday.new(request: request_options) do |faraday|
|
||||
# ...
|
||||
end
|
||||
|
||||
# You can then override them on a per-request basis.
|
||||
conn.get('/foo') do |req|
|
||||
req.options.timeout = 10
|
||||
end
|
||||
```
|
||||
|
||||
[ProxyOptions]: /customization/proxy-options.md
|
||||
[SSLOptions]: /advanced/streaming-responses.md
|
35
docs/customization/ssl-options.md
Normal file
@ -0,0 +1,35 @@
|
||||
# SSL Options
|
||||
|
||||
Faraday supports a number of SSL options, which can be provided while initializing the connection.
|
||||
|
||||
| Option | Type | Default | Description |
|
||||
|--------------------|----------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `:verify` | Boolean | true | Verify SSL certificate. Defaults to `true`. |
|
||||
| `:verify_hostname` | Boolean | true | Verify SSL certificate hostname. Defaults to `true`. |
|
||||
| `:hostname` | String | nil | Server hostname for SNI (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLSocket.html#method-i-hostname-3D)). |
|
||||
| `:ca_file` | String | nil | Path to a CA file in PEM format. |
|
||||
| `:ca_path` | String | nil | Path to a CA directory. |
|
||||
| `:verify_mode` | Integer | nil | Any `OpenSSL::SSL::` constant (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL.html)). |
|
||||
| `:cert_store` | OpenSSL::X509::Store | nil | OpenSSL certificate store. |
|
||||
| `:client_cert` | OpenSSL::X509::Certificate | nil | Client certificate. |
|
||||
| `:client_key` | OpenSSL::PKey::RSA, OpenSSL::PKey::DSA | nil | Client private key. |
|
||||
| `:certificate` | OpenSSL::X509::Certificate | nil | Certificate (Excon only). |
|
||||
| `:private_key` | OpenSSL::PKey::RSA | nil | Private key (Excon only). |
|
||||
| `:verify_depth` | Integer | nil | Maximum depth for the certificate chain verification. |
|
||||
| `:version` | Integer | nil | SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-ssl_version-3D)). |
|
||||
| `:min_version` | Integer | nil | Minimum SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-min_version-3D)). |
|
||||
| `:max_version` | Integer | nil | Maximum SSL version (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-max_version-3D)). |
|
||||
| `:ciphers` | String | nil | Ciphers supported (see [SSL docs](https://ruby-doc.org/3.2.2/exts/openssl/OpenSSL/SSL/SSLContext.html#method-i-ciphers-3D)). |
|
||||
|
||||
## Example
|
||||
|
||||
```ruby
|
||||
ssl_options = {
|
||||
ca_file: '/path/to/ca_file',
|
||||
min_version: :TLS1_2
|
||||
}
|
||||
|
||||
conn = Faraday.new(ssl: options) do |faraday|
|
||||
# ...
|
||||
end
|
||||
```
|
51
docs/getting-started/env-object.md
Normal file
@ -0,0 +1,51 @@
|
||||
# The Env Object
|
||||
|
||||
Inspired by Rack, Faraday uses an `env` object to pass data between middleware.
|
||||
This object is initialized at the beginning of the request and passed down the middleware stack.
|
||||
The adapter is then responsible to run the HTTP request and set the `response` property on the `env` object,
|
||||
which is then passed back up the middleware stack.
|
||||
|
||||
You can read more about how the `env` object is used in the [Middleware - How it works](/middleware/index?id=how-it-works) section.
|
||||
|
||||
Because of its nature, the `env` object is a complex structure that holds a lot of information and can
|
||||
therefore be a bit intimidating at first. This page will try to explain the different properties of the `env` object.
|
||||
|
||||
## Properties
|
||||
|
||||
Please also note that these properties are not all available at the same time: while configuration
|
||||
and request properties are available at the beginning of the request, response properties are only
|
||||
available after the request has been performed (i.e. in the `on_complete` callback of middleware).
|
||||
|
||||
|
||||
| Property | Type | Request | Response | Description |
|
||||
|---------------------|----------------------------|:------------------:|:------------------:|-----------------------------|
|
||||
| `:method` | `Symbol` | :heavy_check_mark: | :heavy_check_mark: | The HTTP method to use. |
|
||||
| `:request_body` | `String` | :heavy_check_mark: | :heavy_check_mark: | The request body. |
|
||||
| `:url` | `URI` | :heavy_check_mark: | :heavy_check_mark: | The request URL. |
|
||||
| `:request` | `Faraday::RequestOptions` | :heavy_check_mark: | :heavy_check_mark: | The request options. |
|
||||
| `:request_headers` | `Faraday::Utils::Headers` | :heavy_check_mark: | :heavy_check_mark: | The request headers. |
|
||||
| `:ssl` | `Faraday::SSLOptions` | :heavy_check_mark: | :heavy_check_mark: | The SSL options. |
|
||||
| `:parallel_manager` | `Faraday::ParallelManager` | :heavy_check_mark: | :heavy_check_mark: | The parallel manager. |
|
||||
| `:params` | `Hash` | :heavy_check_mark: | :heavy_check_mark: | The request params. |
|
||||
| `:response` | `Faraday::Response` | :x: | :heavy_check_mark: | The response. |
|
||||
| `:response_headers` | `Faraday::Utils::Headers` | :x: | :heavy_check_mark: | The response headers. |
|
||||
| `:status` | `Integer` | :x: | :heavy_check_mark: | The response status code. |
|
||||
| `:reason_phrase` | `String` | :x: | :heavy_check_mark: | The response reason phrase. |
|
||||
| `:response_body` | `String` | :x: | :heavy_check_mark: | The response body. |
|
||||
|
||||
## Helpers
|
||||
|
||||
The `env` object also provides some helper methods to make it easier to work with the properties.
|
||||
|
||||
| Method | Description |
|
||||
|-------------------------|--------------------------------------------------------------------------------------------------|
|
||||
| `#body`/`#current_body` | Returns the request or response body, based on the presence of `#status`. |
|
||||
| `#success?` | Returns `true` if the response status is in the 2xx range. |
|
||||
| `#needs_body?` | Returns `true` if there's no body yet, and the method is in the set of `Env::MethodsWithBodies`. |
|
||||
| `#clear_body` | Clears the body, if it's present. That includes resetting the `Content-Length` header. |
|
||||
| `#parse_body?` | Returns `true` unless the status indicates otherwise (e.g. 204, 304). |
|
||||
| `#parallel?` | Returns `true` if a parallel manager is available. |
|
||||
| `#stream_response?` | Returns `true` if the `on_data` streaming callback has been provided. |
|
||||
| `#stream_response` | Helper method to implement streaming in adapters. See [Support streaming in your adapter]. |
|
||||
|
||||
[Support streaming in your adapter]: /adapters/custom/streaming.md
|
17
docs/getting-started/errors.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Dealing with Errors
|
||||
|
||||
As an abstraction layer between the user and the underlying HTTP library,
|
||||
it's important that Faraday provides a consistent interface for dealing with errors.
|
||||
This is especially important when dealing with multiple adapters, as each adapter may raise different errors.
|
||||
|
||||
Below is a list of errors that Faraday may raise, and that you should be prepared to handle.
|
||||
|
||||
| Error | Description |
|
||||
|-----------------------------|--------------------------------------------------------------------------------|
|
||||
| `Faraday::Error` | Base class for all Faraday errors, also used for generic or unexpected errors. |
|
||||
| `Faraday::ConnectionFailed` | Raised when the connection to the remote server failed. |
|
||||
| `Faraday::TimeoutError` | Raised when the connection to the remote server timed out. |
|
||||
| `Faraday::SSLError` | Raised when the connection to the remote server failed due to an SSL error. |
|
||||
|
||||
If you add the `raise_error` middleware, Faraday will also raise additional errors for 4xx and 5xx responses.
|
||||
You can find the full list of errors in the [raise_error middleware](/middleware/included/raising-errors) page.
|
266
docs/getting-started/quick-start.md
Normal file
@ -0,0 +1,266 @@
|
||||
# Quick Start
|
||||
|
||||
## Installation
|
||||
|
||||
Add this line to your application’s `Gemfile`:
|
||||
|
||||
```ruby
|
||||
gem 'faraday'
|
||||
```
|
||||
|
||||
And then execute:
|
||||
|
||||
```bash
|
||||
$ bundle
|
||||
```
|
||||
|
||||
Or install it yourself as:
|
||||
|
||||
```bash
|
||||
$ gem install faraday
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Quick requests
|
||||
|
||||
Let's fetch the home page for the wonderful [httpbingo.org](https://httpbingo.org) service.
|
||||
|
||||
You can make a simple `GET` request using `Faraday.get`:
|
||||
|
||||
```ruby
|
||||
response = Faraday.get('http://httpbingo.org')
|
||||
```
|
||||
|
||||
This returns a `Faraday::Response` object with the response status, headers, and body.
|
||||
|
||||
```ruby
|
||||
response.status
|
||||
# => 200
|
||||
|
||||
response.headers
|
||||
# => {"server"=>"Fly/c375678 (2021-04-23)", "content-type"=> ...
|
||||
|
||||
response.body
|
||||
# => "<!DOCTYPE html><html> ...
|
||||
```
|
||||
|
||||
### Faraday Connection
|
||||
|
||||
The recommended way to use Faraday, especially when integrating to 3rd party services and APIs, is to create
|
||||
a `Faraday::Connection`. The connection initializer allows you to set:
|
||||
|
||||
- default request headers & query parameters
|
||||
- network settings like proxy or timeout
|
||||
- common URL base path
|
||||
- Faraday adapter & middleware (see below)
|
||||
|
||||
Create a `Faraday::Connection` by calling `Faraday.new`. You can then call each HTTP verb
|
||||
(`get`, `post`, ...) on your `Faraday::Connection` to perform a request:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(
|
||||
url: 'http://httpbingo.org',
|
||||
params: {param: '1'},
|
||||
headers: {'Content-Type' => 'application/json'}
|
||||
)
|
||||
|
||||
response = conn.post('/post') do |req|
|
||||
req.params['limit'] = 100
|
||||
req.body = {query: 'chunky bacon'}.to_json
|
||||
end
|
||||
# => POST http://httpbingo.org/post?param=1&limit=100
|
||||
```
|
||||
|
||||
### GET, HEAD, DELETE, TRACE
|
||||
|
||||
Faraday supports the following HTTP verbs that typically don't include a request body:
|
||||
|
||||
- `get(url, params = nil, headers = nil)`
|
||||
- `head(url, params = nil, headers = nil)`
|
||||
- `delete(url, params = nil, headers = nil)`
|
||||
- `trace(url, params = nil, headers = nil)`
|
||||
|
||||
You can specify URI query parameters and HTTP headers when making a request.
|
||||
|
||||
```ruby
|
||||
response = conn.get('get', { boom: 'zap' }, { 'User-Agent' => 'myapp' })
|
||||
# => GET http://httpbingo.org/get?boom=zap
|
||||
```
|
||||
|
||||
### POST, PUT, PATCH
|
||||
|
||||
Faraday also supports HTTP verbs with bodies. Instead of query parameters, these
|
||||
accept a request body:
|
||||
|
||||
- `post(url, body = nil, headers = nil)`
|
||||
- `put(url, body = nil, headers = nil)`
|
||||
- `patch(url, body = nil, headers = nil)`
|
||||
|
||||
```ruby
|
||||
# POST 'application/x-www-form-urlencoded' content
|
||||
response = conn.post('post', 'boom=zap')
|
||||
|
||||
# POST JSON content
|
||||
response = conn.post('post', '{"boom": "zap"}',
|
||||
"Content-Type" => "application/json")
|
||||
```
|
||||
|
||||
#### Posting Forms
|
||||
|
||||
Faraday will automatically convert key/value hashes into proper form bodies
|
||||
thanks to the `url_encoded` middleware included in the default connection.
|
||||
|
||||
```ruby
|
||||
# POST 'application/x-www-form-urlencoded' content
|
||||
response = conn.post('post', boom: 'zap')
|
||||
# => POST 'boom=zap' to http://httpbingo.org/post
|
||||
```
|
||||
|
||||
### Detailed HTTP Requests
|
||||
|
||||
Faraday supports a longer style for making requests. This is handy if you need
|
||||
to change many of the defaults, or if the details of the HTTP request change
|
||||
according to method arguments. Each of the HTTP verb helpers can yield a
|
||||
`Faraday::Request` that can be modified before being sent.
|
||||
|
||||
This example shows a hypothetical search endpoint that accepts a JSON request
|
||||
body as the actual search query.
|
||||
|
||||
```ruby
|
||||
response = conn.post('post') do |req|
|
||||
req.params['limit'] = 100
|
||||
req.headers['Content-Type'] = 'application/json'
|
||||
req.body = {query: 'chunky bacon'}.to_json
|
||||
end
|
||||
# => POST http://httpbingo.org/post?limit=100
|
||||
```
|
||||
|
||||
### Using Middleware
|
||||
|
||||
Configuring your connection or request with predefined headers and parameters is a good start,
|
||||
but the real power of Faraday comes from its middleware stack.
|
||||
Middleware are classes that allow you to hook into the request/response cycle and modify the request.
|
||||
They can help you with things like:
|
||||
* adding authentication headers
|
||||
* parsing JSON responses
|
||||
* logging requests and responses
|
||||
* raise errors on 4xx and 5xx responses
|
||||
* and much more!
|
||||
|
||||
For example, let's say you want to call an API that:
|
||||
* requires an authentication token in the `Authorization` header
|
||||
* expects JSON request bodies
|
||||
* returns JSON responses
|
||||
|
||||
and on top of that, you want to automatically raise errors on 4xx and 5xx responses,
|
||||
as well as log all requests and responses.
|
||||
|
||||
You can easily achieve all of the above by adding the necessary middleware to your connection:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
|
||||
# Calls MyAuthStorage.get_auth_token on each request to get the auth token
|
||||
# and sets it in the Authorization header with Bearer scheme.
|
||||
builder.request :authorization, 'Bearer', -> { MyAuthStorage.get_auth_token }
|
||||
|
||||
# Sets the Content-Type header to application/json on each request.
|
||||
# Also, if the request body is a Hash, it will automatically be encoded as JSON.
|
||||
builder.request :json
|
||||
|
||||
# Parses JSON response bodies.
|
||||
# If the response body is not valid JSON, it will raise a Faraday::ParsingError.
|
||||
builder.response :json
|
||||
|
||||
# Raises an error on 4xx and 5xx responses.
|
||||
builder.response :raise_error
|
||||
|
||||
# Logs requests and responses.
|
||||
# By default, it only logs the request method and URL, and the request/response headers.
|
||||
builder.response :logger
|
||||
end
|
||||
|
||||
# A simple example implementation for MyAuthStorage
|
||||
class MyAuthStorage
|
||||
def self.get_auth_token
|
||||
rand(36 ** 8).to_s(36)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The connection can now be used to make requests.
|
||||
|
||||
```ruby
|
||||
begin
|
||||
response = conn.post('post', { payload: 'this ruby hash will become JSON' })
|
||||
rescue Faraday::Error => e
|
||||
# You can handle errors here (4xx/5xx responses, timeouts, etc.)
|
||||
puts e.response[:status]
|
||||
puts e.response[:body]
|
||||
end
|
||||
|
||||
# At this point, you can assume the request was successful
|
||||
puts response.body
|
||||
|
||||
# I, [2023-06-30T14:27:11.776511 #35368] INFO -- request: POST http://httpbingo.org/post
|
||||
# I, [2023-06-30T14:27:11.776646 #35368] INFO -- request: User-Agent: "Faraday v2.7.8"
|
||||
# Authorization: "Bearer wibzjgyh"
|
||||
# Content-Type: "application/json"
|
||||
# I, [2023-06-30T14:27:12.063897 #35368] INFO -- response: Status 200
|
||||
# I, [2023-06-30T14:27:12.064260 #35368] INFO -- response: access-control-allow-credentials: "true"
|
||||
# access-control-allow-origin: "*"
|
||||
# content-type: "application/json; encoding=utf-8"
|
||||
# date: "Fri, 30 Jun 2023 13:27:12 GMT"
|
||||
# content-encoding: "gzip"
|
||||
# transfer-encoding: "chunked"
|
||||
# server: "Fly/a0b91024 (2023-06-13)"
|
||||
# via: "1.1 fly.io"
|
||||
# fly-request-id: "01H467RYRHA0YK4TQSZ7HS8ZFT-lhr"
|
||||
# cf-team: "19ae1592b8000003bbaedcf400000001"
|
||||
```
|
||||
|
||||
Faraday ships with a number of useful middleware, and you can also write your own.
|
||||
To learn more about middleware, please check the [Middleware] section.
|
||||
|
||||
### Swapping Adapters
|
||||
|
||||
Faraday does not make HTTP requests itself, but instead relies on a Faraday adapter to do so.
|
||||
By default, it will use the `Net::HTTP` adapter, which is part of the Ruby standard library.
|
||||
Although `Net::HTTP` is the only adapter that ships with Faraday, there are [many other adapters
|
||||
available as separate gems](https://github.com/lostisland/awesome-faraday#adapters).
|
||||
|
||||
Once you have installed an adapter, you can use it by passing the `adapter` option to `Faraday.new`:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: 'http://httpbingo.org') do |builder|
|
||||
builder.adapter :async_http
|
||||
end
|
||||
```
|
||||
|
||||
To learn more about adapters, including how to write your own, please check the [Adapters] section.
|
||||
|
||||
### Default Connection, Default Adapter
|
||||
|
||||
Remember how we said that Faraday will automatically encode key/value hash
|
||||
bodies into form bodies? Internally, the top level shortcut methods
|
||||
`Faraday.get`, `post`, etc. use a simple default `Faraday::Connection`. The only
|
||||
middleware used for the default connection is `:url_encoded`, which encodes
|
||||
those form hashes, and the `default_adapter`.
|
||||
|
||||
You can change the default adapter or connection. Be careful because they're set globally.
|
||||
|
||||
```ruby
|
||||
Faraday.default_adapter = :async_http # defaults to :net_http
|
||||
|
||||
# The default connection has only `:url_encoded` middleware.
|
||||
# Note that if you create your own connection with middleware, it won't encode
|
||||
# form bodies unless you too include the :url_encoded middleware!
|
||||
Faraday.default_connection = Faraday.new do |conn|
|
||||
conn.request :url_encoded
|
||||
conn.response :logger
|
||||
conn.adapter Faraday.default_adapter
|
||||
end
|
||||
```
|
||||
|
||||
[Adapters]: /adapters/index.md
|
||||
[Middleware]: /middleware/index.md
|
225
docs/getting-started/rest-client-migration.md
Normal file
@ -0,0 +1,225 @@
|
||||
# Migrating from `rest-client` to `Faraday`
|
||||
|
||||
The `rest-client` gem is in maintenance mode, and developers are encouraged to migrate to actively maintained alternatives like [`faraday`](https://github.com/lostisland/faraday). This guide highlights common usage patterns in `rest-client` and how to migrate them to `faraday`.
|
||||
|
||||
---
|
||||
|
||||
## Quick Comparison
|
||||
|
||||
| Task | rest-client example | faraday example |
|
||||
| ----------------- | -------------------------------------------------------- | -------------------------------------------------------------------------- |
|
||||
| Simple GET | `RestClient.get("https://httpbingo.org/get")` | `Faraday.get("https://httpbingo.org/get")` |
|
||||
| GET with params | `RestClient.get(url, params: { id: 1 })` | `Faraday.get(url, { id: 1 })` |
|
||||
| POST form data | `RestClient.post(url, { a: 1 })` | `Faraday.post(url, { a: 1 })` |
|
||||
| POST JSON | `RestClient.post(url, obj.to_json, content_type: :json)` | `Faraday.post(url, obj.to_json, { 'Content-Type' => 'application/json' })` |
|
||||
| Custom headers | `RestClient.get(url, { Authorization: 'Bearer token' })` | `Faraday.get(url, nil, { 'Authorization' => 'Bearer token' })` |
|
||||
| Get response body | `response.body` | `response.body` |
|
||||
| Get status code | `response.code` | `response.status` |
|
||||
| Get headers | `response.headers` (returns `Hash<Symbol, String>`) | `response.headers` (returns `Hash<String, String>`) |
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
In your `Gemfile`, replace `rest-client` with:
|
||||
|
||||
```ruby
|
||||
gem "faraday"
|
||||
```
|
||||
|
||||
Then run:
|
||||
|
||||
```sh
|
||||
bundle install
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Basic HTTP Requests
|
||||
|
||||
### GET request
|
||||
|
||||
**rest-client:**
|
||||
|
||||
```ruby
|
||||
RestClient.get("https://httpbingo.org/get")
|
||||
```
|
||||
|
||||
**faraday:**
|
||||
|
||||
```ruby
|
||||
Faraday.get("https://httpbingo.org/get")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET with Params
|
||||
|
||||
**rest-client:**
|
||||
|
||||
```ruby
|
||||
RestClient.get("https://httpbingo.org/get", params: { id: 1, foo: "bar" })
|
||||
```
|
||||
|
||||
**faraday:**
|
||||
|
||||
```ruby
|
||||
Faraday.get("https://httpbingo.org/get", { id: 1, foo: "bar" })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST Requests
|
||||
|
||||
**rest-client:**
|
||||
|
||||
```ruby
|
||||
RestClient.post("https://httpbingo.org/post", { foo: "bar" })
|
||||
```
|
||||
|
||||
**faraday:**
|
||||
|
||||
```ruby
|
||||
Faraday.post("https://httpbingo.org/post", { foo: "bar" })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Sending JSON
|
||||
|
||||
**rest-client:**
|
||||
|
||||
```ruby
|
||||
RestClient.post("https://httpbingo.org/post", { foo: "bar" }.to_json, content_type: :json)
|
||||
```
|
||||
|
||||
**faraday (manual):**
|
||||
|
||||
```ruby
|
||||
Faraday.post("https://httpbingo.org/post", { foo: "bar" }.to_json, { 'Content-Type' => 'application/json' })
|
||||
```
|
||||
|
||||
**faraday (with middleware):**
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: "https://httpbingo.org") do |f|
|
||||
f.request :json # encode request body as JSON and set Content-Type
|
||||
f.response :json # parse response body as JSON
|
||||
end
|
||||
|
||||
conn.post("/post", { foo: "bar" })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Handling Responses
|
||||
|
||||
**rest-client:**
|
||||
|
||||
```ruby
|
||||
response = RestClient.get("https://httpbingo.org/headers")
|
||||
response.code # => 200
|
||||
response.body # => "..."
|
||||
response.headers # => { content_type: "application/json", ... }
|
||||
```
|
||||
|
||||
**faraday:**
|
||||
|
||||
> notice headers Hash keys are stringified, not symbolized like in rest-client
|
||||
|
||||
```ruby
|
||||
response = Faraday.get("https://httpbingo.org/headers")
|
||||
response.status # => 200
|
||||
response.body # => "..."
|
||||
response.headers # => { "content-type" => "application/json", ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
**rest-client:**
|
||||
|
||||
```ruby
|
||||
begin
|
||||
RestClient.get("https://httpbingo.org/status/404")
|
||||
rescue RestClient::NotFound => e
|
||||
puts e.response.code # 404
|
||||
end
|
||||
```
|
||||
|
||||
**faraday:**
|
||||
|
||||
> By default, Faraday does **not** raise exceptions for HTTP errors (like 404 or 500); it simply returns the response. If you want exceptions to be raised on HTTP error responses, include the `:raise_error` middleware.
|
||||
>
|
||||
> With `:raise_error`, Faraday will raise `Faraday::ResourceNotFound` for 404s and other exceptions for other 4xx/5xx responses.
|
||||
>
|
||||
> See also:
|
||||
>
|
||||
> * [Dealing with Errors](getting-started/errors.md)
|
||||
> * [Raising Errors](middleware/included/raising-errors.md)
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: "https://httpbingo.org") do |f|
|
||||
f.response :raise_error
|
||||
end
|
||||
|
||||
begin
|
||||
conn.get("/status/404")
|
||||
rescue Faraday::ResourceNotFound => e
|
||||
puts e.response[:status] # 404
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced Request Configuration
|
||||
|
||||
**rest-client:**
|
||||
|
||||
```ruby
|
||||
RestClient::Request.execute(method: :get, url: "https://httpbingo.org/get", timeout: 10)
|
||||
```
|
||||
|
||||
**faraday:**
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: "https://httpbingo.org", request: { timeout: 10 })
|
||||
conn.get("/get")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Headers
|
||||
|
||||
**rest-client:**
|
||||
|
||||
```ruby
|
||||
RestClient.get("https://httpbingo.org/headers", { Authorization: "Bearer token" })
|
||||
```
|
||||
|
||||
**faraday:**
|
||||
|
||||
> Notice headers Hash expects stringified keys.
|
||||
|
||||
```ruby
|
||||
Faraday.get("https://httpbingo.org/headers", nil, { "Authorization" => "Bearer token" })
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Redirects
|
||||
|
||||
**rest-client:**
|
||||
Automatically follows GET/HEAD redirects by default.
|
||||
|
||||
**faraday:**
|
||||
Use the `follow_redirects` middleware (not included by default):
|
||||
|
||||
```ruby
|
||||
require "faraday/follow_redirects"
|
||||
|
||||
conn = Faraday.new(url: "https://httpbingo.org") do |f|
|
||||
f.response :follow_redirects
|
||||
end
|
||||
```
|
121
docs/index.html
Normal file
@ -0,0 +1,121 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" type="image/x-icon" href="_media/favicon.png">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
|
||||
<!-- HTML Meta Tags -->
|
||||
<title>Faraday Docs</title>
|
||||
<meta name="description" content="Faraday is an HTTP client library abstraction layer that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.">
|
||||
|
||||
<!-- OpenGraph Meta Tags -->
|
||||
<meta property="og:url" content="https://lostisland.github.io/faraday/#/">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:title" content="Faraday Docs">
|
||||
<meta property="og:description" content="Faraday is an HTTP client library abstraction layer that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.">
|
||||
<meta property="og:image" content="https://lostisland.github.io/faraday/_media/repo-card.png">
|
||||
|
||||
<!-- Twitter Meta Tags -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta property="twitter:domain" content="lostisland.github.io">
|
||||
<meta property="twitter:url" content="https://lostisland.github.io/faraday/#/">
|
||||
<meta name="twitter:title" content="Faraday Docs">
|
||||
<meta name="twitter:description" content="Faraday is an HTTP client library abstraction layer that provides a common interface over many adapters (such as Net::HTTP) and embraces the concept of Rack middleware when processing the request/response cycle.">
|
||||
<meta name="twitter:image" content="https://lostisland.github.io/faraday/_media/repo-card.png">
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/style.min.css"
|
||||
title="docsify-darklight-theme"
|
||||
type="text/css"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="//cdn.jsdelivr.net/npm/prism-themes/themes/prism-material-light.min.css"
|
||||
id="prism-theme"
|
||||
type="text/css"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<!-- Docsify plugins -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-edit-on-github"></script>
|
||||
|
||||
<!-- Docsify config -->
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'Faraday',
|
||||
repo: 'lostisland/faraday',
|
||||
logo: '_media/home-logo.svg',
|
||||
homepage: 'index.md',
|
||||
search: true,
|
||||
loadSidebar: true,
|
||||
subMaxLevel: 4,
|
||||
auto2top: true,
|
||||
darklightTheme: {
|
||||
dark: {
|
||||
accent: '#EE4266',
|
||||
prismTheme: 'prism-material-dark'
|
||||
},
|
||||
light: {
|
||||
accent: '#EE4266',
|
||||
prismTheme: 'prism-material-light'
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
EditOnGithubPlugin.create(
|
||||
'https://github.com/lostisland/faraday/blob/main/docs/',
|
||||
null,
|
||||
'Edit this page on GitHub'
|
||||
),
|
||||
function pageFooter(hook, _vm) {
|
||||
var footer = [
|
||||
'<hr/>',
|
||||
'<footer>',
|
||||
'<span>© 2009 - 2023, the Faraday Team. </span>',
|
||||
'<span>Website and branding design by <a href="https://elelopic.design" target="_blank" rel="noopener">Elena Lo Piccolo</a>.</span>',
|
||||
'</footer>',
|
||||
].join('');
|
||||
|
||||
hook.afterEach(function (html) {
|
||||
return html + footer;
|
||||
});
|
||||
},
|
||||
function prismThemeSwitcher(hook, _vm) {
|
||||
// Switch Prism theme based on docsify-darklight-theme setting
|
||||
let lightTheme = '//cdn.jsdelivr.net/npm/prism-themes/themes/prism-one-light.min.css';
|
||||
let darkTheme = '//cdn.jsdelivr.net/npm/prism-themes/themes/prism-one-dark.min.css';
|
||||
|
||||
let switchTheme = () => {
|
||||
console.log('Theme changed');
|
||||
let theme = localStorage.getItem('DARK_LIGHT_THEME')
|
||||
let link = document.getElementById('prism-theme');
|
||||
link.setAttribute('href', theme === 'dark' ? darkTheme : lightTheme);
|
||||
}
|
||||
|
||||
hook.ready(() => {
|
||||
document.getElementById('main').addEventListener('click', switchTheme);
|
||||
switchTheme();
|
||||
});
|
||||
},
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<!-- Docsify v4 -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
||||
<!-- Docsify Darklight Theme -->
|
||||
<script
|
||||
src="//cdn.jsdelivr.net/npm/docsify-darklight-theme@latest/dist/index.min.js"
|
||||
type="text/javascript">
|
||||
</script>
|
||||
<!-- Prism Ruby highlight -->
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@v1.x/components/prism-ruby.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/prismjs@v1.x/plugins/autoloader/prism-autoloader.min.js"></script>
|
||||
|
||||
<!-- Other Plugins -->
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||||
<script src="//cdn.jsdelivr.net/npm/docsify-copy-code/dist/docsify-copy-code.min.js"></script>
|
||||
</body>
|
||||
</html>
|
28
docs/index.md
Normal file
@ -0,0 +1,28 @@
|
||||
# 
|
||||
|
||||
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.
|
||||
|
||||
## 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!
|
||||
|
||||
## Who uses Faraday?
|
||||
|
||||
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)
|
||||
```
|
65
docs/middleware/included/authentication.md
Normal file
@ -0,0 +1,65 @@
|
||||
# Authentication
|
||||
|
||||
The `Faraday::Request::Authorization` middleware allows you to automatically add an `Authorization` header
|
||||
to your requests. It also features a handy helper to manage Basic authentication.
|
||||
**Please note the way you use this middleware in Faraday 1.x is different**,
|
||||
examples are available at the bottom of this page.
|
||||
|
||||
```ruby
|
||||
Faraday.new(...) do |conn|
|
||||
conn.request :authorization, 'Bearer', 'authentication-token'
|
||||
end
|
||||
```
|
||||
|
||||
### With a proc
|
||||
|
||||
You can also provide a proc, which will be evaluated on each request:
|
||||
|
||||
```ruby
|
||||
Faraday.new(...) do |conn|
|
||||
conn.request :authorization, 'Bearer', -> { MyAuthStorage.get_auth_token }
|
||||
end
|
||||
```
|
||||
|
||||
If the proc takes an argument, it will receive the forwarded `env` (see [The Env Object](getting-started/env-object.md)):
|
||||
|
||||
```ruby
|
||||
Faraday.new(...) do |conn|
|
||||
conn.request :authorization, 'Bearer', ->(env) { MyAuthStorage.get_auth_token(env) }
|
||||
end
|
||||
```
|
||||
|
||||
### Basic Authentication
|
||||
|
||||
The middleware will automatically Base64 encode your Basic username and password:
|
||||
|
||||
```ruby
|
||||
Faraday.new(...) do |conn|
|
||||
conn.request :authorization, :basic, 'username', 'password'
|
||||
end
|
||||
```
|
||||
|
||||
### Faraday 1.x usage
|
||||
|
||||
In Faraday 1.x, the way you use this middleware is slightly different:
|
||||
|
||||
```ruby
|
||||
# Basic Auth request
|
||||
# Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
|
||||
Faraday.new(...) do |conn|
|
||||
conn.request :basic_auth, 'username', 'password'
|
||||
end
|
||||
|
||||
# Token Auth request
|
||||
# `options` are automatically converted into `key=value` format
|
||||
# Authorization: Token authentication-token <options>
|
||||
Faraday.new(...) do |conn|
|
||||
conn.request :token_auth, 'authentication-token', **options
|
||||
end
|
||||
|
||||
# Generic Auth Request
|
||||
# Authorization: Bearer authentication-token
|
||||
Faraday.new(...) do |conn|
|
||||
conn.request :authorization, 'Bearer', 'authentication-token'
|
||||
end
|
||||
```
|
37
docs/middleware/included/index.md
Normal file
@ -0,0 +1,37 @@
|
||||
# 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**.
|
||||
The former usually deal with the request, encoding the parameters or setting headers.
|
||||
The latter instead activate after the request is completed and a response has been received, like
|
||||
parsing the response body, logging useful info or checking the response status.
|
||||
|
||||
### Request Middleware
|
||||
|
||||
**Request middleware** can modify Request details before the Adapter runs. Most
|
||||
middleware set Header values or transform the request body based on the
|
||||
content type.
|
||||
|
||||
* [`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.
|
||||
|
||||
|
||||
### Response Middleware
|
||||
|
||||
**Response middleware** receives the response from the adapter and can modify its details
|
||||
before returning it.
|
||||
|
||||
* [`Json Response`][json-response] parses response body into a hash of key/value pairs.
|
||||
* [`Logger`][logger] logs both the request and the response body and headers.
|
||||
* [`RaiseError`][raise_error] checks the response HTTP code and raises an exception if it is a 4xx or 5xx code.
|
||||
|
||||
|
||||
[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
|
34
docs/middleware/included/instrumentation.md
Normal file
@ -0,0 +1,34 @@
|
||||
# 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.
|
||||
They default to `request.faraday` and `ActiveSupport::Notifications` respectively, but you can provide your own:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(...) do |f|
|
||||
f.request :instrumentation, name: 'custom_name', instrumenter: MyInstrumenter
|
||||
...
|
||||
end
|
||||
```
|
||||
|
||||
### Example Usage
|
||||
|
||||
The `Instrumentation` middleware will use `ActiveSupport::Notifications` by default as instrumenter,
|
||||
allowing you to subscribe to the default event name and instrument requests:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new('http://example.com') do |f|
|
||||
f.request :instrumentation
|
||||
...
|
||||
end
|
||||
|
||||
ActiveSupport::Notifications.subscribe('request.faraday') do |name, starts, ends, _, env|
|
||||
url = env[:url]
|
||||
http_method = env[:method].to_s.upcase
|
||||
duration = ends - starts
|
||||
$stdout.puts '[%s] %s %s (%.3f s)' % [url.host, http_method, url.request_uri, duration]
|
||||
end
|
||||
|
||||
conn.get('/search', { a: 1, b: 2 })
|
||||
#=> [example.com] GET /search?a=1&b=2 (0.529 s)
|
||||
```
|
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
|
||||
```
|
114
docs/middleware/included/logging.md
Normal file
@ -0,0 +1,114 @@
|
||||
# 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.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||
faraday.response :logger # log requests and responses to $stdout
|
||||
end
|
||||
|
||||
conn.get
|
||||
# => INFO -- request: GET http://httpbingo.org/
|
||||
# => DEBUG -- request: User-Agent: "Faraday v1.0.0"
|
||||
# => INFO -- response: Status 301
|
||||
# => DEBUG -- response: date: "Sun, 19 May 2019 16:05:40 GMT"
|
||||
```
|
||||
|
||||
### Customize the logger
|
||||
|
||||
By default, the `Logger` middleware uses the Ruby `Logger.new($stdout)`.
|
||||
You can customize it to use any logger you want by providing it when you add the middleware to the stack:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||
faraday.response :logger, MyLogger.new($stdout)
|
||||
end
|
||||
```
|
||||
|
||||
### Include and exclude headers/bodies
|
||||
|
||||
By default, the `logger` middleware logs only headers for security reasons, however, you can configure it
|
||||
to log bodies and errors as well, or disable headers logging if you need to.
|
||||
To do so, simply provide a configuration hash when you add the middleware to the stack:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||
faraday.response :logger, nil, { headers: true, bodies: true, errors: true }
|
||||
end
|
||||
```
|
||||
|
||||
You can also configure the `logger` middleware with a little more complex settings
|
||||
like "do not log the request bodies, but log the response bodies".
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||
faraday.response :logger, nil, { bodies: { request: false, response: true } }
|
||||
end
|
||||
```
|
||||
|
||||
Please note this only works with the default formatter.
|
||||
|
||||
### Filter sensitive information
|
||||
|
||||
You can filter sensitive information from Faraday logs using a regex matcher:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||
faraday.response :logger do | logger |
|
||||
logger.filter(/(api_key=)([^&]+)/, '\1[REMOVED]')
|
||||
end
|
||||
end
|
||||
|
||||
conn.get('/', api_key: 'secret')
|
||||
# => INFO -- request: GET http://httpbingo.org/?api_key=[REMOVED]
|
||||
# => DEBUG -- request: User-Agent: "Faraday v1.0.0"
|
||||
# => INFO -- response: Status 301
|
||||
# => DEBUG -- response: date: "Sun, 19 May 2019 16:12:36 GMT"
|
||||
```
|
||||
|
||||
### Change log level
|
||||
|
||||
By default, the `logger` middleware logs on the `info` log level. It is possible to configure
|
||||
the severity by providing the `log_level` option:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||
faraday.response :logger, nil, { bodies: true, log_level: :debug }
|
||||
end
|
||||
```
|
||||
|
||||
### Customize the formatter
|
||||
|
||||
You can also provide a custom formatter to control how requests, responses and errors are logged.
|
||||
Any custom formatter MUST implement the `request` and `response` method, with one argument which
|
||||
will be passed being the Faraday environment.
|
||||
Any custom formatter CAN implement the `exception` method,
|
||||
with one argument which will be passed being the exception (StandardError).
|
||||
If you make your formatter inheriting from `Faraday::Logging::Formatter`,
|
||||
then the methods `debug`, `info`, `warn`, `error` and `fatal` are automatically delegated to the logger.
|
||||
|
||||
```ruby
|
||||
class MyFormatter < Faraday::Logging::Formatter
|
||||
def request(env)
|
||||
# Build a custom message using `env`
|
||||
info('Request') { 'Sending Request' }
|
||||
end
|
||||
|
||||
def response(env)
|
||||
# Build a custom message using `env`
|
||||
info('Response') { 'Response Received' }
|
||||
end
|
||||
|
||||
def exception(exc)
|
||||
# Build a custom message using `exc`
|
||||
info('Error') { 'Error Raised' }
|
||||
end
|
||||
end
|
||||
|
||||
conn = Faraday.new(url: 'http://httpbingo.org/api_key=s3cr3t') do |faraday|
|
||||
faraday.response :logger, nil, formatter: MyFormatter
|
||||
end
|
||||
```
|
90
docs/middleware/included/raising-errors.md
Normal file
@ -0,0 +1,90 @@
|
||||
# Raising Errors
|
||||
|
||||
The `RaiseError` middleware raises a `Faraday::Error` exception if an HTTP
|
||||
response returns with a 4xx or 5xx status code.
|
||||
This greatly increases the ease of use of Faraday, as you don't have to check
|
||||
the response status code manually.
|
||||
These errors add to the list of default errors [raised by Faraday](getting-started/errors.md).
|
||||
|
||||
All exceptions are initialized with a hash containing the response `status`, `headers`, and `body`.
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||
faraday.response :raise_error # raise Faraday::Error on status code 4xx or 5xx
|
||||
end
|
||||
|
||||
begin
|
||||
conn.get('/wrong-url') # => Assume this raises a 404 response
|
||||
rescue Faraday::ResourceNotFound => e
|
||||
e.response_status #=> 404
|
||||
e.response_headers #=> { ... }
|
||||
e.response_body #=> "..."
|
||||
end
|
||||
```
|
||||
|
||||
Specific exceptions are raised based on the HTTP Status code of the response.
|
||||
|
||||
## 4xx Errors
|
||||
|
||||
An HTTP status in the 400-499 range typically represents an error
|
||||
by the client. They raise error classes inheriting from `Faraday::ClientError`.
|
||||
|
||||
| Status Code | Exception Class |
|
||||
|---------------------------------------------------------------------|-------------------------------------|
|
||||
| [400](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400) | `Faraday::BadRequestError` |
|
||||
| [401](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) | `Faraday::UnauthorizedError` |
|
||||
| [403](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403) | `Faraday::ForbiddenError` |
|
||||
| [404](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/404) | `Faraday::ResourceNotFound` |
|
||||
| [407](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/407) | `Faraday::ProxyAuthError` |
|
||||
| [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) | `Faraday::RequestTimeoutError` |
|
||||
| [409](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/409) | `Faraday::ConflictError` |
|
||||
| [422](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/422) | `Faraday::UnprocessableEntityError` |
|
||||
| [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) | `Faraday::TooManyRequestsError` |
|
||||
| 4xx (any other) | `Faraday::ClientError` |
|
||||
|
||||
## 5xx Errors
|
||||
|
||||
An HTTP status in the 500-599 range represents a server error, and raises a
|
||||
`Faraday::ServerError` exception.
|
||||
|
||||
It's important to note that this exception is only returned if we receive a response and the
|
||||
HTTP status in such response is in the 500-599 range.
|
||||
Other kind of errors normally attributed to errors in the 5xx range (such as timeouts, failure to connect, etc...)
|
||||
are raised as specific exceptions inheriting from `Faraday::Error`.
|
||||
See [Faraday Errors](getting-started/errors.md) for more information on these.
|
||||
|
||||
### Missing HTTP status
|
||||
|
||||
The HTTP response status may be nil due to a malformed HTTP response from the
|
||||
server, or a bug in the underlying HTTP library. This is considered a server error
|
||||
and raised as `Faraday::NilStatusError`, which inherits from `Faraday::ServerError`.
|
||||
|
||||
## Middleware Options
|
||||
|
||||
The behavior of this middleware can be customized with the following options:
|
||||
|
||||
| Option | Default | Description |
|
||||
|----------------------|---------|-------------|
|
||||
| **include_request** | true | When true, exceptions are initialized with request information including `method`, `url`, `url_path`, `params`, `headers`, and `body`. |
|
||||
| **allowed_statuses** | [] | An array of status codes that should not raise an error. |
|
||||
|
||||
### Example Usage
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(url: 'http://httpbingo.org') do |faraday|
|
||||
faraday.response :raise_error, include_request: true, allowed_statuses: [404]
|
||||
end
|
||||
|
||||
begin
|
||||
conn.get('/wrong-url') # => Assume this raises a 404 response
|
||||
conn.get('/protected-url') # => Assume this raises a 401 response
|
||||
rescue Faraday::UnauthorizedError => e
|
||||
e.response[:status] # => 401
|
||||
e.response[:headers] # => { ... }
|
||||
e.response[:body] # => "..."
|
||||
e.response[:request][:url_path] # => "/protected-url"
|
||||
end
|
||||
```
|
||||
|
||||
In this example, a `Faraday::UnauthorizedError` exception is raised for the `/protected-url` request, while the
|
||||
`/wrong-url` request does not raise an error because the status code `404` is in the `allowed_statuses` array.
|
31
docs/middleware/included/url-encoding.md
Normal file
@ -0,0 +1,31 @@
|
||||
# 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 in the [Request Options](customization/request-options.md).
|
||||
|
||||
|
||||
### Example Usage
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new(...) do |f|
|
||||
f.request :url_encoded
|
||||
...
|
||||
end
|
||||
|
||||
conn.post('/', { a: 1, b: 2 })
|
||||
# POST with
|
||||
# Content-Type: application/x-www-form-urlencoded
|
||||
# Body: a=1&b=2
|
||||
```
|
||||
|
||||
Complex structures can also be passed
|
||||
|
||||
```ruby
|
||||
conn.post('/', { a: [1, 3], b: { c: 2, d: 4} })
|
||||
# POST with
|
||||
# Content-Type: application/x-www-form-urlencoded
|
||||
# Body: a%5B%5D=1&a%5B%5D=3&b%5Bc%5D=2&b%5Bd%5D=4
|
||||
```
|
||||
|
||||
[customize]: ../usage/customize#changing-how-parameters-are-serialized
|
200
docs/middleware/index.md
Normal file
@ -0,0 +1,200 @@
|
||||
# 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.
|
||||
|
||||
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
|
||||
|
||||
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'
|
||||
|
||||
conn = Faraday.new do |f|
|
||||
f.request :json # encode req bodies as JSON
|
||||
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
|
||||
response = conn.get("http://httpbingo.org/get")
|
||||
```
|
||||
|
||||
### How it Works
|
||||
|
||||
A `Faraday::Connection` uses a `Faraday::RackBuilder` to assemble a
|
||||
Rack-inspired middleware stack for making HTTP requests. Each middleware runs
|
||||
and passes an Env object around to the next one. After the final middleware has
|
||||
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/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.
|
||||
|
||||
Say you have the following:
|
||||
|
||||
```ruby
|
||||
Faraday.new(...) do |conn|
|
||||
conn.request :authorization
|
||||
conn.response :json
|
||||
conn.response :parse_dates
|
||||
end
|
||||
```
|
||||
|
||||
This will result into a middleware stack like this:
|
||||
|
||||
```ruby
|
||||
authorization do
|
||||
# authorization request hook
|
||||
json do
|
||||
# json request hook
|
||||
parse_dates do
|
||||
# parse_dates request hook
|
||||
response = adapter.perform(request)
|
||||
# parse_dates response hook
|
||||
end
|
||||
# json response hook
|
||||
end
|
||||
# authorization response hook
|
||||
end
|
||||
```
|
||||
|
||||
In this example, you can see that `parse_dates` is the LAST middleware processing the request, and the FIRST middleware processing the response.
|
||||
This is why it's important for the adapter to always be at the end of the middleware list.
|
||||
|
||||
### Using Middleware
|
||||
|
||||
Calling `use` is the most basic way to add middleware to your stack, but most
|
||||
middleware is conveniently registered in the `request`, `response` or `adapter`
|
||||
namespaces. All four methods are equivalent apart from the namespacing.
|
||||
|
||||
For example, the `Faraday::Request::UrlEncoded` middleware registers itself in
|
||||
`Faraday::Request` so it can be added with `request`. These two are equivalent:
|
||||
|
||||
```ruby
|
||||
# add by symbol, lookup from Faraday::Request,
|
||||
# Faraday::Response and Faraday::Adapter registries
|
||||
conn = Faraday.new do |f|
|
||||
f.request :url_encoded
|
||||
f.response :logger
|
||||
f.adapter :net_http
|
||||
end
|
||||
```
|
||||
|
||||
or:
|
||||
|
||||
```ruby
|
||||
# identical, but add the class directly instead of using lookups
|
||||
conn = Faraday.new do |f|
|
||||
f.use Faraday::Request::UrlEncoded
|
||||
f.use Faraday::Response::Logger
|
||||
f.use Faraday::Adapter::NetHttp
|
||||
end
|
||||
```
|
||||
|
||||
This is also the place to pass options. For example:
|
||||
|
||||
```ruby
|
||||
conn = Faraday.new do |f|
|
||||
f.request :logger, bodies: true
|
||||
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.
|
||||
|
||||
### Detailed Example
|
||||
|
||||
Here's a more realistic example:
|
||||
|
||||
```ruby
|
||||
Faraday.new(...) do |conn|
|
||||
# POST/PUT params encoder
|
||||
conn.request :url_encoded
|
||||
|
||||
# Logging of requests/responses
|
||||
conn.response :logger
|
||||
|
||||
# Last middleware must be the adapter
|
||||
conn.adapter :net_http
|
||||
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.
|
||||
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
|
||||
"Content-Type" for the request is explicitly stating which middleware should
|
||||
process it.
|
119
examples/client_spec.rb
Normal file
@ -0,0 +1,119 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Requires Ruby with rspec and faraday gems.
|
||||
# rspec client_spec.rb
|
||||
|
||||
require 'faraday'
|
||||
require 'json'
|
||||
|
||||
# Example API client
|
||||
class Client
|
||||
def initialize(conn)
|
||||
@conn = conn
|
||||
end
|
||||
|
||||
def httpbingo(jname, params: {})
|
||||
res = @conn.get("/#{jname}", params)
|
||||
data = JSON.parse(res.body)
|
||||
data['origin']
|
||||
end
|
||||
|
||||
def foo(params)
|
||||
res = @conn.post('/foo', JSON.dump(params))
|
||||
res.status
|
||||
end
|
||||
end
|
||||
|
||||
RSpec.describe Client do
|
||||
let(:stubs) { Faraday::Adapter::Test::Stubs.new }
|
||||
let(:conn) { Faraday.new { |b| b.adapter(:test, stubs) } }
|
||||
let(:client) { Client.new(conn) }
|
||||
|
||||
it 'parses origin' do
|
||||
stubs.get('/ip') do |env|
|
||||
# optional: you can inspect the Faraday::Env
|
||||
expect(env.url.path).to eq('/ip')
|
||||
[
|
||||
200,
|
||||
{ 'Content-Type': 'application/javascript' },
|
||||
'{"origin": "127.0.0.1"}'
|
||||
]
|
||||
end
|
||||
|
||||
# uncomment to trigger stubs.verify_stubbed_calls failure
|
||||
# stubs.get('/unused') { [404, {}, ''] }
|
||||
|
||||
expect(client.httpbingo('ip')).to eq('127.0.0.1')
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
it 'handles 404' do
|
||||
stubs.get('/api') do
|
||||
[
|
||||
404,
|
||||
{ 'Content-Type': 'application/javascript' },
|
||||
'{}'
|
||||
]
|
||||
end
|
||||
expect(client.httpbingo('api')).to be_nil
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
it 'handles exception' do
|
||||
stubs.get('/api') do
|
||||
raise Faraday::ConnectionFailed
|
||||
end
|
||||
|
||||
expect { client.httpbingo('api') }.to raise_error(Faraday::ConnectionFailed)
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
context 'When the test stub is run in strict_mode' do
|
||||
let(:stubs) { Faraday::Adapter::Test::Stubs.new(strict_mode: true) }
|
||||
|
||||
it 'verifies the all parameter values are identical' do
|
||||
stubs.get('/api?abc=123') do
|
||||
[
|
||||
200,
|
||||
{ 'Content-Type': 'application/javascript' },
|
||||
'{"origin": "127.0.0.1"}'
|
||||
]
|
||||
end
|
||||
|
||||
# uncomment to raise Stubs::NotFound
|
||||
# expect(client.httpbingo('api', params: { abc: 123, foo: 'Kappa' })).to eq('127.0.0.1')
|
||||
expect(client.httpbingo('api', params: { abc: 123 })).to eq('127.0.0.1')
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
end
|
||||
|
||||
context 'When the Faraday connection is configured with FlatParamsEncoder' do
|
||||
let(:conn) { Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) { |b| b.adapter(:test, stubs) } }
|
||||
|
||||
it 'handles the same multiple URL parameters' do
|
||||
stubs.get('/api?a=x&a=y&a=z') { [200, { 'Content-Type' => 'application/json' }, '{"origin": "127.0.0.1"}'] }
|
||||
|
||||
# uncomment to raise Stubs::NotFound
|
||||
# expect(client.httpbingo('api', params: { a: %w[x y] })).to eq('127.0.0.1')
|
||||
expect(client.httpbingo('api', params: { a: %w[x y z] })).to eq('127.0.0.1')
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
end
|
||||
|
||||
context 'When you want to test the body, you can use a proc as well as string' do
|
||||
it 'tests with a string' do
|
||||
stubs.post('/foo', '{"name":"YK"}') { [200, {}, ''] }
|
||||
|
||||
expect(client.foo(name: 'YK')).to eq 200
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
it 'tests with a proc' do
|
||||
check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }
|
||||
stubs.post('/foo', check) { [200, {}, ''] }
|
||||
|
||||
expect(client.foo(name: 'YK', created_at: Time.now)).to eq 200
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
end
|
||||
end
|
144
examples/client_test.rb
Normal file
@ -0,0 +1,144 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Requires Ruby with test-unit and faraday gems.
|
||||
# ruby client_test.rb
|
||||
|
||||
require 'faraday'
|
||||
require 'json'
|
||||
require 'test/unit'
|
||||
|
||||
# Example API client
|
||||
class Client
|
||||
def initialize(conn)
|
||||
@conn = conn
|
||||
end
|
||||
|
||||
def httpbingo(jname, params: {})
|
||||
res = @conn.get("/#{jname}", params)
|
||||
data = JSON.parse(res.body)
|
||||
data['origin']
|
||||
end
|
||||
|
||||
def foo(params)
|
||||
res = @conn.post('/foo', JSON.dump(params))
|
||||
res.status
|
||||
end
|
||||
end
|
||||
|
||||
# Example API client test
|
||||
class ClientTest < Test::Unit::TestCase
|
||||
def test_httpbingo_name
|
||||
stubs = Faraday::Adapter::Test::Stubs.new
|
||||
stubs.get('/api') do |env|
|
||||
# optional: you can inspect the Faraday::Env
|
||||
assert_equal '/api', env.url.path
|
||||
[
|
||||
200,
|
||||
{ 'Content-Type': 'application/javascript' },
|
||||
'{"origin": "127.0.0.1"}'
|
||||
]
|
||||
end
|
||||
|
||||
# uncomment to trigger stubs.verify_stubbed_calls failure
|
||||
# stubs.get('/unused') { [404, {}, ''] }
|
||||
|
||||
cli = client(stubs)
|
||||
assert_equal '127.0.0.1', cli.httpbingo('api')
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
def test_httpbingo_not_found
|
||||
stubs = Faraday::Adapter::Test::Stubs.new
|
||||
stubs.get('/api') do
|
||||
[
|
||||
404,
|
||||
{ 'Content-Type': 'application/javascript' },
|
||||
'{}'
|
||||
]
|
||||
end
|
||||
|
||||
cli = client(stubs)
|
||||
assert_nil cli.httpbingo('api')
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
def test_httpbingo_exception
|
||||
stubs = Faraday::Adapter::Test::Stubs.new
|
||||
stubs.get('/api') do
|
||||
raise Faraday::ConnectionFailed
|
||||
end
|
||||
|
||||
cli = client(stubs)
|
||||
assert_raise Faraday::ConnectionFailed do
|
||||
cli.httpbingo('api')
|
||||
end
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
def test_strict_mode
|
||||
stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true)
|
||||
stubs.get('/api?abc=123') do
|
||||
[
|
||||
200,
|
||||
{ 'Content-Type': 'application/javascript' },
|
||||
'{"origin": "127.0.0.1"}'
|
||||
]
|
||||
end
|
||||
|
||||
cli = client(stubs)
|
||||
assert_equal '127.0.0.1', cli.httpbingo('api', params: { abc: 123 })
|
||||
|
||||
# uncomment to raise Stubs::NotFound
|
||||
# assert_equal '127.0.0.1', cli.httpbingo('api', params: { abc: 123, foo: 'Kappa' })
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
def test_non_default_params_encoder
|
||||
stubs = Faraday::Adapter::Test::Stubs.new(strict_mode: true)
|
||||
stubs.get('/api?a=x&a=y&a=z') do
|
||||
[
|
||||
200,
|
||||
{ 'Content-Type': 'application/javascript' },
|
||||
'{"origin": "127.0.0.1"}'
|
||||
]
|
||||
end
|
||||
conn = Faraday.new(request: { params_encoder: Faraday::FlatParamsEncoder }) do |builder|
|
||||
builder.adapter :test, stubs
|
||||
end
|
||||
|
||||
cli = Client.new(conn)
|
||||
assert_equal '127.0.0.1', cli.httpbingo('api', params: { a: %w[x y z] })
|
||||
|
||||
# uncomment to raise Stubs::NotFound
|
||||
# assert_equal '127.0.0.1', cli.httpbingo('api', params: { a: %w[x y] })
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
def test_with_string_body
|
||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
||||
stub.post('/foo', '{"name":"YK"}') { [200, {}, ''] }
|
||||
end
|
||||
cli = client(stubs)
|
||||
assert_equal 200, cli.foo(name: 'YK')
|
||||
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
def test_with_proc_body
|
||||
stubs = Faraday::Adapter::Test::Stubs.new do |stub|
|
||||
check = ->(request_body) { JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }
|
||||
stub.post('/foo', check) { [200, {}, ''] }
|
||||
end
|
||||
cli = client(stubs)
|
||||
assert_equal 200, cli.foo(name: 'YK', created_at: Time.now)
|
||||
|
||||
stubs.verify_stubbed_calls
|
||||
end
|
||||
|
||||
def client(stubs)
|
||||
conn = Faraday.new do |builder|
|
||||
builder.adapter :test, stubs
|
||||
end
|
||||
Client.new(conn)
|
||||
end
|
||||
end
|
@ -1,70 +1,39 @@
|
||||
# Generated by jeweler
|
||||
# DO NOT EDIT THIS FILE DIRECTLY
|
||||
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
||||
# -*- encoding: utf-8 -*-
|
||||
# frozen_string_literal: true
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = %q{faraday}
|
||||
s.version = "0.1.2"
|
||||
require_relative 'lib/faraday/version'
|
||||
|
||||
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
||||
s.authors = ["rick"]
|
||||
s.date = %q{2010-01-05}
|
||||
s.description = %q{HTTP/REST API client library with pluggable components}
|
||||
s.email = %q{technoweenie@gmail.com}
|
||||
s.extra_rdoc_files = [
|
||||
"LICENSE",
|
||||
"README.rdoc"
|
||||
]
|
||||
s.files = [
|
||||
".document",
|
||||
".gitignore",
|
||||
"LICENSE",
|
||||
"README.rdoc",
|
||||
"Rakefile",
|
||||
"VERSION",
|
||||
"faraday.gemspec",
|
||||
"lib/faraday.rb",
|
||||
"lib/faraday/adapter/mock_request.rb",
|
||||
"lib/faraday/adapter/net_http.rb",
|
||||
"lib/faraday/adapter/typhoeus.rb",
|
||||
"lib/faraday/connection.rb",
|
||||
"lib/faraday/error.rb",
|
||||
"lib/faraday/loadable.rb",
|
||||
"lib/faraday/request/post_request.rb",
|
||||
"lib/faraday/request/yajl_request.rb",
|
||||
"lib/faraday/response.rb",
|
||||
"lib/faraday/response/yajl_response.rb",
|
||||
"lib/faraday/test_connection.rb",
|
||||
"test/adapter/typhoeus_test.rb",
|
||||
"test/adapter_test.rb",
|
||||
"test/connection_test.rb",
|
||||
"test/helper.rb",
|
||||
"test/live_server.rb",
|
||||
"test/response_test.rb"
|
||||
]
|
||||
s.homepage = %q{http://github.com/technoweenie/faraday}
|
||||
s.rdoc_options = ["--charset=UTF-8"]
|
||||
s.require_paths = ["lib"]
|
||||
s.rubygems_version = %q{1.3.5}
|
||||
s.summary = %q{HTTP/REST API client library}
|
||||
s.test_files = [
|
||||
"test/adapter/typhoeus_test.rb",
|
||||
"test/adapter_test.rb",
|
||||
"test/connection_test.rb",
|
||||
"test/helper.rb",
|
||||
"test/live_server.rb",
|
||||
"test/response_test.rb"
|
||||
]
|
||||
Gem::Specification.new do |spec|
|
||||
spec.name = 'faraday'
|
||||
spec.version = Faraday::VERSION
|
||||
|
||||
if s.respond_to? :specification_version then
|
||||
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
||||
s.specification_version = 3
|
||||
spec.summary = 'HTTP/REST API client library.'
|
||||
|
||||
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
||||
else
|
||||
end
|
||||
else
|
||||
end
|
||||
spec.authors = ['@technoweenie', '@iMacTia', '@olleolleolle']
|
||||
spec.email = 'technoweenie@gmail.com'
|
||||
spec.homepage = 'https://lostisland.github.io/faraday'
|
||||
spec.licenses = ['MIT']
|
||||
|
||||
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.
|
||||
# To avoid releasing a major version every time there's a new Faraday API, we should
|
||||
# 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.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']
|
||||
spec.require_paths = %w[lib spec/external_adapters]
|
||||
spec.metadata = {
|
||||
'homepage_uri' => 'https://lostisland.github.io/faraday',
|
||||
'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',
|
||||
'rubygems_mfa_required' => 'true'
|
||||
}
|
||||
end
|
||||
|
||||
|
211
lib/faraday.rb
@ -1,69 +1,158 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'cgi/escape'
|
||||
require 'cgi/util' if RUBY_VERSION < '3.5'
|
||||
require 'date'
|
||||
require 'set'
|
||||
require 'forwardable'
|
||||
require 'faraday/version'
|
||||
require 'faraday/methods'
|
||||
require 'faraday/error'
|
||||
require 'faraday/middleware_registry'
|
||||
require 'faraday/utils'
|
||||
require 'faraday/options'
|
||||
require 'faraday/connection'
|
||||
require 'faraday/rack_builder'
|
||||
require 'faraday/parameters'
|
||||
require 'faraday/middleware'
|
||||
require 'faraday/adapter'
|
||||
require 'faraday/request'
|
||||
require 'faraday/response'
|
||||
require 'faraday/net_http'
|
||||
# This is the main namespace for Faraday.
|
||||
#
|
||||
# It provides methods to create {Connection} objects, and HTTP-related
|
||||
# methods to use directly.
|
||||
#
|
||||
# @example Helpful class methods for easy usage
|
||||
# Faraday.get "http://faraday.com"
|
||||
#
|
||||
# @example Helpful class method `.new` to create {Connection} objects.
|
||||
# conn = Faraday.new "http://faraday.com"
|
||||
# conn.get '/'
|
||||
#
|
||||
module Faraday
|
||||
module AutoloadHelper
|
||||
def autoload_all(prefix, options)
|
||||
options.each do |const_name, path|
|
||||
autoload const_name, File.join(prefix, path)
|
||||
end
|
||||
CONTENT_TYPE = 'Content-Type'
|
||||
|
||||
class << self
|
||||
# The root path that Faraday is being loaded from.
|
||||
#
|
||||
# This is the root from where the libraries are auto-loaded.
|
||||
#
|
||||
# @return [String]
|
||||
attr_accessor :root_path
|
||||
|
||||
# Gets or sets the path that the Faraday libs are loaded from.
|
||||
# @return [String]
|
||||
attr_accessor :lib_path
|
||||
|
||||
# @overload default_adapter
|
||||
# Gets the Symbol key identifying a default Adapter to use
|
||||
# for the default {Faraday::Connection}. Defaults to `:net_http`.
|
||||
# @return [Symbol] the default adapter
|
||||
# @overload default_adapter=(adapter)
|
||||
# Updates default adapter while resetting {.default_connection}.
|
||||
# @return [Symbol] the new default_adapter.
|
||||
attr_reader :default_adapter
|
||||
|
||||
# Option for the default_adapter
|
||||
# @return [Hash] default_adapter options
|
||||
attr_accessor :default_adapter_options
|
||||
|
||||
# Documented below, see default_connection
|
||||
attr_writer :default_connection
|
||||
|
||||
# Tells Faraday to ignore the environment proxy (http_proxy).
|
||||
# Defaults to `false`.
|
||||
# @return [Boolean]
|
||||
attr_accessor :ignore_env_proxy
|
||||
|
||||
# Initializes a new {Connection}.
|
||||
#
|
||||
# @param url [String,Hash] The optional String base URL to use as a prefix
|
||||
# for all requests. Can also be the options Hash. Any of these
|
||||
# values will be set on every request made, unless overridden
|
||||
# for a specific request.
|
||||
# @param options [Hash]
|
||||
# @option options [String] :url Base URL
|
||||
# @option options [Hash] :params Hash of unencoded URI query params.
|
||||
# @option options [Hash] :headers Hash of unencoded HTTP headers.
|
||||
# @option options [Hash] :request Hash of request options.
|
||||
# @option options [Hash] :ssl Hash of SSL options.
|
||||
# @option options [Hash] :proxy Hash of Proxy options.
|
||||
# @return [Faraday::Connection]
|
||||
#
|
||||
# @example With an URL argument
|
||||
# Faraday.new 'http://faraday.com'
|
||||
# # => Faraday::Connection to http://faraday.com
|
||||
#
|
||||
# @example With an URL argument and an options hash
|
||||
# Faraday.new 'http://faraday.com', params: { page: 1 }
|
||||
# # => Faraday::Connection to http://faraday.com?page=1
|
||||
#
|
||||
# @example With everything in an options hash
|
||||
# Faraday.new url: 'http://faraday.com',
|
||||
# params: { page: 1 }
|
||||
# # => Faraday::Connection to http://faraday.com?page=1
|
||||
def new(url = nil, options = {}, &block)
|
||||
options = Utils.deep_merge(default_connection_options, options)
|
||||
Faraday::Connection.new(url, options, &block)
|
||||
end
|
||||
|
||||
# Loads each autoloaded constant. If thread safety is a concern, wrap
|
||||
# this in a Mutex.
|
||||
def load
|
||||
constants.each do |const|
|
||||
const_get(const) if autoload?(const)
|
||||
# Documented elsewhere, see default_adapter reader
|
||||
def default_adapter=(adapter)
|
||||
@default_connection = nil
|
||||
@default_adapter = adapter
|
||||
end
|
||||
|
||||
def respond_to_missing?(symbol, include_private = false)
|
||||
default_connection.respond_to?(symbol, include_private) || super
|
||||
end
|
||||
|
||||
# @overload default_connection
|
||||
# Gets the default connection used for simple scripts.
|
||||
# @return [Faraday::Connection] a connection configured with
|
||||
# the default_adapter.
|
||||
# @overload default_connection=(connection)
|
||||
# @param connection [Faraday::Connection]
|
||||
# Sets the default {Faraday::Connection} for simple scripts that
|
||||
# access the Faraday constant directly, such as
|
||||
# <code>Faraday.get "https://faraday.com"</code>.
|
||||
def default_connection
|
||||
@default_connection ||= Connection.new(default_connection_options)
|
||||
end
|
||||
|
||||
# Gets the default connection options used when calling {Faraday#new}.
|
||||
#
|
||||
# @return [Faraday::ConnectionOptions]
|
||||
def default_connection_options
|
||||
@default_connection_options ||= ConnectionOptions.new
|
||||
end
|
||||
|
||||
# Sets the default options used when calling {Faraday#new}.
|
||||
#
|
||||
# @param options [Hash, Faraday::ConnectionOptions]
|
||||
def default_connection_options=(options)
|
||||
@default_connection = nil
|
||||
@default_connection_options = ConnectionOptions.from(options)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Internal: Proxies method calls on the Faraday constant to
|
||||
# .default_connection.
|
||||
def method_missing(name, *args, &block)
|
||||
if default_connection.respond_to?(name)
|
||||
default_connection.send(name, *args, &block)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
extend AutoloadHelper
|
||||
|
||||
autoload_all 'faraday',
|
||||
:Connection => 'connection',
|
||||
:TestConnection => 'test_connection',
|
||||
:Response => 'response',
|
||||
:Error => 'error',
|
||||
:Loadable => 'loadable'
|
||||
|
||||
module Request
|
||||
extend AutoloadHelper
|
||||
autoload_all 'faraday/request',
|
||||
:YajlRequest => 'yajl_request',
|
||||
:PostRequest => 'post_request'
|
||||
end
|
||||
|
||||
module Adapter
|
||||
extend AutoloadHelper
|
||||
autoload_all 'faraday/adapter',
|
||||
:NetHttp => 'net_http',
|
||||
:Typhoeus => 'typhoeus',
|
||||
:MockRequest => 'mock_request'
|
||||
|
||||
# Names of available adapters. Should not actually load them.
|
||||
def self.adapters
|
||||
constants
|
||||
end
|
||||
|
||||
# Array of Adapters. These have been loaded and confirmed to work (right gems, etc).
|
||||
def self.loaded_adapters
|
||||
adapters.map { |c| const_get(c) }.select { |a| a.loaded? }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# not pulling in active-support JUST for this method.
|
||||
class Object
|
||||
# Yields <code>x</code> to the block, and then returns <code>x</code>.
|
||||
# The primary purpose of this method is to "tap into" a method chain,
|
||||
# in order to perform operations on intermediate results within the chain.
|
||||
#
|
||||
# (1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
|
||||
# tap { |x| puts "array: #{x.inspect}" }.
|
||||
# select { |x| x%2 == 0 }.
|
||||
# tap { |x| puts "evens: #{x.inspect}" }.
|
||||
# map { |x| x*x }.
|
||||
# tap { |x| puts "squares: #{x.inspect}" }
|
||||
def tap
|
||||
yield self
|
||||
self
|
||||
end unless Object.respond_to?(:tap)
|
||||
self.ignore_env_proxy = false
|
||||
self.root_path = File.expand_path __dir__
|
||||
self.lib_path = File.expand_path 'faraday', __dir__
|
||||
self.default_adapter = :net_http
|
||||
self.default_adapter_options = {}
|
||||
end
|
||||
|
101
lib/faraday/adapter.rb
Normal file
@ -0,0 +1,101 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# Base class for all Faraday adapters. Adapters are
|
||||
# responsible for fulfilling a Faraday request.
|
||||
class Adapter
|
||||
extend MiddlewareRegistry
|
||||
|
||||
CONTENT_LENGTH = 'Content-Length'
|
||||
|
||||
# This module marks an Adapter as supporting parallel requests.
|
||||
module Parallelism
|
||||
attr_writer :supports_parallel
|
||||
|
||||
def supports_parallel?
|
||||
@supports_parallel
|
||||
end
|
||||
|
||||
def inherited(subclass)
|
||||
super
|
||||
subclass.supports_parallel = supports_parallel?
|
||||
end
|
||||
end
|
||||
|
||||
extend Parallelism
|
||||
self.supports_parallel = false
|
||||
|
||||
def initialize(_app = nil, opts = {}, &block)
|
||||
@app = lambda(&:response)
|
||||
@connection_options = opts
|
||||
@config_block = block
|
||||
end
|
||||
|
||||
# Yields or returns an adapter's configured connection. Depends on
|
||||
# #build_connection being defined on this adapter.
|
||||
#
|
||||
# @param env [Faraday::Env, Hash] The env object for a faraday request.
|
||||
#
|
||||
# @return The return value of the given block, or the HTTP connection object
|
||||
# if no block is given.
|
||||
def connection(env)
|
||||
conn = build_connection(env)
|
||||
return conn unless block_given?
|
||||
|
||||
yield conn
|
||||
end
|
||||
|
||||
# Close any persistent connections. The adapter should still be usable
|
||||
# after calling close.
|
||||
def close
|
||||
# Possible implementation:
|
||||
# @app.close if @app.respond_to?(:close)
|
||||
end
|
||||
|
||||
def call(env)
|
||||
env.clear_body if env.needs_body?
|
||||
env.response = Response.new
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def save_response(env, status, body, headers = nil, reason_phrase = nil, finished: true)
|
||||
env.status = status
|
||||
env.body = body
|
||||
env.reason_phrase = reason_phrase&.to_s&.strip
|
||||
env.response_headers = Utils::Headers.new.tap do |response_headers|
|
||||
response_headers.update headers unless headers.nil?
|
||||
yield(response_headers) if block_given?
|
||||
end
|
||||
|
||||
env.response.finish(env) unless env.parallel? || !finished
|
||||
env.response
|
||||
end
|
||||
|
||||
# Fetches either a read, write, or open timeout setting. Defaults to the
|
||||
# :timeout value if a more specific one is not given.
|
||||
#
|
||||
# @param type [Symbol] Describes which timeout setting to get: :read,
|
||||
# :write, or :open.
|
||||
# @param options [Hash] Hash containing Symbol keys like :timeout,
|
||||
# :read_timeout, :write_timeout, or :open_timeout
|
||||
#
|
||||
# @return [Integer, nil] Timeout duration in seconds, or nil if no timeout
|
||||
# has been set.
|
||||
def request_timeout(type, options)
|
||||
key = TIMEOUT_KEYS.fetch(type) do
|
||||
msg = "Expected :read, :write, :open. Got #{type.inspect} :("
|
||||
raise ArgumentError, msg
|
||||
end
|
||||
options[key] || options[:timeout]
|
||||
end
|
||||
|
||||
TIMEOUT_KEYS = {
|
||||
read: :read_timeout,
|
||||
open: :open_timeout,
|
||||
write: :write_timeout
|
||||
}.freeze
|
||||
end
|
||||
end
|
||||
|
||||
require 'faraday/adapter/test'
|
@ -1,120 +0,0 @@
|
||||
module Faraday
|
||||
module Adapter
|
||||
module MockRequest
|
||||
extend Faraday::Connection::Options
|
||||
def self.loaded?() false end
|
||||
|
||||
include Faraday::Error # ConnectionFailed
|
||||
|
||||
class Stubs
|
||||
def initialize
|
||||
# {:get => [Stub, Stub]}
|
||||
@stack = {}
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
def empty?
|
||||
@stack.empty?
|
||||
end
|
||||
|
||||
def match(request_method, path, data, request_headers)
|
||||
return false if !@stack.key?(request_method)
|
||||
stub = @stack[request_method].detect { |stub| stub.matches?(path, data, request_headers) }
|
||||
@stack[request_method].delete(stub) if stub
|
||||
end
|
||||
|
||||
def get(path, request_headers = {}, &block)
|
||||
(@stack[:get] ||= []) << new_stub(path, {}, request_headers, block)
|
||||
end
|
||||
|
||||
def delete(path, request_headers = {}, &block)
|
||||
(@stack[:delete] ||= []) << new_stub(path, {}, request_headers, block)
|
||||
end
|
||||
|
||||
def post(path, data, request_headers = {}, &block)
|
||||
(@stack[:post] ||= []) << new_stub(path, data, request_headers, block)
|
||||
end
|
||||
|
||||
def put(path, data, request_headers = {}, &block)
|
||||
(@stack[:put] ||= []) << new_stub(path, data, request_headers, block)
|
||||
end
|
||||
|
||||
def new_stub(path, data, request_headers, block)
|
||||
status, response_headers, body = block.call
|
||||
Stub.new(path, request_headers, status, response_headers, body, data)
|
||||
end
|
||||
end
|
||||
|
||||
class Stub < Struct.new(:path, :request_headers, :status, :response_headers, :body, :data)
|
||||
def matches?(request_path, params, headers)
|
||||
return false if request_path != path
|
||||
return false if params != data
|
||||
return true if request_headers.empty?
|
||||
request_headers.each do |key, value|
|
||||
return false if headers[key] != value
|
||||
end
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def initialize &block
|
||||
super
|
||||
configure(&block) if block
|
||||
end
|
||||
|
||||
def configure
|
||||
yield stubs
|
||||
end
|
||||
|
||||
def stubs
|
||||
@stubs ||= Stubs.new
|
||||
end
|
||||
|
||||
def _get(uri, headers)
|
||||
raise ConnectionFailed, "no stubbed requests" if stubs.empty?
|
||||
if stub = @stubs.match(:get, uri.path, {}, headers)
|
||||
response_class.new do |resp|
|
||||
resp.headers = stub.response_headers
|
||||
resp.process stub.body
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
def _delete(uri, headers)
|
||||
raise ConnectionFailed, "no stubbed requests" if stubs.empty?
|
||||
if stub = @stubs.match(:delete, uri.path, {}, headers)
|
||||
response_class.new do |resp|
|
||||
resp.headers = stub.response_headers
|
||||
resp.process stub.body
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
def _post(uri, data, headers)
|
||||
raise ConnectionFailed, "no stubbed requests" if stubs.empty?
|
||||
if stub = @stubs.match(:post, uri.path, data, headers)
|
||||
response_class.new do |resp|
|
||||
resp.headers = stub.response_headers
|
||||
resp.process stub.body
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
def _put(uri, data, headers)
|
||||
raise ConnectionFailed, "no stubbed requests" if stubs.empty?
|
||||
if stub = @stubs.match(:put, uri.path, data, headers)
|
||||
response_class.new do |resp|
|
||||
resp.headers = stub.response_headers
|
||||
resp.process stub.body
|
||||
end
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,42 +0,0 @@
|
||||
require 'net/http'
|
||||
require 'cgi'
|
||||
module Faraday
|
||||
module Adapter
|
||||
module NetHttp
|
||||
extend Faraday::Connection::Options
|
||||
|
||||
def _perform(method, uri, data, request_headers)
|
||||
http = Net::HTTP.new(uri.host, uri.port)
|
||||
response_class.new do |resp|
|
||||
http_resp = http.send_request(method, path_for(uri), data, request_headers)
|
||||
raise Faraday::Error::ResourceNotFound if http_resp.code == '404'
|
||||
resp.process http_resp.body
|
||||
http_resp.each_header do |key, value|
|
||||
resp.headers[key] = value
|
||||
end
|
||||
end
|
||||
rescue Errno::ECONNREFUSED
|
||||
raise Faraday::Error::ConnectionFailed, "connection refused"
|
||||
end
|
||||
|
||||
def _put(uri, data, request_headers)
|
||||
request = request_class.new(data, request_headers)
|
||||
_perform('PUT', uri, request.body, request.headers)
|
||||
end
|
||||
|
||||
def _post(uri, data, request_headers)
|
||||
request = request_class.new(data, request_headers)
|
||||
_perform('POST', uri, request.body, request.headers)
|
||||
end
|
||||
|
||||
def _get(uri, request_headers)
|
||||
_perform('GET', uri, uri.query, request_headers)
|
||||
end
|
||||
|
||||
def _delete(uri, request_headers)
|
||||
_perform('DELETE', uri, uri.query, request_headers)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
311
lib/faraday/adapter/test.rb
Normal file
@ -0,0 +1,311 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'timeout'
|
||||
|
||||
module Faraday
|
||||
class Adapter
|
||||
# @example
|
||||
# test = Faraday::Connection.new do
|
||||
# use Faraday::Adapter::Test do |stub|
|
||||
# # Define matcher to match the request
|
||||
# stub.get '/resource.json' do
|
||||
# # return static content
|
||||
# [200, {'Content-Type' => 'application/json'}, 'hi world']
|
||||
# end
|
||||
#
|
||||
# # response with content generated based on request
|
||||
# stub.get '/showget' do |env|
|
||||
# [200, {'Content-Type' => 'text/plain'}, env[:method].to_s]
|
||||
# end
|
||||
#
|
||||
# # A regular expression can be used as matching filter
|
||||
# stub.get /\A\/items\/(\d+)\z/ do |env, meta|
|
||||
# # in case regular expression is used, an instance of MatchData
|
||||
# # can be received
|
||||
# [200,
|
||||
# {'Content-Type' => 'text/plain'},
|
||||
# "showing item: #{meta[:match_data][1]}"
|
||||
# ]
|
||||
# end
|
||||
#
|
||||
# # Test the request body is the same as the stubbed body
|
||||
# stub.post('/bar', 'name=YK&word=call') { [200, {}, ''] }
|
||||
#
|
||||
# # You can pass a proc as a stubbed body and check the request body in your way.
|
||||
# # In this case, the proc should return true or false.
|
||||
# stub.post('/foo', ->(request_body) do
|
||||
# JSON.parse(request_body).slice('name') == { 'name' => 'YK' } }) { [200, {}, '']
|
||||
# end
|
||||
#
|
||||
# # You can set strict_mode to exactly match the stubbed requests.
|
||||
# stub.strict_mode = true
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# resp = test.get '/resource.json'
|
||||
# resp.body # => 'hi world'
|
||||
#
|
||||
# resp = test.get '/showget'
|
||||
# resp.body # => 'get'
|
||||
#
|
||||
# resp = test.get '/items/1'
|
||||
# resp.body # => 'showing item: 1'
|
||||
#
|
||||
# resp = test.get '/items/2'
|
||||
# resp.body # => 'showing item: 2'
|
||||
#
|
||||
# resp = test.post '/bar', 'name=YK&word=call'
|
||||
# resp.status # => 200
|
||||
#
|
||||
# resp = test.post '/foo', JSON.dump(name: 'YK', created_at: Time.now)
|
||||
# resp.status # => 200
|
||||
class Test < Faraday::Adapter
|
||||
attr_accessor :stubs
|
||||
|
||||
# A stack of Stubs
|
||||
class Stubs
|
||||
class NotFound < StandardError
|
||||
end
|
||||
|
||||
def initialize(strict_mode: false)
|
||||
# { get: [Stub, Stub] }
|
||||
@stack = {}
|
||||
@consumed = {}
|
||||
@strict_mode = strict_mode
|
||||
@stubs_mutex = Monitor.new
|
||||
yield(self) if block_given?
|
||||
end
|
||||
|
||||
def empty?
|
||||
@stack.empty?
|
||||
end
|
||||
|
||||
# @param env [Faraday::Env]
|
||||
def match(env)
|
||||
request_method = env[:method]
|
||||
return false unless @stack.key?(request_method)
|
||||
|
||||
stack = @stack[request_method]
|
||||
consumed = (@consumed[request_method] ||= [])
|
||||
|
||||
@stubs_mutex.synchronize do
|
||||
stub, meta = matches?(stack, env)
|
||||
if stub
|
||||
removed = stack.delete(stub)
|
||||
consumed << removed unless removed.nil?
|
||||
return stub, meta
|
||||
end
|
||||
end
|
||||
matches?(consumed, env)
|
||||
end
|
||||
|
||||
def get(path, headers = {}, &block)
|
||||
new_stub(:get, path, headers, &block)
|
||||
end
|
||||
|
||||
def head(path, headers = {}, &block)
|
||||
new_stub(:head, path, headers, &block)
|
||||
end
|
||||
|
||||
def post(path, body = nil, headers = {}, &block)
|
||||
new_stub(:post, path, headers, body, &block)
|
||||
end
|
||||
|
||||
def put(path, body = nil, headers = {}, &block)
|
||||
new_stub(:put, path, headers, body, &block)
|
||||
end
|
||||
|
||||
def patch(path, body = nil, headers = {}, &block)
|
||||
new_stub(:patch, path, headers, body, &block)
|
||||
end
|
||||
|
||||
def delete(path, headers = {}, &block)
|
||||
new_stub(:delete, path, headers, &block)
|
||||
end
|
||||
|
||||
def options(path, headers = {}, &block)
|
||||
new_stub(:options, path, headers, &block)
|
||||
end
|
||||
|
||||
# Raises an error if any of the stubbed calls have not been made.
|
||||
def verify_stubbed_calls
|
||||
failed_stubs = []
|
||||
@stack.each do |method, stubs|
|
||||
next if stubs.empty?
|
||||
|
||||
failed_stubs.concat(
|
||||
stubs.map do |stub|
|
||||
"Expected #{method} #{stub}."
|
||||
end
|
||||
)
|
||||
end
|
||||
raise failed_stubs.join(' ') unless failed_stubs.empty?
|
||||
end
|
||||
|
||||
# Set strict_mode. If the value is true, this adapter tries to find matched requests strictly,
|
||||
# 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_value do |stubs|
|
||||
stubs.each do |stub|
|
||||
stub.strict_mode = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def new_stub(request_method, path, headers = {}, body = nil, &block)
|
||||
normalized_path, host =
|
||||
if path.is_a?(Regexp)
|
||||
path
|
||||
else
|
||||
[
|
||||
Faraday::Utils.normalize_path(path),
|
||||
Faraday::Utils.URI(path).host
|
||||
]
|
||||
end
|
||||
path, query = normalized_path.respond_to?(:split) ? normalized_path.split('?') : normalized_path
|
||||
headers = Utils::Headers.new(headers)
|
||||
|
||||
stub = Stub.new(host, path, query, headers, body, @strict_mode, block)
|
||||
(@stack[request_method] ||= []) << stub
|
||||
end
|
||||
|
||||
# @param stack [Hash]
|
||||
# @param env [Faraday::Env]
|
||||
def matches?(stack, env)
|
||||
stack.each do |stub|
|
||||
match_result, meta = stub.matches?(env)
|
||||
return stub, meta if match_result
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
# Stub request
|
||||
Stub = Struct.new(:host, :path, :query, :headers, :body, :strict_mode, :block) do
|
||||
# @param env [Faraday::Env]
|
||||
def matches?(env)
|
||||
request_host = env[:url].host
|
||||
request_path = Faraday::Utils.normalize_path(env[:url].path)
|
||||
request_headers = env.request_headers
|
||||
request_body = env[:body]
|
||||
|
||||
# meta is a hash used as carrier
|
||||
# that will be yielded to consumer block
|
||||
meta = {}
|
||||
[(host.nil? || host == request_host) &&
|
||||
path_match?(request_path, meta) &&
|
||||
params_match?(env) &&
|
||||
body_match?(request_body) &&
|
||||
headers_match?(request_headers), meta]
|
||||
end
|
||||
|
||||
def path_match?(request_path, meta)
|
||||
if path.is_a?(Regexp)
|
||||
!!(meta[:match_data] = path.match(request_path))
|
||||
else
|
||||
path == request_path
|
||||
end
|
||||
end
|
||||
|
||||
# @param env [Faraday::Env]
|
||||
def params_match?(env)
|
||||
request_params = env[:params]
|
||||
params = env.params_encoder.decode(query) || {}
|
||||
|
||||
if strict_mode
|
||||
return Set.new(params) == Set.new(request_params)
|
||||
end
|
||||
|
||||
params.keys.all? do |key|
|
||||
request_params[key] == params[key]
|
||||
end
|
||||
end
|
||||
|
||||
def headers_match?(request_headers)
|
||||
if strict_mode
|
||||
headers_with_user_agent = headers.dup.tap do |hs|
|
||||
# NOTE: Set User-Agent in case it's not set when creating Stubs.
|
||||
# Users would not want to set Faraday's User-Agent explicitly.
|
||||
hs[:user_agent] ||= Connection::USER_AGENT
|
||||
end
|
||||
return Set.new(headers_with_user_agent) == Set.new(request_headers)
|
||||
end
|
||||
|
||||
headers.keys.all? do |key|
|
||||
request_headers[key] == headers[key]
|
||||
end
|
||||
end
|
||||
|
||||
def body_match?(request_body)
|
||||
return true if body.to_s.empty?
|
||||
|
||||
case body
|
||||
when Proc
|
||||
body.call(request_body)
|
||||
else
|
||||
request_body == body
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{path} #{body}"
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(app, stubs = nil, &block)
|
||||
super(app)
|
||||
@stubs = stubs || Stubs.new
|
||||
configure(&block) if block
|
||||
end
|
||||
|
||||
def configure
|
||||
yield(stubs)
|
||||
end
|
||||
|
||||
# @param env [Faraday::Env]
|
||||
def call(env)
|
||||
super
|
||||
|
||||
env.request.params_encoder ||= Faraday::Utils.default_params_encoder
|
||||
env[:params] = env.params_encoder.decode(env[:url].query) || {}
|
||||
stub, meta = stubs.match(env)
|
||||
|
||||
unless stub
|
||||
raise Stubs::NotFound, "no stubbed request for #{env[:method]} " \
|
||||
"#{env[:url]} #{env[:body]} #{env[:headers]}"
|
||||
end
|
||||
|
||||
block_arity = stub.block.arity
|
||||
params = if block_arity >= 0
|
||||
[env, meta].take(block_arity)
|
||||
else
|
||||
[env, meta]
|
||||
end
|
||||
|
||||
timeout = request_timeout(:open, env[:request])
|
||||
timeout ||= request_timeout(:read, env[:request])
|
||||
|
||||
status, headers, body =
|
||||
if timeout
|
||||
::Timeout.timeout(timeout, Faraday::TimeoutError) do
|
||||
stub.block.call(*params)
|
||||
end
|
||||
else
|
||||
stub.block.call(*params)
|
||||
end
|
||||
|
||||
# We need to explicitly pass `reason_phrase = nil` here to avoid keyword args conflicts.
|
||||
# See https://github.com/lostisland/faraday/issues/1444
|
||||
# TODO: remove `nil` explicit reason_phrase once Ruby 3.0 becomes minimum req. version
|
||||
save_response(env, status, body, headers, nil)
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Faraday::Adapter.register_middleware(test: Faraday::Adapter::Test)
|
@ -1,76 +0,0 @@
|
||||
module Faraday
|
||||
module Adapter
|
||||
module Typhoeus
|
||||
extend Faraday::Connection::Options
|
||||
|
||||
begin
|
||||
require 'typhoeus'
|
||||
|
||||
def in_parallel?
|
||||
!!@parallel_manager
|
||||
end
|
||||
|
||||
def in_parallel(options = {})
|
||||
setup_parallel_manager(options)
|
||||
yield
|
||||
run_parallel_requests
|
||||
end
|
||||
|
||||
def setup_parallel_manager(options = {})
|
||||
@parallel_manager ||= ::Typhoeus::Hydra.new(options)
|
||||
end
|
||||
|
||||
def run_parallel_requests
|
||||
@parallel_manager.run
|
||||
@parallel_manager = nil
|
||||
end
|
||||
|
||||
def _post(uri, data, request_headers)
|
||||
request = request_class.new(data, request_headers)
|
||||
_perform(:post, uri, :headers => request.headers, :body => request.body)
|
||||
end
|
||||
|
||||
def _get(uri, request_headers)
|
||||
_perform(:get, uri, :headers => request_headers)
|
||||
end
|
||||
|
||||
def _put(uri, data, request_headers)
|
||||
request = request_class.new(data, request_headers)
|
||||
_perform(:put, uri, :headers => request.headers, :body => request.body)
|
||||
end
|
||||
|
||||
def _delete(uri, request_headers)
|
||||
_perform(:delete, uri, :headers => request_headers)
|
||||
end
|
||||
|
||||
def _perform method, uri, params
|
||||
response_class.new do |resp|
|
||||
is_async = in_parallel?
|
||||
setup_parallel_manager
|
||||
params[:method] = method
|
||||
req = ::Typhoeus::Request.new(uri.to_s, params)
|
||||
req.on_complete do |response|
|
||||
raise Faraday::Error::ResourceNotFound if response.code == 404
|
||||
resp.process!(response.body)
|
||||
resp.headers = parse_response_headers(response.headers)
|
||||
end
|
||||
@parallel_manager.queue(req)
|
||||
if !is_async then run_parallel_requests end
|
||||
end
|
||||
rescue Errno::ECONNREFUSED
|
||||
raise Faraday::Error::ConnectionFailed, "connection refused"
|
||||
end
|
||||
|
||||
def parse_response_headers(header_string)
|
||||
return {} unless header_string # XXX
|
||||
Hash[*header_string.split(/\r\n/).
|
||||
tap { |a| a.shift }. # drop the HTTP status line
|
||||
map! { |h| h.split(/:\s+/,2) }. # split key and value
|
||||
map! { |(k, v)| [k.downcase, v] }.flatten!]
|
||||
end
|
||||
rescue LoadError => e
|
||||
self.load_error = e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
30
lib/faraday/adapter_registry.rb
Normal file
@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'monitor'
|
||||
|
||||
module Faraday
|
||||
# AdapterRegistry registers adapter class names so they can be looked up by a
|
||||
# String or Symbol name.
|
||||
class AdapterRegistry
|
||||
def initialize
|
||||
@lock = Monitor.new
|
||||
@constants = {}
|
||||
end
|
||||
|
||||
def get(name)
|
||||
klass = @lock.synchronize do
|
||||
@constants[name]
|
||||
end
|
||||
return klass if klass
|
||||
|
||||
Object.const_get(name).tap { |c| set(c, name) }
|
||||
end
|
||||
|
||||
def set(klass, name = nil)
|
||||
name ||= klass.to_s
|
||||
@lock.synchronize do
|
||||
@constants[name] = klass
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -1,176 +1,564 @@
|
||||
require 'addressable/uri'
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# Connection objects manage the default properties and the middleware
|
||||
# stack for fulfilling an HTTP request.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# conn = Faraday::Connection.new 'http://httpbingo.org'
|
||||
#
|
||||
# # GET http://httpbingo.org/nigiri
|
||||
# conn.get 'nigiri'
|
||||
# # => #<Faraday::Response>
|
||||
#
|
||||
class Connection
|
||||
module Options
|
||||
def load_error() @load_error end
|
||||
def load_error=(v) @load_error = v end
|
||||
def supports_async() @supports_async end
|
||||
def supports_async=(v) @supports_async = v end
|
||||
def loaded?() !@load_error end
|
||||
alias supports_async? supports_async
|
||||
end
|
||||
# A Set of allowed HTTP verbs.
|
||||
METHODS = Set.new %i[get post put delete head patch options trace]
|
||||
USER_AGENT = "Faraday v#{VERSION}".freeze
|
||||
|
||||
include Addressable
|
||||
# @return [Hash] URI query unencoded key/value pairs.
|
||||
attr_reader :params
|
||||
|
||||
attr_accessor :host, :port, :scheme, :params, :headers
|
||||
attr_reader :path_prefix
|
||||
# @return [Hash] unencoded HTTP header key/value pairs.
|
||||
attr_reader :headers
|
||||
|
||||
# :url
|
||||
# :params
|
||||
# :headers
|
||||
# :response
|
||||
def initialize(url = nil, options = {})
|
||||
if url.is_a?(Hash)
|
||||
options = url
|
||||
url = options[:url]
|
||||
end
|
||||
@response_class = options[:response]
|
||||
@params = options[:params] || {}
|
||||
@headers = options[:headers] || {}
|
||||
self.url_prefix = url if url
|
||||
end
|
||||
# @return [String] a URI with the prefix used for all requests from this
|
||||
# Connection. This includes a default host name, scheme, port, and path.
|
||||
attr_reader :url_prefix
|
||||
|
||||
def url_prefix=(url)
|
||||
uri = URI.parse(url)
|
||||
self.scheme = uri.scheme
|
||||
self.host = uri.host
|
||||
self.port = uri.port
|
||||
self.path_prefix = uri.path
|
||||
end
|
||||
# @return [Faraday::RackBuilder] Builder for this Connection.
|
||||
attr_reader :builder
|
||||
|
||||
# @return [Hash] SSL options.
|
||||
attr_reader :ssl
|
||||
|
||||
# Override in a subclass, or include an adapter
|
||||
# @return [Object] the parallel manager for this Connection.
|
||||
attr_reader :parallel_manager
|
||||
|
||||
# Sets the default parallel manager for this connection.
|
||||
attr_writer :default_parallel_manager
|
||||
|
||||
# @return [Hash] proxy options.
|
||||
attr_reader :proxy
|
||||
|
||||
# Initializes a new Faraday::Connection.
|
||||
#
|
||||
# def _post(uri, post_params, headers)
|
||||
# @param url [URI, String] URI or String base URL to use as a prefix for all
|
||||
# requests (optional).
|
||||
# @param options [Hash, Faraday::ConnectionOptions]
|
||||
# @option options [URI, String] :url ('http:/') URI or String base URL
|
||||
# @option options [Hash<String => String>] :params URI query unencoded
|
||||
# key/value pairs.
|
||||
# @option options [Hash<String => String>] :headers Hash of unencoded HTTP
|
||||
# header key/value pairs.
|
||||
# @option options [Hash] :request Hash of request options.
|
||||
# @option options [Hash] :ssl Hash of SSL options.
|
||||
# @option options [Hash, URI, String] :proxy proxy options, either as a URL
|
||||
# or as a Hash
|
||||
# @option options [URI, String] :proxy[:uri]
|
||||
# @option options [String] :proxy[:user]
|
||||
# @option options [String] :proxy[:password]
|
||||
# @yield [self] after all setup has been done
|
||||
def initialize(url = nil, options = nil)
|
||||
options = ConnectionOptions.from(options)
|
||||
|
||||
if url.is_a?(Hash) || url.is_a?(ConnectionOptions)
|
||||
options = Utils.deep_merge(options, url)
|
||||
url = options.url
|
||||
end
|
||||
|
||||
@parallel_manager = nil
|
||||
@headers = Utils::Headers.new
|
||||
@params = Utils::ParamsHash.new
|
||||
@options = options.request
|
||||
@ssl = options.ssl
|
||||
@default_parallel_manager = options.parallel_manager
|
||||
@manual_proxy = nil
|
||||
|
||||
@builder = options.builder || begin
|
||||
# pass an empty block to Builder so it doesn't assume default middleware
|
||||
options.new_builder(block_given? ? proc { |b| } : nil)
|
||||
end
|
||||
|
||||
self.url_prefix = url || 'http:/'
|
||||
|
||||
@params.update(options.params) if options.params
|
||||
@headers.update(options.headers) if options.headers
|
||||
|
||||
initialize_proxy(url, options)
|
||||
|
||||
yield(self) if block_given?
|
||||
|
||||
@headers[:user_agent] ||= USER_AGENT
|
||||
end
|
||||
|
||||
def initialize_proxy(url, options)
|
||||
@manual_proxy = !!options.proxy
|
||||
@proxy =
|
||||
if options.proxy
|
||||
ProxyOptions.from(options.proxy)
|
||||
else
|
||||
proxy_from_env(url)
|
||||
end
|
||||
end
|
||||
|
||||
# Sets the Hash of URI query unencoded key/value pairs.
|
||||
# @param hash [Hash]
|
||||
def params=(hash)
|
||||
@params.replace hash
|
||||
end
|
||||
|
||||
# Sets the Hash of unencoded HTTP header key/value pairs.
|
||||
# @param hash [Hash]
|
||||
def headers=(hash)
|
||||
@headers.replace hash
|
||||
end
|
||||
|
||||
extend Forwardable
|
||||
|
||||
def_delegators :builder, :use, :request, :response, :adapter, :app
|
||||
|
||||
# Closes the underlying resources and/or connections. In the case of
|
||||
# persistent connections, this closes all currently open connections
|
||||
# but does not prevent new connections from being made.
|
||||
def close
|
||||
app.close
|
||||
end
|
||||
|
||||
# @!method get(url = nil, params = nil, headers = nil)
|
||||
# Makes a GET HTTP request without a body.
|
||||
# @!scope class
|
||||
#
|
||||
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||
# all requests. Can also be the options Hash.
|
||||
# @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
|
||||
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||
#
|
||||
# @example
|
||||
# conn.get '/items', { page: 1 }, :accept => 'application/json'
|
||||
#
|
||||
# # ElasticSearch example sending a body with GET.
|
||||
# conn.get '/twitter/tweet/_search' do |req|
|
||||
# req.headers[:content_type] = 'application/json'
|
||||
# req.params[:routing] = 'kimchy'
|
||||
# req.body = JSON.generate(query: {...})
|
||||
# end
|
||||
#
|
||||
def post(uri, params = {}, headers = {})
|
||||
_post build_uri(uri), build_params(params), build_headers(headers)
|
||||
# @yield [Faraday::Request] for further request customizations
|
||||
# @return [Faraday::Response]
|
||||
|
||||
# @!method head(url = nil, params = nil, headers = nil)
|
||||
# Makes a HEAD HTTP request without a body.
|
||||
# @!scope class
|
||||
#
|
||||
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||
# all requests. Can also be the options Hash.
|
||||
# @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
|
||||
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||
#
|
||||
# @example
|
||||
# conn.head '/items/1'
|
||||
#
|
||||
# @yield [Faraday::Request] for further request customizations
|
||||
# @return [Faraday::Response]
|
||||
|
||||
# @!method delete(url = nil, params = nil, headers = nil)
|
||||
# Makes a DELETE HTTP request without a body.
|
||||
# @!scope class
|
||||
#
|
||||
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||
# all requests. Can also be the options Hash.
|
||||
# @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
|
||||
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||
#
|
||||
# @example
|
||||
# conn.delete '/items/1'
|
||||
#
|
||||
# @yield [Faraday::Request] for further request customizations
|
||||
# @return [Faraday::Response]
|
||||
|
||||
# @!method trace(url = nil, params = nil, headers = nil)
|
||||
# Makes a TRACE HTTP request without a body.
|
||||
# @!scope class
|
||||
#
|
||||
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||
# all requests. Can also be the options Hash.
|
||||
# @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
|
||||
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||
#
|
||||
# @example
|
||||
# conn.connect '/items/1'
|
||||
#
|
||||
# @yield [Faraday::Request] for further request customizations
|
||||
# @return [Faraday::Response]
|
||||
|
||||
# @!visibility private
|
||||
METHODS_WITH_QUERY.each do |method|
|
||||
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def #{method}(url = nil, params = nil, headers = nil)
|
||||
run_request(:#{method}, url, nil, headers) do |request|
|
||||
request.params.update(params) if params
|
||||
yield request if block_given?
|
||||
end
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
# Override in a subclass, or include an adapter
|
||||
# @overload options()
|
||||
# Returns current Connection options.
|
||||
#
|
||||
# def _put(uri, post_params, headers)
|
||||
# @overload options(url, params = nil, headers = nil)
|
||||
# Makes an OPTIONS HTTP request to the given URL.
|
||||
# @param url [String, URI, nil] String base URL to sue as a prefix for all requests.
|
||||
# @param params [Hash, nil] Hash of URI query unencoded key/value pairs.
|
||||
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||
#
|
||||
# @example
|
||||
# conn.options '/items/1'
|
||||
#
|
||||
# @yield [Faraday::Request] for further request customizations
|
||||
# @return [Faraday::Response]
|
||||
def options(*args)
|
||||
return @options if args.empty?
|
||||
|
||||
url, params, headers = *args
|
||||
run_request(:options, url, nil, headers) do |request|
|
||||
request.params.update(params) if params
|
||||
yield request if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
# @!method post(url = nil, body = nil, headers = nil)
|
||||
# Makes a POST HTTP request with a body.
|
||||
# @!scope class
|
||||
#
|
||||
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||
# all requests. Can also be the options Hash.
|
||||
# @param body [String, nil] body for the request.
|
||||
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||
#
|
||||
# @example
|
||||
# conn.post '/items', 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', ...)
|
||||
# end
|
||||
#
|
||||
def put(uri, params = {}, headers = {})
|
||||
_put build_uri(uri), build_params(params), build_headers(headers)
|
||||
end
|
||||
# @yield [Faraday::Request] for further request customizations
|
||||
# @return [Faraday::Response]
|
||||
|
||||
# Override in a subclass, or include an adapter
|
||||
# @!method put(url = nil, body = nil, headers = nil)
|
||||
# Makes a PUT HTTP request with a body.
|
||||
# @!scope class
|
||||
#
|
||||
# def _delete(uri, headers)
|
||||
# @param url [String, URI, nil] The optional String base URL to use as a prefix for
|
||||
# all requests. Can also be the options Hash.
|
||||
# @param body [String, nil] body for the request.
|
||||
# @param headers [Hash, nil] unencoded HTTP header key/value pairs.
|
||||
#
|
||||
# @example
|
||||
# conn.put '/products/123', data, content_type: 'application/json'
|
||||
#
|
||||
# # 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
|
||||
#
|
||||
def delete(uri, params = {}, headers = {})
|
||||
_delete build_uri(uri, build_params(params)), build_headers(headers)
|
||||
# @yield [Faraday::Request] for further request customizations
|
||||
# @return [Faraday::Response]
|
||||
|
||||
# @!visibility private
|
||||
METHODS_WITH_BODY.each do |method|
|
||||
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def #{method}(url = nil, body = nil, headers = nil, &block)
|
||||
run_request(:#{method}, url, body, headers, &block)
|
||||
end
|
||||
RUBY
|
||||
end
|
||||
|
||||
# Override in a subclass, or include an adapter
|
||||
# Check if the adapter is parallel-capable.
|
||||
#
|
||||
# def _get(uri, headers)
|
||||
# end
|
||||
# @yield if the adapter isn't parallel-capable, or if no adapter is set yet.
|
||||
#
|
||||
def get(url, params = nil, headers = nil)
|
||||
uri = build_uri(url, build_params(params))
|
||||
_get(uri, build_headers(headers))
|
||||
end
|
||||
# @return [Object, nil] a parallel manager or nil if yielded
|
||||
# @api private
|
||||
def default_parallel_manager
|
||||
@default_parallel_manager ||= begin
|
||||
adapter = @builder.adapter.klass if @builder.adapter
|
||||
|
||||
def request_class
|
||||
@request_class || Request::PostRequest
|
||||
end
|
||||
|
||||
def response_class
|
||||
@response_class || Response
|
||||
end
|
||||
|
||||
def response_class=(v)
|
||||
if v.respond_to?(:loaded?) && !v.loaded?
|
||||
raise ArgumentError, "The response class: #{v.inspect} does not appear to be loaded."
|
||||
end
|
||||
@response_class = v
|
||||
end
|
||||
|
||||
def request_class=(v)
|
||||
if v.respond_to?(:loaded?) && !v.loaded?
|
||||
raise ArgumentError, "The request class: #{v.inspect} does not appear to be loaded."
|
||||
end
|
||||
@request_class = v
|
||||
end
|
||||
|
||||
def in_parallel?
|
||||
!!@parallel_manager
|
||||
end
|
||||
|
||||
def in_parallel(options = {})
|
||||
@parallel_manager = true
|
||||
yield
|
||||
@parallel_manager = false
|
||||
end
|
||||
|
||||
def setup_parallel_manager(options = {})
|
||||
end
|
||||
|
||||
def run_parallel_requests
|
||||
end
|
||||
|
||||
def path_prefix=(value)
|
||||
if value
|
||||
value.chomp! "/"
|
||||
value.replace "/#{value}" if value !~ /^\//
|
||||
end
|
||||
@path_prefix = value
|
||||
end
|
||||
|
||||
def build_uri(url, params = nil)
|
||||
uri = URI.parse(url)
|
||||
uri.scheme ||= @scheme
|
||||
uri.host ||= @host
|
||||
uri.port ||= @port
|
||||
if @path_prefix && uri.path !~ /^\//
|
||||
uri.path = "#{@path_prefix.size > 1 ? @path_prefix : nil}/#{uri.path}"
|
||||
end
|
||||
if params && !params.empty?
|
||||
uri.query = params_to_query(params)
|
||||
end
|
||||
uri
|
||||
end
|
||||
|
||||
def path_for(uri)
|
||||
uri.path.tap do |s|
|
||||
s << "?#{uri.query}" if uri.query
|
||||
s << "##{uri.fragment}" if uri.fragment
|
||||
end
|
||||
end
|
||||
|
||||
def build_params(existing)
|
||||
build_hash :params, existing
|
||||
end
|
||||
|
||||
def build_headers(existing)
|
||||
build_hash(:headers, existing).tap do |headers|
|
||||
headers.keys.each do |key|
|
||||
headers[key] = headers.delete(key).to_s
|
||||
if support_parallel?(adapter)
|
||||
adapter.setup_parallel_manager
|
||||
elsif block_given?
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def build_hash(method, existing)
|
||||
existing ? send(method).merge(existing) : send(method)
|
||||
# Determine if this Faraday::Connection can make parallel requests.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def in_parallel?
|
||||
!!@parallel_manager
|
||||
end
|
||||
|
||||
def params_to_query(params)
|
||||
params.inject([]) do |memo, (key, val)|
|
||||
memo << "#{escape_for_querystring(key)}=#{escape_for_querystring(val)}"
|
||||
end.join("&")
|
||||
end
|
||||
|
||||
# Some servers convert +'s in URL query params to spaces.
|
||||
# Go ahead and encode it.
|
||||
def escape_for_querystring(s)
|
||||
URI.encode_component(s.to_s, Addressable::URI::CharacterClasses::QUERY).tap do |escaped|
|
||||
escaped.gsub! /\+/, "%2B"
|
||||
# Sets up the parallel manager to make a set of requests.
|
||||
#
|
||||
# @param manager [Object] The parallel manager that this Connection's
|
||||
# Adapter uses.
|
||||
#
|
||||
# @yield a block to execute multiple requests.
|
||||
# @return [void]
|
||||
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
|
||||
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
|
||||
|
||||
# Sets the Hash proxy options.
|
||||
#
|
||||
# @param new_value [Object]
|
||||
def proxy=(new_value)
|
||||
@manual_proxy = true
|
||||
@proxy = new_value ? ProxyOptions.from(new_value) : nil
|
||||
end
|
||||
|
||||
def_delegators :url_prefix, :scheme, :scheme=, :host, :host=, :port, :port=
|
||||
def_delegator :url_prefix, :path, :path_prefix
|
||||
|
||||
# Parses the given URL with URI and stores the individual
|
||||
# components in this connection. These components serve as defaults for
|
||||
# requests made by this connection.
|
||||
#
|
||||
# @param url [String, URI]
|
||||
# @param encoder [Object]
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# conn = Faraday::Connection.new { ... }
|
||||
# conn.url_prefix = "https://httpbingo.org/api"
|
||||
# conn.scheme # => https
|
||||
# conn.path_prefix # => "/api"
|
||||
#
|
||||
# conn.get("nigiri?page=2") # accesses https://httpbingo.org/api/nigiri
|
||||
def url_prefix=(url, encoder = nil)
|
||||
uri = @url_prefix = Utils.URI(url)
|
||||
self.path_prefix = uri.path
|
||||
|
||||
params.merge_query(uri.query, encoder)
|
||||
uri.query = nil
|
||||
|
||||
with_uri_credentials(uri) do |user, password|
|
||||
set_basic_auth(user, password)
|
||||
uri.user = uri.password = nil
|
||||
end
|
||||
|
||||
@proxy = proxy_from_env(url) unless @manual_proxy
|
||||
end
|
||||
|
||||
def set_basic_auth(user, password)
|
||||
header = Faraday::Utils.basic_header_from(user, password)
|
||||
headers[Faraday::Request::Authorization::KEY] = header
|
||||
end
|
||||
|
||||
# Sets the path prefix and ensures that it always has a leading
|
||||
# slash.
|
||||
#
|
||||
# @param value [String]
|
||||
#
|
||||
# @return [String] the new path prefix
|
||||
def path_prefix=(value)
|
||||
url_prefix.path = if value
|
||||
value = "/#{value}" unless value[0, 1] == '/'
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
# Takes a relative url for a request and combines it with the defaults
|
||||
# set on the connection instance.
|
||||
#
|
||||
# @param url [String, URI, nil]
|
||||
# @param extra_params [Hash]
|
||||
#
|
||||
# @example
|
||||
# conn = Faraday::Connection.new { ... }
|
||||
# conn.url_prefix = "https://httpbingo.org/api?token=abc"
|
||||
# conn.scheme # => https
|
||||
# conn.path_prefix # => "/api"
|
||||
#
|
||||
# conn.build_url("nigiri?page=2")
|
||||
# # => https://httpbingo.org/api/nigiri?token=abc&page=2
|
||||
#
|
||||
# conn.build_url("nigiri", page: 2)
|
||||
# # => https://httpbingo.org/api/nigiri?token=abc&page=2
|
||||
#
|
||||
def build_url(url = nil, extra_params = nil)
|
||||
uri = build_exclusive_url(url)
|
||||
|
||||
query_values = params.dup.merge_query(uri.query, options.params_encoder)
|
||||
query_values.update(extra_params) if extra_params
|
||||
uri.query =
|
||||
if query_values.empty?
|
||||
nil
|
||||
else
|
||||
query_values.to_query(options.params_encoder)
|
||||
end
|
||||
|
||||
uri
|
||||
end
|
||||
|
||||
# Builds and runs the Faraday::Request.
|
||||
#
|
||||
# @param method [Symbol] HTTP method.
|
||||
# @param url [String, URI, nil] String or URI to access.
|
||||
# @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]
|
||||
def run_request(method, url, body, headers)
|
||||
unless METHODS.include?(method)
|
||||
raise ArgumentError, "unknown http method: #{method}"
|
||||
end
|
||||
|
||||
request = build_request(method) do |req|
|
||||
req.options.proxy = proxy_for_request(url)
|
||||
req.url(url) if url
|
||||
req.headers.update(headers) if headers
|
||||
req.body = body if body
|
||||
yield(req) if block_given?
|
||||
end
|
||||
|
||||
builder.build_response(self, request)
|
||||
end
|
||||
|
||||
# Creates and configures the request object.
|
||||
#
|
||||
# @param method [Symbol]
|
||||
#
|
||||
# @yield [Faraday::Request] if block given
|
||||
# @return [Faraday::Request]
|
||||
def build_request(method)
|
||||
Request.create(method) do |req|
|
||||
req.params = params.dup
|
||||
req.headers = headers.dup
|
||||
req.options = options.dup
|
||||
yield(req) if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
# Build an absolute URL based on url_prefix.
|
||||
#
|
||||
# @param url [String, URI, nil]
|
||||
# @param params [Faraday::Utils::ParamsHash] A Faraday::Utils::ParamsHash to
|
||||
# replace the query values
|
||||
# of the resulting url (default: nil).
|
||||
#
|
||||
# @return [URI]
|
||||
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.end_with?('/')
|
||||
base.path = "#{base.path}/" # ensure trailing slash
|
||||
end
|
||||
# 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)
|
||||
end
|
||||
uri.query = nil if uri.query && uri.query.empty?
|
||||
uri
|
||||
end
|
||||
|
||||
# Creates a duplicate of this Faraday::Connection.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @return [Faraday::Connection]
|
||||
def dup
|
||||
self.class.new(build_exclusive_url,
|
||||
headers: headers.dup,
|
||||
params: params.dup,
|
||||
builder: builder.dup,
|
||||
ssl: ssl.dup,
|
||||
request: options.dup)
|
||||
end
|
||||
|
||||
# Yields username and password extracted from a URI if they both exist.
|
||||
#
|
||||
# @param uri [URI]
|
||||
# @yield [username, password] any username and password
|
||||
# @yieldparam username [String] any username from URI
|
||||
# @yieldparam password [String] any password from URI
|
||||
# @return [void]
|
||||
# @api private
|
||||
def with_uri_credentials(uri)
|
||||
return unless uri.user && uri.password
|
||||
|
||||
yield(Utils.unescape(uri.user), Utils.unescape(uri.password))
|
||||
end
|
||||
|
||||
def proxy_from_env(url)
|
||||
return if Faraday.ignore_env_proxy
|
||||
|
||||
uri = nil
|
||||
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
|
||||
end
|
||||
|
||||
def find_default_proxy
|
||||
uri = ENV.fetch('http_proxy', nil)
|
||||
return unless uri && !uri.empty?
|
||||
|
||||
uri = "http://#{uri}" unless uri.match?(/^http/i)
|
||||
uri
|
||||
end
|
||||
|
||||
def proxy_for_request(url)
|
||||
return proxy if @manual_proxy
|
||||
|
||||
if url && Utils.URI(url).absolute?
|
||||
proxy_from_env(url)
|
||||
else
|
||||
proxy
|
||||
end
|
||||
end
|
||||
|
||||
def support_parallel?(adapter)
|
||||
adapter.respond_to?(:supports_parallel?) && adapter&.supports_parallel?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
105
lib/faraday/encoders/flat_params_encoder.rb
Normal file
@ -0,0 +1,105 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# FlatParamsEncoder manages URI params as a flat hash. Any Array values repeat
|
||||
# the parameter multiple times.
|
||||
module FlatParamsEncoder
|
||||
class << self
|
||||
extend Forwardable
|
||||
def_delegators :'Faraday::Utils', :escape, :unescape
|
||||
end
|
||||
|
||||
# Encode converts the given param into a URI querystring. Keys and values
|
||||
# will converted to strings and appropriately escaped for the URI.
|
||||
#
|
||||
# @param params [Hash] query arguments to convert.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# encode({a: %w[one two three], b: true, c: "C"})
|
||||
# # => 'a=one&a=two&a=three&b=true&c=C'
|
||||
#
|
||||
# @return [String] the URI querystring (without the leading '?')
|
||||
def self.encode(params)
|
||||
return nil if params.nil?
|
||||
|
||||
unless params.is_a?(Array)
|
||||
unless params.respond_to?(:to_hash)
|
||||
raise TypeError,
|
||||
"Can't convert #{params.class} into Hash."
|
||||
end
|
||||
params = params.to_hash
|
||||
params = params.map do |key, value|
|
||||
key = key.to_s if key.is_a?(Symbol)
|
||||
[key, value]
|
||||
end
|
||||
|
||||
# Only to be used for non-Array inputs. Arrays should preserve order.
|
||||
params.sort! if @sort_params
|
||||
end
|
||||
|
||||
# The params have form [['key1', 'value1'], ['key2', 'value2']].
|
||||
buffer = +''
|
||||
params.each do |key, value|
|
||||
encoded_key = escape(key)
|
||||
if value.nil?
|
||||
buffer << "#{encoded_key}&"
|
||||
elsif value.is_a?(Array)
|
||||
if value.empty?
|
||||
buffer << "#{encoded_key}=&"
|
||||
else
|
||||
value.each do |sub_value|
|
||||
encoded_value = escape(sub_value)
|
||||
buffer << "#{encoded_key}=#{encoded_value}&"
|
||||
end
|
||||
end
|
||||
else
|
||||
encoded_value = escape(value)
|
||||
buffer << "#{encoded_key}=#{encoded_value}&"
|
||||
end
|
||||
end
|
||||
buffer.chop
|
||||
end
|
||||
|
||||
# Decode converts the given URI querystring into a hash.
|
||||
#
|
||||
# @param query [String] query arguments to parse.
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# decode('a=one&a=two&a=three&b=true&c=C')
|
||||
# # => {"a"=>["one", "two", "three"], "b"=>"true", "c"=>"C"}
|
||||
#
|
||||
# @return [Hash] parsed keys and value strings from the querystring.
|
||||
def self.decode(query)
|
||||
return nil if query.nil?
|
||||
|
||||
empty_accumulator = {}
|
||||
|
||||
split_query = query.split('&').filter_map do |pair|
|
||||
pair.split('=', 2) if pair && !pair.empty?
|
||||
end
|
||||
split_query.each_with_object(empty_accumulator.dup) do |pair, accu|
|
||||
pair[0] = unescape(pair[0])
|
||||
pair[1] = true if pair[1].nil?
|
||||
if pair[1].respond_to?(:to_str)
|
||||
pair[1] = unescape(pair[1].to_str.tr('+', ' '))
|
||||
end
|
||||
if accu[pair[0]].is_a?(Array)
|
||||
accu[pair[0]] << pair[1]
|
||||
elsif accu[pair[0]]
|
||||
accu[pair[0]] = [accu[pair[0]], pair[1]]
|
||||
else
|
||||
accu[pair[0]] = pair[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
attr_accessor :sort_params
|
||||
end
|
||||
|
||||
# Useful default for OAuth and caching.
|
||||
@sort_params = true
|
||||
end
|
||||
end
|
183
lib/faraday/encoders/nested_params_encoder.rb
Normal file
@ -0,0 +1,183 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# Sub-module for encoding parameters into query-string.
|
||||
module EncodeMethods
|
||||
# @param params [nil, Array, #to_hash] parameters to be encoded
|
||||
#
|
||||
# @return [String] the encoded params
|
||||
#
|
||||
# @raise [TypeError] if params can not be converted to a Hash
|
||||
def encode(params)
|
||||
return nil if params.nil?
|
||||
|
||||
unless params.is_a?(Array)
|
||||
unless params.respond_to?(:to_hash)
|
||||
raise TypeError, "Can't convert #{params.class} into Hash."
|
||||
end
|
||||
|
||||
params = params.to_hash
|
||||
params = params.map do |key, value|
|
||||
key = key.to_s if key.is_a?(Symbol)
|
||||
[key, value]
|
||||
end
|
||||
|
||||
# Only to be used for non-Array inputs. Arrays should preserve order.
|
||||
params.sort! if @sort_params
|
||||
end
|
||||
|
||||
# The params have form [['key1', 'value1'], ['key2', 'value2']].
|
||||
buffer = +''
|
||||
params.each do |parent, value|
|
||||
encoded_parent = escape(parent)
|
||||
buffer << "#{encode_pair(encoded_parent, value)}&"
|
||||
end
|
||||
buffer.chop
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def encode_pair(parent, value)
|
||||
if value.is_a?(Hash)
|
||||
encode_hash(parent, value)
|
||||
elsif value.is_a?(Array)
|
||||
encode_array(parent, value)
|
||||
elsif value.nil?
|
||||
parent
|
||||
else
|
||||
encoded_value = escape(value)
|
||||
"#{parent}=#{encoded_value}"
|
||||
end
|
||||
end
|
||||
|
||||
def encode_hash(parent, value)
|
||||
value = value.map { |key, val| [escape(key), val] }.sort
|
||||
|
||||
buffer = +''
|
||||
value.each do |key, val|
|
||||
new_parent = "#{parent}%5B#{key}%5D"
|
||||
buffer << "#{encode_pair(new_parent, val)}&"
|
||||
end
|
||||
buffer.chop
|
||||
end
|
||||
|
||||
def encode_array(parent, value)
|
||||
return "#{parent}%5B%5D" if value.empty?
|
||||
|
||||
buffer = +''
|
||||
value.each_with_index do |val, index|
|
||||
new_parent = if @array_indices
|
||||
"#{parent}%5B#{index}%5D"
|
||||
else
|
||||
"#{parent}%5B%5D"
|
||||
end
|
||||
buffer << "#{encode_pair(new_parent, val)}&"
|
||||
end
|
||||
buffer.chop
|
||||
end
|
||||
end
|
||||
|
||||
# Sub-module for decoding query-string into parameters.
|
||||
module DecodeMethods
|
||||
# @param query [nil, String]
|
||||
#
|
||||
# @return [Array<Array, String>] the decoded params
|
||||
#
|
||||
# @raise [TypeError] if the nesting is incorrect
|
||||
def decode(query)
|
||||
return nil if query.nil?
|
||||
|
||||
params = {}
|
||||
query.split('&').each do |pair|
|
||||
next if pair.empty?
|
||||
|
||||
key, value = pair.split('=', 2)
|
||||
key = unescape(key)
|
||||
value = unescape(value.tr('+', ' ')) if value
|
||||
decode_pair(key, value, params)
|
||||
end
|
||||
|
||||
dehash(params, 0)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
SUBKEYS_REGEX = /[^\[\]]+(?:\]?\[\])?/
|
||||
|
||||
def decode_pair(key, value, context)
|
||||
subkeys = key.scan(SUBKEYS_REGEX)
|
||||
subkeys.each_with_index do |subkey, i|
|
||||
is_array = subkey =~ /[\[\]]+\Z/
|
||||
subkey = Regexp.last_match.pre_match if is_array
|
||||
last_subkey = i == subkeys.length - 1
|
||||
|
||||
context = prepare_context(context, subkey, is_array, last_subkey)
|
||||
add_to_context(is_array, context, value, subkey) if last_subkey
|
||||
end
|
||||
end
|
||||
|
||||
def prepare_context(context, subkey, is_array, last_subkey)
|
||||
if !last_subkey || is_array
|
||||
context = new_context(subkey, is_array, context)
|
||||
end
|
||||
if context.is_a?(Array) && !is_array
|
||||
context = match_context(context, subkey)
|
||||
end
|
||||
context
|
||||
end
|
||||
|
||||
def new_context(subkey, is_array, context)
|
||||
value_type = is_array ? Array : Hash
|
||||
if context[subkey] && !context[subkey].is_a?(value_type)
|
||||
raise TypeError, "expected #{value_type.name} " \
|
||||
"(got #{context[subkey].class.name}) for param `#{subkey}'"
|
||||
end
|
||||
|
||||
context[subkey] ||= value_type.new
|
||||
end
|
||||
|
||||
def match_context(context, subkey)
|
||||
context << {} if !context.last.is_a?(Hash) || context.last.key?(subkey)
|
||||
context.last
|
||||
end
|
||||
|
||||
def add_to_context(is_array, context, value, subkey)
|
||||
is_array ? context << value : context[subkey] = value
|
||||
end
|
||||
|
||||
# Internal: convert a nested hash with purely numeric keys into an array.
|
||||
# FIXME: this is not compatible with Rack::Utils.parse_nested_query
|
||||
# @!visibility private
|
||||
def dehash(hash, depth)
|
||||
hash.each do |key, value|
|
||||
hash[key] = dehash(value, depth + 1) if value.is_a?(Hash)
|
||||
end
|
||||
|
||||
if depth.positive? && !hash.empty? && hash.keys.all? { |k| k =~ /^\d+$/ }
|
||||
hash.sort.map(&:last)
|
||||
else
|
||||
hash
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This is the default encoder for Faraday requests.
|
||||
# Using this encoder, parameters will be encoded respecting their structure,
|
||||
# so you can send objects such as Arrays or Hashes as parameters
|
||||
# for your requests.
|
||||
module NestedParamsEncoder
|
||||
class << self
|
||||
attr_accessor :sort_params, :array_indices
|
||||
|
||||
extend Forwardable
|
||||
def_delegators :'Faraday::Utils', :escape, :unescape
|
||||
end
|
||||
|
||||
# Useful default for OAuth and caching.
|
||||
@sort_params = true
|
||||
@array_indices = false
|
||||
|
||||
extend EncodeMethods
|
||||
extend DecodeMethods
|
||||
end
|
||||
end
|
@ -1,6 +1,199 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Faraday namespace.
|
||||
module Faraday
|
||||
module Error
|
||||
class ConnectionFailed < StandardError; end
|
||||
class ResourceNotFound < StandardError; end
|
||||
# Faraday error base class.
|
||||
class Error < StandardError
|
||||
attr_reader :response, :wrapped_exception
|
||||
|
||||
def initialize(exc = nil, response = nil)
|
||||
@wrapped_exception = nil unless defined?(@wrapped_exception)
|
||||
@response = nil unless defined?(@response)
|
||||
super(exc_msg_and_response!(exc, response))
|
||||
end
|
||||
|
||||
def backtrace
|
||||
if @wrapped_exception
|
||||
@wrapped_exception.backtrace
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
inner = +''
|
||||
inner << " wrapped=#{@wrapped_exception.inspect}" if @wrapped_exception
|
||||
inner << " response=#{@response.inspect}" if @response
|
||||
inner << " #{super}" if inner.empty?
|
||||
%(#<#{self.class}#{inner}>)
|
||||
end
|
||||
|
||||
def response_status
|
||||
return unless @response
|
||||
|
||||
@response.is_a?(Faraday::Response) ? @response.status : @response[:status]
|
||||
end
|
||||
|
||||
def response_headers
|
||||
return unless @response
|
||||
|
||||
@response.is_a?(Faraday::Response) ? @response.headers : @response[:headers]
|
||||
end
|
||||
|
||||
def response_body
|
||||
return unless @response
|
||||
|
||||
@response.is_a?(Faraday::Response) ? @response.body : @response[:body]
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Pulls out potential parent exception and response hash, storing them in
|
||||
# instance variables.
|
||||
# exc - Either an Exception, a string message, or a response hash.
|
||||
# response - Hash
|
||||
# :status - Optional integer HTTP response status
|
||||
# :headers - String key/value hash of HTTP response header
|
||||
# values.
|
||||
# :body - Optional string HTTP response body.
|
||||
# :request - Hash
|
||||
# :method - Symbol with the request HTTP method.
|
||||
# :url - URI object with the url requested.
|
||||
# :url_path - String with the url path requested.
|
||||
# :params - String key/value hash of query params
|
||||
# present in the request.
|
||||
# :headers - String key/value hash of HTTP request
|
||||
# header values.
|
||||
# :body - String HTTP request body.
|
||||
#
|
||||
# If a subclass has to call this, then it should pass a string message
|
||||
# to `super`. See NilStatusError.
|
||||
def exc_msg_and_response!(exc, response = nil)
|
||||
if @response.nil? && @wrapped_exception.nil?
|
||||
@wrapped_exception, msg, @response = exc_msg_and_response(exc, response)
|
||||
return msg
|
||||
end
|
||||
|
||||
exc.to_s
|
||||
end
|
||||
|
||||
# Pulls out potential parent exception and response hash.
|
||||
def exc_msg_and_response(exc, response = nil)
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
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
|
||||
|
||||
# Faraday client error class. Represents 4xx status responses.
|
||||
class ClientError < Error
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 400 response.
|
||||
class BadRequestError < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 401 response.
|
||||
class UnauthorizedError < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 403 response.
|
||||
class ForbiddenError < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 404 response.
|
||||
class ResourceNotFound < ClientError
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 407 response.
|
||||
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
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a 422 response.
|
||||
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
|
||||
|
||||
# A unified client error for timeouts.
|
||||
class TimeoutError < ServerError
|
||||
def initialize(exc = 'timeout', response = nil)
|
||||
super(exc, response)
|
||||
end
|
||||
end
|
||||
|
||||
# Raised by Faraday::Response::RaiseError in case of a nil status in response.
|
||||
class NilStatusError < ServerError
|
||||
def initialize(exc, response = nil)
|
||||
exc_msg_and_response!(exc, response)
|
||||
super('http status could not be derived from the server response')
|
||||
end
|
||||
end
|
||||
|
||||
# A unified error for failed connections.
|
||||
class ConnectionFailed < Error
|
||||
end
|
||||
|
||||
# A unified client error for SSL errors.
|
||||
class SSLError < Error
|
||||
end
|
||||
|
||||
# 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,13 +0,0 @@
|
||||
module Faraday
|
||||
module Loadable
|
||||
def self.extended mod
|
||||
class << mod
|
||||
attr_accessor :load_error
|
||||
end
|
||||
end
|
||||
|
||||
def self.loaded?
|
||||
load_error.nil?
|
||||
end
|
||||
end
|
||||
end
|
118
lib/faraday/logging/formatter.rb
Normal file
@ -0,0 +1,118 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'pp' # This require is necessary for Hash#pretty_inspect to work, do not remove it, people rely on it.
|
||||
|
||||
module Faraday
|
||||
module Logging
|
||||
# Serves as an integration point to customize logging
|
||||
class Formatter
|
||||
extend Forwardable
|
||||
|
||||
DEFAULT_OPTIONS = { headers: true, bodies: false, errors: false,
|
||||
log_level: :info }.freeze
|
||||
|
||||
def initialize(logger:, options:)
|
||||
@logger = logger
|
||||
@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)
|
||||
public_send(log_level) do
|
||||
"request: #{env.method.upcase} #{apply_filters(env.url.to_s)}"
|
||||
end
|
||||
|
||||
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)
|
||||
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)
|
||||
end
|
||||
|
||||
def exception(exc)
|
||||
return unless log_errors?
|
||||
|
||||
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)
|
||||
|
||||
log_body('error', exc.response_body)
|
||||
end
|
||||
|
||||
def filter(filter_word, filter_replacement)
|
||||
@filter.push([filter_word, filter_replacement])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def dump_headers(headers)
|
||||
return if headers.nil?
|
||||
|
||||
headers.map { |k, v| "#{k}: #{v.inspect}" }.join("\n")
|
||||
end
|
||||
|
||||
def dump_body(body)
|
||||
if body.respond_to?(:to_str)
|
||||
body.to_str
|
||||
else
|
||||
pretty_inspect(body)
|
||||
end
|
||||
end
|
||||
|
||||
def pretty_inspect(body)
|
||||
body.pretty_inspect
|
||||
end
|
||||
|
||||
def log_headers?(type)
|
||||
case @options[:headers]
|
||||
when Hash
|
||||
@options[:headers][type]
|
||||
else
|
||||
@options[:headers]
|
||||
end
|
||||
end
|
||||
|
||||
def log_body?(type)
|
||||
case @options[:bodies]
|
||||
when Hash
|
||||
@options[:bodies][type]
|
||||
else
|
||||
@options[:bodies]
|
||||
end
|
||||
end
|
||||
|
||||
def log_errors?
|
||||
@options[:errors]
|
||||
end
|
||||
|
||||
def apply_filters(output)
|
||||
@filter.each do |pattern, replacement|
|
||||
output = output.to_s.gsub(pattern, replacement)
|
||||
end
|
||||
output
|
||||
end
|
||||
|
||||
def log_level
|
||||
@options[:log_level]
|
||||
end
|
||||
|
||||
def log_headers(type, headers)
|
||||
public_send(log_level) { "#{type}: #{apply_filters(dump_headers(headers))}" }
|
||||
end
|
||||
|
||||
def log_body(type, body)
|
||||
public_send(log_level) { "#{type}: #{apply_filters(dump_body(body))}" }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
6
lib/faraday/methods.rb
Normal file
@ -0,0 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
METHODS_WITH_QUERY = %w[get head delete trace].freeze
|
||||
METHODS_WITH_BODY = %w[post put patch].freeze
|
||||
end
|
72
lib/faraday/middleware.rb
Normal file
@ -0,0 +1,72 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'monitor'
|
||||
|
||||
module Faraday
|
||||
# Middleware is the basic base class of any Faraday middleware.
|
||||
class Middleware
|
||||
extend MiddlewareRegistry
|
||||
|
||||
attr_reader :app, :options
|
||||
|
||||
DEFAULT_OPTIONS = {}.freeze
|
||||
LOCK = Mutex.new
|
||||
|
||||
def initialize(app = nil, options = {})
|
||||
@app = app
|
||||
@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)
|
||||
on_request(env) if respond_to?(:on_request)
|
||||
app.call(env).on_complete do |environment|
|
||||
on_complete(environment) if respond_to?(:on_complete)
|
||||
end
|
||||
rescue StandardError => e
|
||||
on_error(e) if respond_to?(:on_error)
|
||||
raise
|
||||
end
|
||||
|
||||
def close
|
||||
if app.respond_to?(:close)
|
||||
app.close
|
||||
else
|
||||
warn "#{app} does not implement \#close!"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
83
lib/faraday/middleware_registry.rb
Normal file
@ -0,0 +1,83 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'monitor'
|
||||
|
||||
module Faraday
|
||||
# Adds the ability for other modules to register and lookup
|
||||
# middleware classes.
|
||||
module MiddlewareRegistry
|
||||
def registered_middleware
|
||||
@registered_middleware ||= {}
|
||||
end
|
||||
|
||||
# Register middleware class(es) on the current module.
|
||||
#
|
||||
# @param mappings [Hash] Middleware mappings from a lookup symbol to a middleware class.
|
||||
# @return [void]
|
||||
#
|
||||
# @example Lookup by a constant
|
||||
#
|
||||
# module Faraday
|
||||
# class Whatever < Middleware
|
||||
# # Middleware looked up by :foo returns Faraday::Whatever::Foo.
|
||||
# register_middleware(foo: Whatever)
|
||||
# end
|
||||
# end
|
||||
def register_middleware(**mappings)
|
||||
middleware_mutex do
|
||||
registered_middleware.update(mappings)
|
||||
end
|
||||
end
|
||||
|
||||
# Unregister a previously registered middleware class.
|
||||
#
|
||||
# @param key [Symbol] key for the registered middleware.
|
||||
def unregister_middleware(key)
|
||||
registered_middleware.delete(key)
|
||||
end
|
||||
|
||||
# Lookup middleware class with a registered Symbol shortcut.
|
||||
#
|
||||
# @param key [Symbol] key for the registered middleware.
|
||||
# @return [Class] a middleware Class.
|
||||
# @raise [Faraday::Error] if given key is not registered
|
||||
#
|
||||
# @example
|
||||
#
|
||||
# module Faraday
|
||||
# class Whatever < Middleware
|
||||
# register_middleware(foo: Whatever)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Faraday::Middleware.lookup_middleware(:foo)
|
||||
# # => Faraday::Whatever
|
||||
def lookup_middleware(key)
|
||||
load_middleware(key) ||
|
||||
raise(Faraday::Error, "#{key.inspect} is not registered on #{self}")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def middleware_mutex(&block)
|
||||
@middleware_mutex ||= Monitor.new
|
||||
@middleware_mutex.synchronize(&block)
|
||||
end
|
||||
|
||||
def load_middleware(key)
|
||||
value = registered_middleware[key]
|
||||
case value
|
||||
when Module
|
||||
value
|
||||
when Symbol, String
|
||||
middleware_mutex do
|
||||
@registered_middleware[key] = const_get(value)
|
||||
end
|
||||
when Proc
|
||||
middleware_mutex do
|
||||
@registered_middleware[key] = value.call
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
219
lib/faraday/options.rb
Normal file
@ -0,0 +1,219 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# Subclasses Struct with some special helpers for converting from a Hash to
|
||||
# a Struct.
|
||||
class Options < Struct
|
||||
# Public
|
||||
def self.from(value)
|
||||
value ? new.update(value) : new
|
||||
end
|
||||
|
||||
# Public
|
||||
def each
|
||||
return to_enum(:each) unless block_given?
|
||||
|
||||
members.each do |key|
|
||||
yield(key.to_sym, send(key))
|
||||
end
|
||||
end
|
||||
|
||||
# Public
|
||||
def update(obj)
|
||||
obj.each do |key, value|
|
||||
sub_options = self.class.options_for(key)
|
||||
if sub_options
|
||||
new_value = sub_options.from(value) if value
|
||||
elsif value.is_a?(Hash)
|
||||
new_value = value.dup
|
||||
else
|
||||
new_value = value
|
||||
end
|
||||
|
||||
send(:"#{key}=", new_value) unless new_value.nil?
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Public
|
||||
def delete(key)
|
||||
value = send(key)
|
||||
send(:"#{key}=", nil)
|
||||
value
|
||||
end
|
||||
|
||||
# Public
|
||||
def clear
|
||||
members.each { |member| delete(member) }
|
||||
end
|
||||
|
||||
# Public
|
||||
def merge!(other)
|
||||
other.each do |key, other_value|
|
||||
self_value = send(key)
|
||||
sub_options = self.class.options_for(key)
|
||||
new_value = if self_value && sub_options && other_value
|
||||
self_value.merge(other_value)
|
||||
else
|
||||
other_value
|
||||
end
|
||||
send(:"#{key}=", new_value) unless new_value.nil?
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Public
|
||||
def merge(other)
|
||||
dup.merge!(other)
|
||||
end
|
||||
|
||||
# Public
|
||||
def deep_dup
|
||||
self.class.from(self)
|
||||
end
|
||||
|
||||
# Public
|
||||
def fetch(key, *args)
|
||||
unless symbolized_key_set.include?(key.to_sym)
|
||||
key_setter = "#{key}="
|
||||
if !args.empty?
|
||||
send(key_setter, args.first)
|
||||
elsif block_given?
|
||||
send(key_setter, yield(key))
|
||||
else
|
||||
raise self.class.fetch_error_class, "key not found: #{key.inspect}"
|
||||
end
|
||||
end
|
||||
send(key)
|
||||
end
|
||||
|
||||
# Public
|
||||
def values_at(*keys)
|
||||
keys.map { |key| send(key) }
|
||||
end
|
||||
|
||||
# Public
|
||||
def keys
|
||||
members.reject { |member| send(member).nil? }
|
||||
end
|
||||
|
||||
# Public
|
||||
def empty?
|
||||
keys.empty?
|
||||
end
|
||||
|
||||
# Public
|
||||
def each_key(&block)
|
||||
return to_enum(:each_key) unless block
|
||||
|
||||
keys.each(&block)
|
||||
end
|
||||
|
||||
# Public
|
||||
def key?(key)
|
||||
keys.include?(key)
|
||||
end
|
||||
|
||||
alias has_key? key?
|
||||
|
||||
# Public
|
||||
def each_value(&block)
|
||||
return to_enum(:each_value) unless block
|
||||
|
||||
values.each(&block)
|
||||
end
|
||||
|
||||
# Public
|
||||
def value?(value)
|
||||
values.include?(value)
|
||||
end
|
||||
|
||||
alias has_value? value?
|
||||
|
||||
# Public
|
||||
def to_hash
|
||||
hash = {}
|
||||
members.each do |key|
|
||||
value = send(key)
|
||||
hash[key.to_sym] = value unless value.nil?
|
||||
end
|
||||
hash
|
||||
end
|
||||
|
||||
# Internal
|
||||
def inspect
|
||||
values = []
|
||||
members.each do |member|
|
||||
value = send(member)
|
||||
values << "#{member}=#{value.inspect}" if value
|
||||
end
|
||||
values = values.empty? ? '(empty)' : values.join(', ')
|
||||
|
||||
%(#<#{self.class} #{values}>)
|
||||
end
|
||||
|
||||
# Internal
|
||||
def self.options(mapping)
|
||||
attribute_options.update(mapping)
|
||||
end
|
||||
|
||||
# Internal
|
||||
def self.options_for(key)
|
||||
attribute_options[key]
|
||||
end
|
||||
|
||||
# Internal
|
||||
def self.attribute_options
|
||||
@attribute_options ||= {}
|
||||
end
|
||||
|
||||
def self.memoized(key, &block)
|
||||
unless block
|
||||
raise ArgumentError, '#memoized must be called with a block'
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def self.memoized_attributes
|
||||
@memoized_attributes ||= {}
|
||||
end
|
||||
|
||||
def [](key)
|
||||
key = key.to_sym
|
||||
if (method = self.class.memoized_attributes[key])
|
||||
super(key) || (self[key] = instance_eval(&method))
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def symbolized_key_set
|
||||
@symbolized_key_set ||= Set.new(keys.map(&:to_sym))
|
||||
end
|
||||
|
||||
def self.inherited(subclass)
|
||||
super
|
||||
subclass.attribute_options.update(attribute_options)
|
||||
subclass.memoized_attributes.update(memoized_attributes)
|
||||
end
|
||||
|
||||
def self.fetch_error_class
|
||||
@fetch_error_class ||= if Object.const_defined?(:KeyError)
|
||||
::KeyError
|
||||
else
|
||||
::IndexError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'faraday/options/request_options'
|
||||
require 'faraday/options/ssl_options'
|
||||
require 'faraday/options/proxy_options'
|
||||
require 'faraday/options/connection_options'
|
||||
require 'faraday/options/env'
|
23
lib/faraday/options/connection_options.rb
Normal file
@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# @!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 }
|
||||
|
||||
memoized(:ssl) { self.class.options_for(:ssl).new }
|
||||
|
||||
memoized(:builder_class) { RackBuilder }
|
||||
|
||||
def new_builder(block)
|
||||
builder_class.new(&block)
|
||||
end
|
||||
end
|
||||
end
|
204
lib/faraday/options/env.rb
Normal file
@ -0,0 +1,204 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# @!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.
|
||||
const_set(:MethodsWithBodies, Set.new(Faraday::METHODS_WITH_BODY.map(&:to_sym)))
|
||||
|
||||
options request: RequestOptions,
|
||||
request_headers: Utils::Headers, response_headers: Utils::Headers
|
||||
|
||||
extend Forwardable
|
||||
|
||||
def_delegators :request, :params_encoder
|
||||
|
||||
# Build a new Env from given value. Respects and updates `custom_members`.
|
||||
#
|
||||
# @param value [Object] a value fitting Option.from(v).
|
||||
# @return [Env] from given value
|
||||
def self.from(value)
|
||||
env = super(value)
|
||||
if value.respond_to?(:custom_members)
|
||||
env.custom_members.update(value.custom_members)
|
||||
end
|
||||
env
|
||||
end
|
||||
|
||||
# @param key [Object]
|
||||
def [](key)
|
||||
return self[current_body] if key == :body
|
||||
|
||||
if in_member_set?(key)
|
||||
super(key)
|
||||
else
|
||||
custom_members[key]
|
||||
end
|
||||
end
|
||||
|
||||
# @param key [Object]
|
||||
# @param value [Object]
|
||||
def []=(key, value)
|
||||
if key == :body
|
||||
super(current_body, value)
|
||||
return
|
||||
end
|
||||
|
||||
if in_member_set?(key)
|
||||
super(key, value)
|
||||
else
|
||||
custom_members[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
def current_body
|
||||
!!status ? :response_body : :request_body
|
||||
end
|
||||
|
||||
def body
|
||||
self[:body]
|
||||
end
|
||||
|
||||
def body=(value)
|
||||
self[:body] = value
|
||||
end
|
||||
|
||||
# @return [Boolean] true if status is in the set of {SuccessfulStatuses}.
|
||||
def success?
|
||||
Env::SuccessfulStatuses.include?(status)
|
||||
end
|
||||
|
||||
# @return [Boolean] true if there's no body yet, and the method is in the
|
||||
# set of {Env::MethodsWithBodies}.
|
||||
def needs_body?
|
||||
!body && Env::MethodsWithBodies.include?(method)
|
||||
end
|
||||
|
||||
# Sets content length to zero and the body to the empty string.
|
||||
def clear_body
|
||||
request_headers[Env::ContentLength] = '0'
|
||||
self.body = +''
|
||||
end
|
||||
|
||||
# @return [Boolean] true if the status isn't in the set of
|
||||
# {Env::StatusesWithoutBody}.
|
||||
def parse_body?
|
||||
!Env::StatusesWithoutBody.include?(status)
|
||||
end
|
||||
|
||||
# @return [Boolean] true if there is a parallel_manager
|
||||
def parallel?
|
||||
!!parallel_manager
|
||||
end
|
||||
|
||||
def inspect
|
||||
attrs = [nil]
|
||||
members.each do |mem|
|
||||
if (value = send(mem))
|
||||
attrs << "@#{mem}=#{value.inspect}"
|
||||
end
|
||||
end
|
||||
attrs << "@custom=#{custom_members.inspect}" unless custom_members.empty?
|
||||
%(#<#{self.class}#{attrs.join(' ')}>)
|
||||
end
|
||||
|
||||
def stream_response?
|
||||
request.stream_response?
|
||||
end
|
||||
|
||||
def stream_response(&block)
|
||||
size = 0
|
||||
yielded = false
|
||||
block_result = block.call do |chunk|
|
||||
if chunk.bytesize.positive? || size.positive?
|
||||
yielded = true
|
||||
size += chunk.bytesize
|
||||
request.on_data.call(chunk, size, self)
|
||||
end
|
||||
end
|
||||
request.on_data.call(+'', 0, self) unless yielded
|
||||
block_result
|
||||
end
|
||||
|
||||
# @private
|
||||
def custom_members
|
||||
@custom_members ||= {}
|
||||
end
|
||||
|
||||
# @private
|
||||
if members.first.is_a?(Symbol)
|
||||
def in_member_set?(key)
|
||||
self.class.member_set.include?(key.to_sym)
|
||||
end
|
||||
else
|
||||
def in_member_set?(key)
|
||||
self.class.member_set.include?(key.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
# @private
|
||||
def self.member_set
|
||||
@member_set ||= Set.new(members)
|
||||
end
|
||||
end
|
||||
end
|
38
lib/faraday/options/proxy_options.rb
Normal file
@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# @!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.
|
||||
value = "http://#{value}" unless value.include?('://')
|
||||
value = { uri: Utils.URI(value) }
|
||||
when URI
|
||||
value = { uri: value }
|
||||
when Hash, Options
|
||||
if value[:uri]
|
||||
value = value.dup.tap do |duped|
|
||||
duped[:uri] = Utils.URI(duped[:uri])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
super(value)
|
||||
end
|
||||
|
||||
memoized(:user) { uri&.user && Utils.unescape(uri.user) }
|
||||
memoized(:password) { uri&.password && Utils.unescape(uri.password) }
|
||||
end
|
||||
end
|
23
lib/faraday/options/request_options.rb
Normal file
@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# @!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)
|
||||
else
|
||||
super(key, value)
|
||||
end
|
||||
end
|
||||
|
||||
def stream_response?
|
||||
on_data.is_a?(Proc)
|
||||
end
|
||||
end
|
||||
end
|
76
lib/faraday/options/ssl_options.rb
Normal file
@ -0,0 +1,76 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# @!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
|
||||
end
|
||||
|
||||
# @return [Boolean] true if should not verify
|
||||
def disable?
|
||||
!verify?
|
||||
end
|
||||
|
||||
# @return [Boolean] true if should verify_hostname
|
||||
def verify_hostname?
|
||||
verify_hostname != false
|
||||
end
|
||||
end
|
||||
end
|
5
lib/faraday/parameters.rb
Normal file
@ -0,0 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'forwardable'
|
||||
require 'faraday/encoders/nested_params_encoder'
|
||||
require 'faraday/encoders/flat_params_encoder'
|
248
lib/faraday/rack_builder.rb
Normal file
@ -0,0 +1,248 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'faraday/adapter_registry'
|
||||
|
||||
module Faraday
|
||||
# A Builder that processes requests into responses by passing through an inner
|
||||
# middleware stack (heavily inspired by Rack).
|
||||
#
|
||||
# @example
|
||||
# Faraday::Connection.new(url: 'http://httpbingo.org') do |builder|
|
||||
# builder.request :url_encoded # Faraday::Request::UrlEncoded
|
||||
# builder.adapter :net_http # Faraday::Adapter::NetHttp
|
||||
# end
|
||||
class RackBuilder
|
||||
# Used to detect missing arguments
|
||||
NO_ARGUMENT = Object.new
|
||||
|
||||
attr_accessor :handlers
|
||||
|
||||
# Error raised when trying to modify the stack after calling `lock!`
|
||||
class StackLocked < RuntimeError; end
|
||||
|
||||
# borrowed from ActiveSupport::Dependencies::Reference &
|
||||
# ActionDispatch::MiddlewareStack::Middleware
|
||||
class Handler
|
||||
REGISTRY = Faraday::AdapterRegistry.new
|
||||
|
||||
attr_reader :name
|
||||
|
||||
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
|
||||
|
||||
def klass
|
||||
REGISTRY.get(@name)
|
||||
end
|
||||
|
||||
def inspect
|
||||
@name
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
if other.is_a? Handler
|
||||
name == other.name
|
||||
elsif other.respond_to? :name
|
||||
klass == other
|
||||
else
|
||||
@name == other.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def build(app = nil)
|
||||
klass.new(app, *@args, **@kwargs, &@block)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(&block)
|
||||
@adapter = nil
|
||||
@handlers = []
|
||||
build(&block)
|
||||
end
|
||||
|
||||
def initialize_dup(original)
|
||||
super
|
||||
@adapter = original.adapter
|
||||
@handlers = original.handlers.dup
|
||||
end
|
||||
|
||||
def build
|
||||
raise_if_locked
|
||||
block_given? ? yield(self) : request(:url_encoded)
|
||||
adapter(Faraday.default_adapter, **Faraday.default_adapter_options) unless @adapter
|
||||
end
|
||||
|
||||
def [](idx)
|
||||
@handlers[idx]
|
||||
end
|
||||
|
||||
# Locks the middleware stack to ensure no further modifications are made.
|
||||
def lock!
|
||||
@handlers.freeze
|
||||
end
|
||||
|
||||
def locked?
|
||||
@handlers.frozen?
|
||||
end
|
||||
|
||||
def use(klass, ...)
|
||||
if klass.is_a? Symbol
|
||||
use_symbol(Faraday::Middleware, klass, ...)
|
||||
else
|
||||
raise_if_locked
|
||||
raise_if_adapter(klass)
|
||||
@handlers << self.class::Handler.new(klass, ...)
|
||||
end
|
||||
end
|
||||
|
||||
def request(key, ...)
|
||||
use_symbol(Faraday::Request, key, ...)
|
||||
end
|
||||
|
||||
def response(...)
|
||||
use_symbol(Faraday::Response, ...)
|
||||
end
|
||||
|
||||
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, **kwargs, &block)
|
||||
end
|
||||
|
||||
## methods to push onto the various positions in the stack:
|
||||
|
||||
def insert(index, ...)
|
||||
raise_if_locked
|
||||
index = assert_index(index)
|
||||
handler = self.class::Handler.new(...)
|
||||
@handlers.insert(index, handler)
|
||||
end
|
||||
|
||||
alias insert_before insert
|
||||
|
||||
def insert_after(index, ...)
|
||||
index = assert_index(index)
|
||||
insert(index + 1, ...)
|
||||
end
|
||||
|
||||
def swap(index, ...)
|
||||
raise_if_locked
|
||||
index = assert_index(index)
|
||||
@handlers.delete_at(index)
|
||||
insert(index, ...)
|
||||
end
|
||||
|
||||
def delete(handler)
|
||||
raise_if_locked
|
||||
@handlers.delete(handler)
|
||||
end
|
||||
|
||||
# Processes a Request into a Response by passing it through this Builder's
|
||||
# middleware stack.
|
||||
#
|
||||
# @param connection [Faraday::Connection]
|
||||
# @param request [Faraday::Request]
|
||||
#
|
||||
# @return [Faraday::Response]
|
||||
def build_response(connection, request)
|
||||
app.call(build_env(connection, request))
|
||||
end
|
||||
|
||||
# The "rack app" wrapped in middleware. All requests are sent here.
|
||||
#
|
||||
# The builder is responsible for creating the app object. After this,
|
||||
# the builder gets locked to ensure no further modifications are made
|
||||
# to the middleware stack.
|
||||
#
|
||||
# Returns an object that responds to `call` and returns a Response.
|
||||
def app
|
||||
@app ||= begin
|
||||
lock!
|
||||
ensure_adapter!
|
||||
to_app
|
||||
end
|
||||
end
|
||||
|
||||
def to_app
|
||||
# last added handler is the deepest and thus closest to the inner app
|
||||
# adapter is always the last one
|
||||
@handlers.reverse.inject(@adapter.build) do |app, handler|
|
||||
handler.build(app)
|
||||
end
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.is_a?(self.class) &&
|
||||
@handlers == other.handlers &&
|
||||
@adapter == other.adapter
|
||||
end
|
||||
|
||||
# ENV Keys
|
||||
# :http_method - a symbolized request HTTP method (:get, :post)
|
||||
# :body - the request body that will eventually be converted to a string.
|
||||
# :url - URI instance for the current request.
|
||||
# :status - HTTP response status code
|
||||
# :request_headers - hash of HTTP Headers to be sent to the server
|
||||
# :response_headers - Hash of HTTP headers from the server
|
||||
# :parallel_manager - sent if the connection is in parallel mode
|
||||
# :request - Hash of options for configuring the request.
|
||||
# :timeout - open/read timeout Integer in seconds
|
||||
# :open_timeout - read timeout Integer in seconds
|
||||
# :proxy - Hash of proxy options
|
||||
# :uri - Proxy Server URI
|
||||
# :user - Proxy server username
|
||||
# :password - Proxy server password
|
||||
# :ssl - Hash of options for configuring SSL requests.
|
||||
def build_env(connection, request)
|
||||
exclusive_url = connection.build_exclusive_url(
|
||||
request.path, request.params,
|
||||
request.options.params_encoder
|
||||
)
|
||||
|
||||
Env.new(request.http_method, request.body, exclusive_url,
|
||||
request.options, request.headers, connection.ssl,
|
||||
connection.parallel_manager)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
LOCK_ERR = "can't modify middleware stack after making a request"
|
||||
MISSING_ADAPTER_ERROR = "An attempt to run a request with a Faraday::Connection without adapter has been made.\n" \
|
||||
"Please set Faraday.default_adapter or provide one when initializing the connection.\n" \
|
||||
'For more info, check https://lostisland.github.io/faraday/usage/.'
|
||||
|
||||
def raise_if_locked
|
||||
raise StackLocked, LOCK_ERR if locked?
|
||||
end
|
||||
|
||||
def raise_if_adapter(klass)
|
||||
return unless klass <= Faraday::Adapter
|
||||
|
||||
raise 'Adapter should be set using the `adapter` method, not `use`'
|
||||
end
|
||||
|
||||
def ensure_adapter!
|
||||
raise MISSING_ADAPTER_ERROR unless @adapter
|
||||
end
|
||||
|
||||
def adapter_set?
|
||||
!@adapter.nil?
|
||||
end
|
||||
|
||||
def use_symbol(mod, key, ...)
|
||||
use(mod.lookup_middleware(key), ...)
|
||||
end
|
||||
|
||||
def assert_index(index)
|
||||
idx = index.is_a?(Integer) ? index : @handlers.index(index)
|
||||
raise "No such handler: #{index.inspect}" unless idx
|
||||
|
||||
idx
|
||||
end
|
||||
end
|
||||
end
|
139
lib/faraday/request.rb
Normal file
@ -0,0 +1,139 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
# Used to setup URLs, params, headers, and the request body in a sane manner.
|
||||
#
|
||||
# @example
|
||||
# @connection.post do |req|
|
||||
# req.url 'http://localhost', 'a' => '1' # 'http://localhost?a=1'
|
||||
# req.headers['b'] = '2' # Header
|
||||
# req.params['c'] = '3' # GET Param
|
||||
# req['b'] = '2' # also Header
|
||||
# req.body = 'abc'
|
||||
# end
|
||||
#
|
||||
# @!attribute http_method
|
||||
# @return [Symbol] the HTTP method of the Request
|
||||
# @!attribute path
|
||||
# @return [URI, String] the path
|
||||
# @!attribute params
|
||||
# @return [Hash] query parameters
|
||||
# @!attribute headers
|
||||
# @return [Faraday::Utils::Headers] headers
|
||||
# @!attribute body
|
||||
# @return [String] body
|
||||
# @!attribute options
|
||||
# @return [RequestOptions] options
|
||||
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]
|
||||
# @return [Request]
|
||||
def self.create(request_method)
|
||||
new(request_method).tap do |request|
|
||||
yield(request) if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
remove_method :params=
|
||||
# Replace params, preserving the existing hash type.
|
||||
#
|
||||
# @param hash [Hash] new params
|
||||
def params=(hash)
|
||||
if params
|
||||
params.replace hash
|
||||
else
|
||||
member_set(:params, hash)
|
||||
end
|
||||
end
|
||||
|
||||
remove_method :headers=
|
||||
# Replace request headers, preserving the existing hash type.
|
||||
#
|
||||
# @param hash [Hash] new headers
|
||||
def headers=(hash)
|
||||
if headers
|
||||
headers.replace hash
|
||||
else
|
||||
member_set(:headers, hash)
|
||||
end
|
||||
end
|
||||
|
||||
# Update path and params.
|
||||
#
|
||||
# @param path [URI, String]
|
||||
# @param params [Hash, nil]
|
||||
# @return [void]
|
||||
def url(path, params = nil)
|
||||
if path.respond_to? :query
|
||||
if (query = path.query)
|
||||
path = path.dup
|
||||
path.query = nil
|
||||
end
|
||||
else
|
||||
anchor_index = path.index('#')
|
||||
path = path.slice(0, anchor_index) unless anchor_index.nil?
|
||||
path, query = path.split('?', 2)
|
||||
end
|
||||
self.path = path
|
||||
self.params.merge_query query, options.params_encoder
|
||||
self.params.update(params) if params
|
||||
end
|
||||
|
||||
# @param key [Object] key to look up in headers
|
||||
# @return [Object] value of the given header name
|
||||
def [](key)
|
||||
headers[key]
|
||||
end
|
||||
|
||||
# @param key [Object] key of header to write
|
||||
# @param value [Object] value of header
|
||||
def []=(key, value)
|
||||
headers[key] = value
|
||||
end
|
||||
|
||||
# Marshal serialization support.
|
||||
#
|
||||
# @return [Hash] the hash ready to be serialized in Marshal.
|
||||
def marshal_dump
|
||||
{
|
||||
http_method: http_method,
|
||||
body: body,
|
||||
headers: headers,
|
||||
path: path,
|
||||
params: params,
|
||||
options: options
|
||||
}
|
||||
end
|
||||
|
||||
# Marshal serialization support.
|
||||
# Restores the instance variables according to the +serialised+.
|
||||
# @param serialised [Hash] the serialised object.
|
||||
def marshal_load(serialised)
|
||||
self.http_method = serialised[:http_method]
|
||||
self.body = serialised[:body]
|
||||
self.headers = serialised[:headers]
|
||||
self.path = serialised[:path]
|
||||
self.params = serialised[:params]
|
||||
self.options = serialised[:options]
|
||||
end
|
||||
|
||||
# @return [Env] the Env for this Request
|
||||
def to_env(connection)
|
||||
Env.new(http_method, body, connection.build_exclusive_url(path, params),
|
||||
options, headers, connection.ssl, connection.parallel_manager)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'faraday/request/authorization'
|
||||
require 'faraday/request/instrumentation'
|
||||
require 'faraday/request/json'
|
||||
require 'faraday/request/url_encoded'
|
54
lib/faraday/request/authorization.rb
Normal file
@ -0,0 +1,54 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
class Request
|
||||
# Request middleware for the Authorization HTTP header
|
||||
class Authorization < Faraday::Middleware
|
||||
KEY = 'Authorization'
|
||||
|
||||
# @param app [#call]
|
||||
# @param type [String, Symbol] Type of Authorization
|
||||
# @param params [Array<String, Proc, #call>] parameters to build the Authorization header.
|
||||
# If the type is `:basic`, then these can be a login and password pair.
|
||||
# Otherwise, a single value is expected that will be appended after the type.
|
||||
# This value can be a proc or an object responding to `.call`, in which case
|
||||
# it will be invoked on each request.
|
||||
def initialize(app, type, *params)
|
||||
@type = type
|
||||
@params = params
|
||||
super(app)
|
||||
end
|
||||
|
||||
# @param env [Faraday::Env]
|
||||
def on_request(env)
|
||||
return if env.request_headers[KEY]
|
||||
|
||||
env.request_headers[KEY] = header_from(@type, env, *@params)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# @param type [String, Symbol]
|
||||
# @param env [Faraday::Env]
|
||||
# @param params [Array]
|
||||
# @return [String] a header value
|
||||
def header_from(type, env, *params)
|
||||
if type.to_s.casecmp('basic').zero? && params.size == 2
|
||||
Utils.basic_header_from(*params)
|
||||
elsif params.size != 1
|
||||
raise ArgumentError, "Unexpected params received (got #{params.size} instead of 1)"
|
||||
else
|
||||
value = params.first
|
||||
if (value.is_a?(Proc) && value.arity == 1) || (value.respond_to?(:call) && value.method(:call).arity == 1)
|
||||
value = value.call(env)
|
||||
elsif value.is_a?(Proc) || value.respond_to?(:call)
|
||||
value = value.call
|
||||
end
|
||||
"#{type} #{value}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Faraday::Request.register_middleware(authorization: Faraday::Request::Authorization)
|
58
lib/faraday/request/instrumentation.rb
Normal file
@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
class Request
|
||||
# Middleware for instrumenting Requests.
|
||||
class Instrumentation < Faraday::Middleware
|
||||
# Options class used in Request::Instrumentation class.
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
# Instruments requests using Active Support.
|
||||
#
|
||||
# Measures time spent only for synchronous requests.
|
||||
#
|
||||
# @example Using ActiveSupport::Notifications to measure time spent
|
||||
# for Faraday requests.
|
||||
# ActiveSupport::Notifications
|
||||
# .subscribe('request.faraday') do |name, starts, ends, _, env|
|
||||
# url = env[:url]
|
||||
# http_method = env[:method].to_s.upcase
|
||||
# duration = ends - starts
|
||||
# $stderr.puts '[%s] %s %s (%.3f s)' %
|
||||
# [url.host, http_method, url.request_uri, duration]
|
||||
# end
|
||||
# @param app [#call]
|
||||
# @param options [nil, Hash] Options hash
|
||||
# @option options [String] :name ('request.faraday')
|
||||
# Name of the instrumenter
|
||||
# @option options [Class] :instrumenter (ActiveSupport::Notifications)
|
||||
# Active Support instrumenter class.
|
||||
def initialize(app, options = nil)
|
||||
super(app)
|
||||
@name, @instrumenter = Options.from(options)
|
||||
.values_at(:name, :instrumenter)
|
||||
end
|
||||
|
||||
# @param env [Faraday::Env]
|
||||
def call(env)
|
||||
@instrumenter.instrument(@name, env) do
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Faraday::Request.register_middleware(instrumentation: Faraday::Request::Instrumentation)
|
70
lib/faraday/request/json.rb
Normal file
@ -0,0 +1,70 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'json'
|
||||
|
||||
module Faraday
|
||||
class Request
|
||||
# Request middleware that encodes the body as JSON.
|
||||
#
|
||||
# Processes only requests with matching Content-type or those without a type.
|
||||
# If a request doesn't have a type but has a body, it sets the Content-type
|
||||
# to JSON MIME-type.
|
||||
#
|
||||
# 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$}
|
||||
|
||||
def on_request(env)
|
||||
match_content_type(env) do |data|
|
||||
env[:body] = encode(data)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def encode(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)
|
||||
return unless process_request?(env)
|
||||
|
||||
env[:request_headers][CONTENT_TYPE] ||= MIME_TYPE
|
||||
yield env[:body] unless env[:body].respond_to?(:to_str)
|
||||
end
|
||||
|
||||
def process_request?(env)
|
||||
type = request_type(env)
|
||||
body?(env) && (type.empty? || type.match?(MIME_TYPE_REGEX))
|
||||
end
|
||||
|
||||
def body?(env)
|
||||
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)
|
||||
type = env[:request_headers][CONTENT_TYPE].to_s
|
||||
type = type.split(';', 2).first if type.index(';')
|
||||
type
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Faraday::Request.register_middleware(json: Faraday::Request::Json)
|
@ -1,30 +0,0 @@
|
||||
module Faraday
|
||||
module Request
|
||||
class PostRequest
|
||||
extend Loadable
|
||||
|
||||
def initialize params, headers={}
|
||||
@params = params
|
||||
@headers = headers
|
||||
end
|
||||
|
||||
def headers
|
||||
@headers.merge('Content-Type' => 'application/x-www-form-urlencoded')
|
||||
end
|
||||
|
||||
def body
|
||||
create_post_params @params
|
||||
end
|
||||
|
||||
private
|
||||
def create_post_params(params, base = "")
|
||||
[].tap do |toreturn|
|
||||
params.each_key do |key|
|
||||
keystring = base == '' ? key : "#{base}[#{key}]"
|
||||
toreturn << (params[key].kind_of?(Hash) ? create_post_params(params[key], keystring) : "#{keystring}=#{CGI.escape(params[key].to_s)}")
|
||||
end
|
||||
end.join('&')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
60
lib/faraday/request/url_encoded.rb
Normal file
@ -0,0 +1,60 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Faraday
|
||||
class Request
|
||||
# Middleware for supporting urlencoded requests.
|
||||
class UrlEncoded < Faraday::Middleware
|
||||
unless defined?(::Faraday::Request::UrlEncoded::CONTENT_TYPE)
|
||||
CONTENT_TYPE = 'Content-Type'
|
||||
end
|
||||
|
||||
class << self
|
||||
attr_accessor :mime_type
|
||||
end
|
||||
self.mime_type = 'application/x-www-form-urlencoded'
|
||||
|
||||
# Encodes as "application/x-www-form-urlencoded" if not already encoded or
|
||||
# of another type.
|
||||
#
|
||||
# @param env [Faraday::Env]
|
||||
def call(env)
|
||||
match_content_type(env) do |data|
|
||||
params = Faraday::Utils::ParamsHash[data]
|
||||
env.body = params.to_query(env.params_encoder)
|
||||
end
|
||||
@app.call env
|
||||
end
|
||||
|
||||
# @param env [Faraday::Env]
|
||||
# @yield [request_body] Body of the request
|
||||
def match_content_type(env)
|
||||
return unless process_request?(env)
|
||||
|
||||
env.request_headers[CONTENT_TYPE] ||= self.class.mime_type
|
||||
return if env.body.respond_to?(:to_str) || env.body.respond_to?(:read)
|
||||
|
||||
yield(env.body)
|
||||
end
|
||||
|
||||
# @param env [Faraday::Env]
|
||||
#
|
||||
# @return [Boolean] True if the request has a body and its Content-Type is
|
||||
# urlencoded.
|
||||
def process_request?(env)
|
||||
type = request_type(env)
|
||||
env.body && (type.empty? || (type == self.class.mime_type))
|
||||
end
|
||||
|
||||
# @param env [Faraday::Env]
|
||||
#
|
||||
# @return [String]
|
||||
def request_type(env)
|
||||
type = env.request_headers[CONTENT_TYPE].to_s
|
||||
type = type.split(';', 2).first if type.index(';')
|
||||
type
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Faraday::Request.register_middleware(url_encoded: Faraday::Request::UrlEncoded)
|
@ -1,27 +0,0 @@
|
||||
module Faraday
|
||||
module Request
|
||||
class YajlRequest
|
||||
extend Loadable
|
||||
|
||||
begin
|
||||
require 'yajl'
|
||||
|
||||
def initialize params, headers={}
|
||||
@params = params
|
||||
@headers = headers
|
||||
end
|
||||
|
||||
def headers
|
||||
@headers.merge('Content-Type' => 'application/json')
|
||||
end
|
||||
|
||||
# TODO streaming
|
||||
def body
|
||||
Yajl::Encoder.encode @params
|
||||
end
|
||||
rescue LoadError => e
|
||||
self.load_error = e
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|