fetch-api 0.3.5 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +13 -0
- data/lib/fetch/client.rb +6 -4
- data/lib/fetch/config.rb +3 -0
- data/lib/fetch/connection_pool.rb +86 -0
- data/lib/fetch/form_data.rb +7 -5
- data/lib/fetch/headers.rb +6 -4
- data/lib/fetch/url_search_params.rb +5 -4
- data/lib/fetch/version.rb +1 -1
- data/lib/fetch.rb +16 -0
- data/sig/fetch/api.rbs +2 -2
- data/sig/fetch/client.rbs +4 -3
- data/sig/fetch/config.rbs +6 -0
- data/sig/fetch/connection_pool.rbs +21 -0
- data/sig/fetch/form_data.rbs +14 -12
- data/sig/fetch/headers.rbs +10 -6
- data/sig/fetch/response.rbs +1 -3
- data/sig/fetch/url_search_params.rbs +12 -8
- data/sig/fetch.rbs +4 -4
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dbef9662b23db6e0efca2b67264d6450553991b551ec94f1b7099c0b4c2eda55
|
4
|
+
data.tar.gz: 0456d3ff62bcceeaa780ff9cb42c3ff231d667b977ef968e22bb5b2a5578e358
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 516ebe663dc6f346a8acc56f7a1c8e1a3bc11f3e133f6ca0c10a8544516a6dc8a8e34efd660585de627c595ba66ed17b6df3f3415ec90a439be84e21e50d7343
|
7
|
+
data.tar.gz: 3b0630efe83154f9a71df06aec28b551720a6041e24ce1797626a105eedacc54a96495362e061b391018847cbfd2021e4fd750854b75a546841738f2a58d441c
|
data/README.md
CHANGED
@@ -102,6 +102,19 @@ res = fetch('http://example.com', **{
|
|
102
102
|
})
|
103
103
|
```
|
104
104
|
|
105
|
+
## Connection pooling
|
106
|
+
|
107
|
+
Fetch API automatically pools and reuses connections to the same origin. Connections are closed after a certain amount of time has passed since the last use, and a new connection is used the next time. Different connections are returned for different threads (in other words, Fetch API is thread-safe).
|
108
|
+
|
109
|
+
These values (in seconds) can be configured as follows:
|
110
|
+
|
111
|
+
``` ruby
|
112
|
+
Fetch.configure do |config|
|
113
|
+
config.connection_max_idle_time = 10 # default
|
114
|
+
config.keep_alive_timeout = 2 # default
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
105
118
|
## Development
|
106
119
|
|
107
120
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/fetch/client.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require_relative 'connection_pool'
|
1
2
|
require_relative 'errors'
|
2
3
|
require_relative 'form_data'
|
3
4
|
require_relative 'headers'
|
@@ -14,6 +15,10 @@ module Fetch
|
|
14
15
|
class Client
|
15
16
|
include Singleton
|
16
17
|
|
18
|
+
def initialize
|
19
|
+
@pool = ConnectionPool.new
|
20
|
+
end
|
21
|
+
|
17
22
|
def fetch(resource, method: :get, headers: [], body: nil, redirect: :follow, _redirected: false)
|
18
23
|
uri = URI.parse(resource)
|
19
24
|
req = Net::HTTP.const_get(method.capitalize).new(uri)
|
@@ -40,10 +45,7 @@ module Fetch
|
|
40
45
|
req.body = body
|
41
46
|
end
|
42
47
|
|
43
|
-
|
44
|
-
http.use_ssl = uri.scheme == 'https'
|
45
|
-
|
46
|
-
res = http.start { _1.request(req) }
|
48
|
+
res = @pool.with_connection(uri) { _1.request(req) }
|
47
49
|
|
48
50
|
case res
|
49
51
|
when Net::HTTPRedirection
|
data/lib/fetch/config.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative '../fetch'
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
module Fetch
|
6
|
+
class ConnectionPool
|
7
|
+
Entry = Struct.new(:connection, :in_use, :last_used, keyword_init: true)
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@connections = {}
|
11
|
+
@mutex = Mutex.new
|
12
|
+
|
13
|
+
@sweeper = Thread.new {
|
14
|
+
loop do
|
15
|
+
sleep 1
|
16
|
+
|
17
|
+
sweep
|
18
|
+
|
19
|
+
Thread.stop if @connections.empty?
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_connection(uri, &block)
|
25
|
+
conn = acquire(uri)
|
26
|
+
|
27
|
+
begin
|
28
|
+
block.call(conn)
|
29
|
+
ensure
|
30
|
+
release uri
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def acquire(uri)
|
37
|
+
@mutex.synchronize {
|
38
|
+
entry = @connections[key(uri)]
|
39
|
+
|
40
|
+
if entry
|
41
|
+
entry.in_use = true
|
42
|
+
|
43
|
+
entry.connection
|
44
|
+
else
|
45
|
+
Net::HTTP.new(uri.host, uri.port).tap {|http| # steep:ignore ArgumentTypeMismatch
|
46
|
+
http.use_ssl = uri.scheme == 'https'
|
47
|
+
http.keep_alive_timeout = Fetch.config.keep_alive_timeout
|
48
|
+
|
49
|
+
@connections[key(uri)] = Entry.new(connection: http, in_use: true)
|
50
|
+
|
51
|
+
http.start
|
52
|
+
}
|
53
|
+
end
|
54
|
+
}.tap {
|
55
|
+
@sweeper.wakeup
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def release(uri)
|
60
|
+
@mutex.synchronize do
|
61
|
+
if entry = @connections[key(uri)]
|
62
|
+
entry.in_use = false
|
63
|
+
entry.last_used = Time.now
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def sweep
|
69
|
+
@mutex.synchronize do
|
70
|
+
@connections.each do |key, entry|
|
71
|
+
next if entry.in_use
|
72
|
+
|
73
|
+
if entry.last_used + Fetch.config.connection_max_idle_time < Time.now
|
74
|
+
entry.connection.finish
|
75
|
+
|
76
|
+
@connections.delete key
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def key(uri)
|
83
|
+
"#{Thread.current.object_id}/#{uri.origin}".freeze
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/fetch/form_data.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Fetch
|
2
4
|
class FormData
|
3
5
|
include Enumerable
|
4
6
|
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :entries, :each
|
10
|
+
|
5
11
|
def self.build(enumerable)
|
6
12
|
data = FormData.new
|
7
13
|
|
@@ -19,7 +25,7 @@ module Fetch
|
|
19
25
|
attr_reader :entries
|
20
26
|
|
21
27
|
def append(key, value)
|
22
|
-
@entries.push [key.to_s,
|
28
|
+
@entries.push [key.to_s, File === value ? value : value.to_s]
|
23
29
|
end
|
24
30
|
|
25
31
|
def delete(key)
|
@@ -50,9 +56,5 @@ module Fetch
|
|
50
56
|
def values
|
51
57
|
@entries.map(&:last)
|
52
58
|
end
|
53
|
-
|
54
|
-
def each(&)
|
55
|
-
@entries.each(&)
|
56
|
-
end
|
57
59
|
end
|
58
60
|
end
|
data/lib/fetch/headers.rb
CHANGED
@@ -1,7 +1,13 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Fetch
|
2
4
|
class Headers
|
3
5
|
include Enumerable
|
4
6
|
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :entries, :each
|
10
|
+
|
5
11
|
def initialize(init = [])
|
6
12
|
@data = {}
|
7
13
|
|
@@ -41,9 +47,5 @@ module Fetch
|
|
41
47
|
def values
|
42
48
|
@data.values.map { _1.join(', ') }
|
43
49
|
end
|
44
|
-
|
45
|
-
def each(&)
|
46
|
-
entries.each(&)
|
47
|
-
end
|
48
50
|
end
|
49
51
|
end
|
@@ -1,9 +1,14 @@
|
|
1
|
+
require 'forwardable'
|
1
2
|
require 'uri'
|
2
3
|
|
3
4
|
module Fetch
|
4
5
|
class URLSearchParams
|
5
6
|
include Enumerable
|
6
7
|
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :entries, :each
|
11
|
+
|
7
12
|
def initialize(options = [])
|
8
13
|
@entries = []
|
9
14
|
|
@@ -61,9 +66,5 @@ module Fetch
|
|
61
66
|
def values
|
62
67
|
@entries.map(&:last)
|
63
68
|
end
|
64
|
-
|
65
|
-
def each(&)
|
66
|
-
@entries.each(&)
|
67
|
-
end
|
68
69
|
end
|
69
70
|
end
|
data/lib/fetch/version.rb
CHANGED
data/lib/fetch.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative 'fetch/config'
|
2
|
+
|
3
|
+
module Fetch
|
4
|
+
singleton_class.attr_accessor :config
|
5
|
+
|
6
|
+
self.config = Config.new
|
7
|
+
|
8
|
+
def self.configure(&block)
|
9
|
+
block.call(config)
|
10
|
+
end
|
11
|
+
|
12
|
+
configure do |config|
|
13
|
+
config.connection_max_idle_time = 10
|
14
|
+
config.keep_alive_timeout = 2
|
15
|
+
end
|
16
|
+
end
|
data/sig/fetch/api.rbs
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Fetch
|
2
2
|
class API
|
3
3
|
def self?.fetch: (
|
4
|
-
|
4
|
+
string,
|
5
5
|
?method: String | Symbol,
|
6
|
-
?headers:
|
6
|
+
?headers: _Each[[_ToS, _ToS]],
|
7
7
|
?body: (String | FormData | URLSearchParams)?,
|
8
8
|
?redirect: 'follow' | 'error' | 'manual' | :follow | :error | :manual,
|
9
9
|
) -> Response
|
data/sig/fetch/client.rbs
CHANGED
@@ -3,9 +3,9 @@ module Fetch
|
|
3
3
|
include Singleton
|
4
4
|
|
5
5
|
def fetch: (
|
6
|
-
|
6
|
+
string,
|
7
7
|
?method: String | Symbol,
|
8
|
-
?headers:
|
8
|
+
?headers: _Each[[_ToS, _ToS]],
|
9
9
|
?body: (String | FormData | URLSearchParams)?,
|
10
10
|
?redirect: 'follow' | 'error' | 'manual' | :follow | :error | :manual,
|
11
11
|
?_redirected: bool
|
@@ -13,6 +13,7 @@ module Fetch
|
|
13
13
|
|
14
14
|
private
|
15
15
|
|
16
|
-
def
|
16
|
+
def initialize: () -> void
|
17
|
+
def to_response: (string, Net::HTTPResponse, bool) -> Response
|
17
18
|
end
|
18
19
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Fetch
|
2
|
+
class ConnectionPool
|
3
|
+
class Entry
|
4
|
+
attr_accessor connection: Net::HTTP
|
5
|
+
attr_accessor in_use: bool
|
6
|
+
attr_accessor last_used: Time
|
7
|
+
|
8
|
+
def initialize: (connection: Net::HTTP, in_use: bool, ?last_used: Time) -> void
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize: () -> void
|
12
|
+
def with_connection: [T] (URI::HTTP) { (Net::HTTP) -> T } -> T
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def acquire: (URI::HTTP) -> Net::HTTP
|
17
|
+
def release: (URI::HTTP) -> void
|
18
|
+
def sweep: () -> void
|
19
|
+
def key: (URI::HTTP) -> String
|
20
|
+
end
|
21
|
+
end
|
data/sig/fetch/form_data.rbs
CHANGED
@@ -1,22 +1,24 @@
|
|
1
1
|
module Fetch
|
2
2
|
class FormData
|
3
|
-
|
3
|
+
include Enumerable[[String, String | File]]
|
4
4
|
|
5
|
-
|
5
|
+
extend Forwardable
|
6
6
|
|
7
|
-
attr_reader entries: Array[[String,
|
7
|
+
attr_reader entries: Array[[String, String | File]]
|
8
8
|
|
9
|
-
def self.build: (
|
9
|
+
def self.build: (_Each[[_ToS, _ToS | File]]) -> FormData
|
10
10
|
|
11
11
|
def initialize: () -> void
|
12
|
-
def append: (
|
13
|
-
def delete: (
|
14
|
-
def get: (
|
15
|
-
def get_all: (
|
16
|
-
def has: (
|
12
|
+
def append: (_ToS, _ToS | File) -> void
|
13
|
+
def delete: (_ToS) -> void
|
14
|
+
def get: (_ToS) -> (String | File)?
|
15
|
+
def get_all: (_ToS) -> Array[String | File]
|
16
|
+
def has: (_ToS) -> bool
|
17
17
|
def keys: -> Array[String]
|
18
|
-
def set: (
|
19
|
-
def values: () -> Array[
|
20
|
-
|
18
|
+
def set: (_ToS, _ToS | File) -> void
|
19
|
+
def values: () -> Array[String | File]
|
20
|
+
|
21
|
+
def each: () { ([String, String | File]) -> void } -> Array[[String, String | File]]
|
22
|
+
| () -> Enumerator[[String, String | File], Array[[String, String | File]]]
|
21
23
|
end
|
22
24
|
end
|
data/sig/fetch/headers.rbs
CHANGED
@@ -2,15 +2,19 @@ module Fetch
|
|
2
2
|
class Headers
|
3
3
|
include Enumerable[[String, String]]
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
def
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
def initialize: (_Each[[_ToS, _ToS]]) -> void
|
8
|
+
def append: (_ToS, _ToS) -> void
|
9
|
+
def delete: (_ToS) -> void
|
8
10
|
def entries: () -> Array[[String, String]]
|
9
|
-
def get: (
|
10
|
-
def has: (
|
11
|
+
def get: (_ToS) -> String?
|
12
|
+
def has: (_ToS) -> bool
|
11
13
|
def keys: -> Array[String]
|
12
|
-
def set: (
|
14
|
+
def set: (_ToS, _ToS) -> void
|
13
15
|
def values: () -> Array[String]
|
16
|
+
|
14
17
|
def each: () { ([String, String]) -> void } -> Array[[String, String]]
|
18
|
+
| () -> Enumerator[[String, String], Array[[String, String]]]
|
15
19
|
end
|
16
20
|
end
|
data/sig/fetch/response.rbs
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
module Fetch
|
2
2
|
class Response
|
3
|
-
include Data::_DataClass
|
4
|
-
|
5
3
|
attr_reader url: String
|
6
4
|
attr_reader status: Integer
|
7
5
|
attr_reader headers: Headers
|
@@ -11,6 +9,6 @@ module Fetch
|
|
11
9
|
def initialize: (url: String, status: Integer, headers: Headers, body: String?, redirected: bool) -> void
|
12
10
|
def ok: () -> bool
|
13
11
|
def status_text: () -> String
|
14
|
-
def json: (json_options) ->
|
12
|
+
def json: (json_options) -> untyped
|
15
13
|
end
|
16
14
|
end
|
@@ -2,19 +2,23 @@ module Fetch
|
|
2
2
|
class URLSearchParams
|
3
3
|
include Enumerable[[String, String]]
|
4
4
|
|
5
|
+
extend Forwardable
|
6
|
+
|
5
7
|
attr_reader entries: Array[[String, String]]
|
6
8
|
|
7
|
-
def self.build: (
|
9
|
+
def self.build: (_Each[[_ToS, _ToS]]) -> FormData
|
8
10
|
|
9
|
-
def initialize: (String |
|
10
|
-
def append: (
|
11
|
-
def delete: (
|
12
|
-
def get: (
|
13
|
-
def get_all: (
|
14
|
-
def has: (
|
11
|
+
def initialize: (String | _Each[[_ToS, _ToS]]) -> void
|
12
|
+
def append: (_ToS, _ToS) -> void
|
13
|
+
def delete: (_ToS) -> void
|
14
|
+
def get: (_ToS) -> String?
|
15
|
+
def get_all: (_ToS) -> Array[String]
|
16
|
+
def has: (_ToS) -> bool
|
15
17
|
def keys: -> Array[String]
|
16
|
-
def set: (
|
18
|
+
def set: (_ToS, _ToS) -> void
|
17
19
|
def values: () -> Array[String]
|
20
|
+
|
18
21
|
def each: () { ([String, String]) -> void } -> Array[[String, String]]
|
22
|
+
| () -> Enumerator[[String, String], Array[[String, String]]]
|
19
23
|
end
|
20
24
|
end
|
data/sig/fetch.rbs
CHANGED
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fetch-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Keita Urashima
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-07-
|
11
|
+
date: 2024-07-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: forwardable
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: json
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -104,8 +118,11 @@ files:
|
|
104
118
|
- LICENSE.txt
|
105
119
|
- README.md
|
106
120
|
- lib/fetch-api.rb
|
121
|
+
- lib/fetch.rb
|
107
122
|
- lib/fetch/api.rb
|
108
123
|
- lib/fetch/client.rb
|
124
|
+
- lib/fetch/config.rb
|
125
|
+
- lib/fetch/connection_pool.rb
|
109
126
|
- lib/fetch/errors.rb
|
110
127
|
- lib/fetch/form_data.rb
|
111
128
|
- lib/fetch/headers.rb
|
@@ -115,6 +132,8 @@ files:
|
|
115
132
|
- sig/fetch.rbs
|
116
133
|
- sig/fetch/api.rbs
|
117
134
|
- sig/fetch/client.rbs
|
135
|
+
- sig/fetch/config.rbs
|
136
|
+
- sig/fetch/connection_pool.rbs
|
118
137
|
- sig/fetch/errors.rbs
|
119
138
|
- sig/fetch/form_data.rbs
|
120
139
|
- sig/fetch/headers.rbs
|