httpx 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +191 -0
- data/README.md +119 -0
- data/lib/httpx.rb +50 -0
- data/lib/httpx/buffer.rb +34 -0
- data/lib/httpx/callbacks.rb +32 -0
- data/lib/httpx/chainable.rb +51 -0
- data/lib/httpx/channel.rb +222 -0
- data/lib/httpx/channel/http1.rb +220 -0
- data/lib/httpx/channel/http2.rb +224 -0
- data/lib/httpx/client.rb +173 -0
- data/lib/httpx/connection.rb +74 -0
- data/lib/httpx/errors.rb +7 -0
- data/lib/httpx/extensions.rb +52 -0
- data/lib/httpx/headers.rb +152 -0
- data/lib/httpx/io.rb +240 -0
- data/lib/httpx/loggable.rb +11 -0
- data/lib/httpx/options.rb +138 -0
- data/lib/httpx/plugins/authentication.rb +14 -0
- data/lib/httpx/plugins/basic_authentication.rb +20 -0
- data/lib/httpx/plugins/compression.rb +123 -0
- data/lib/httpx/plugins/compression/brotli.rb +55 -0
- data/lib/httpx/plugins/compression/deflate.rb +50 -0
- data/lib/httpx/plugins/compression/gzip.rb +59 -0
- data/lib/httpx/plugins/cookies.rb +63 -0
- data/lib/httpx/plugins/digest_authentication.rb +141 -0
- data/lib/httpx/plugins/follow_redirects.rb +72 -0
- data/lib/httpx/plugins/h2c.rb +85 -0
- data/lib/httpx/plugins/proxy.rb +108 -0
- data/lib/httpx/plugins/proxy/http.rb +115 -0
- data/lib/httpx/plugins/proxy/socks4.rb +110 -0
- data/lib/httpx/plugins/proxy/socks5.rb +152 -0
- data/lib/httpx/plugins/push_promise.rb +67 -0
- data/lib/httpx/plugins/stream.rb +33 -0
- data/lib/httpx/registry.rb +88 -0
- data/lib/httpx/request.rb +222 -0
- data/lib/httpx/response.rb +225 -0
- data/lib/httpx/selector.rb +155 -0
- data/lib/httpx/timeout.rb +68 -0
- data/lib/httpx/transcoder.rb +12 -0
- data/lib/httpx/transcoder/body.rb +56 -0
- data/lib/httpx/transcoder/chunker.rb +38 -0
- data/lib/httpx/transcoder/form.rb +41 -0
- data/lib/httpx/transcoder/json.rb +36 -0
- data/lib/httpx/version.rb +5 -0
- metadata +150 -0
@@ -0,0 +1,155 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class HTTPX::Selector
|
4
|
+
#
|
5
|
+
# I/O monitor
|
6
|
+
#
|
7
|
+
class Monitor
|
8
|
+
attr_accessor :value, :interests, :readiness
|
9
|
+
|
10
|
+
def initialize(io, interests, reactor)
|
11
|
+
@io = io
|
12
|
+
@interests = interests
|
13
|
+
@reactor = reactor
|
14
|
+
@closed = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def readable?
|
18
|
+
@interests == :rw || @interests == :r
|
19
|
+
end
|
20
|
+
|
21
|
+
def writable?
|
22
|
+
@interests == :rw || @interests == :w
|
23
|
+
end
|
24
|
+
|
25
|
+
# closes +@io+, deregisters from reactor (unless +deregister+ is false)
|
26
|
+
def close(deregister = true)
|
27
|
+
return if @closed
|
28
|
+
@closed = true
|
29
|
+
@reactor.deregister(@io) if deregister
|
30
|
+
end
|
31
|
+
|
32
|
+
def closed?
|
33
|
+
@closed
|
34
|
+
end
|
35
|
+
|
36
|
+
# :nocov:
|
37
|
+
def to_s
|
38
|
+
"#<#{self.class}: #{@io}(closed:#{@closed}) #{@interests} #{object_id.to_s(16)}>"
|
39
|
+
end
|
40
|
+
# :nocov:
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@readers = {}
|
45
|
+
@writers = {}
|
46
|
+
@lock = Mutex.new
|
47
|
+
@__r__, @__w__ = IO.pipe
|
48
|
+
@closed = false
|
49
|
+
end
|
50
|
+
|
51
|
+
def empty?
|
52
|
+
@readers.empty? && @writers.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
# deregisters +io+ from selectables.
|
56
|
+
def deregister(io)
|
57
|
+
@lock.synchronize do
|
58
|
+
rmonitor = @readers.delete(io)
|
59
|
+
wmonitor = @writers.delete(io)
|
60
|
+
monitor = rmonitor || wmonitor
|
61
|
+
monitor.close(false) if monitor
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# register +io+ for +interests+ events.
|
66
|
+
def register(io, interests)
|
67
|
+
readable = interests == :r || interests == :rw
|
68
|
+
writable = interests == :w || interests == :rw
|
69
|
+
@lock.synchronize do
|
70
|
+
if readable
|
71
|
+
monitor = @readers[io]
|
72
|
+
if monitor
|
73
|
+
monitor.interests = interests
|
74
|
+
else
|
75
|
+
monitor = Monitor.new(io, interests, self)
|
76
|
+
end
|
77
|
+
@readers[io] = monitor
|
78
|
+
@writers.delete(io) unless writable
|
79
|
+
end
|
80
|
+
if writable
|
81
|
+
monitor = @writers[io]
|
82
|
+
if monitor
|
83
|
+
monitor.interests = interests
|
84
|
+
else
|
85
|
+
# reuse object
|
86
|
+
monitor = readable ? @readers[io] : Monitor.new(io, interests, self)
|
87
|
+
end
|
88
|
+
@writers[io] = monitor
|
89
|
+
@readers.delete(io) unless readable
|
90
|
+
end
|
91
|
+
monitor
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# waits for read/write events for +interval+. Yields for monitors of
|
96
|
+
# selected IO objects.
|
97
|
+
#
|
98
|
+
def select(interval)
|
99
|
+
begin
|
100
|
+
r = nil
|
101
|
+
w = nil
|
102
|
+
@lock.synchronize do
|
103
|
+
r = @readers.keys
|
104
|
+
w = @writers.keys
|
105
|
+
end
|
106
|
+
r.unshift(@__r__)
|
107
|
+
|
108
|
+
readers, writers = IO.select(r, w, nil, interval)
|
109
|
+
|
110
|
+
raise HTTPX::TimeoutError, "timed out while waiting on select" if readers.nil? && writers.nil?
|
111
|
+
rescue IOError, SystemCallError
|
112
|
+
@lock.synchronize do
|
113
|
+
@readers.reject! { |io, _| io.closed? }
|
114
|
+
@writers.reject! { |io, _| io.closed? }
|
115
|
+
end
|
116
|
+
retry
|
117
|
+
end
|
118
|
+
|
119
|
+
readers.each do |io|
|
120
|
+
if io == @__r__
|
121
|
+
# clean up wakeups
|
122
|
+
@__r__.read(@__r__.stat.size)
|
123
|
+
else
|
124
|
+
monitor = io.closed? ? @readers.delete(io) : @readers[io]
|
125
|
+
next unless monitor
|
126
|
+
monitor.readiness = writers.delete(io) ? :rw : :r
|
127
|
+
yield monitor
|
128
|
+
end
|
129
|
+
end if readers
|
130
|
+
|
131
|
+
writers.each do |io|
|
132
|
+
monitor = io.closed? ? @writers.delete(io) : @writers[io]
|
133
|
+
next unless monitor
|
134
|
+
# don't double run this, the last iteration might have run this task already
|
135
|
+
monitor.readiness = :w
|
136
|
+
yield monitor
|
137
|
+
end if writers
|
138
|
+
end
|
139
|
+
|
140
|
+
# Closes the selector.
|
141
|
+
#
|
142
|
+
def close
|
143
|
+
return if @closed
|
144
|
+
@__r__.close
|
145
|
+
@__w__.close
|
146
|
+
rescue IOError
|
147
|
+
ensure
|
148
|
+
@closed = true
|
149
|
+
end
|
150
|
+
|
151
|
+
# interrupts the select call.
|
152
|
+
def wakeup
|
153
|
+
@__w__.write_nonblock("\0", exception: false)
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "timeout"
|
4
|
+
|
5
|
+
module HTTPX
|
6
|
+
class Timeout
|
7
|
+
LOOP_TIMEOUT = 5
|
8
|
+
|
9
|
+
def self.new(opts = {})
|
10
|
+
return opts if opts.is_a?(Timeout)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(loop_timeout: 5, total_timeout: nil)
|
15
|
+
@loop_timeout = loop_timeout
|
16
|
+
@total_timeout = total_timeout
|
17
|
+
reset_counter
|
18
|
+
end
|
19
|
+
|
20
|
+
def timeout
|
21
|
+
@loop_timeout || @total_timeout
|
22
|
+
ensure
|
23
|
+
log_time
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(other)
|
27
|
+
if other.is_a?(Timeout)
|
28
|
+
@loop_timeout == other.instance_variable_get(:@loop_timeout) &&
|
29
|
+
@total_timeout == other.instance_variable_get(:@total_timeout)
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def merge(other)
|
36
|
+
case other
|
37
|
+
when Hash
|
38
|
+
timeout = Timeout.new(other)
|
39
|
+
merge(timeout)
|
40
|
+
when Timeout
|
41
|
+
loop_timeout = other.instance_variable_get(:@loop_timeout) || @loop_timeout
|
42
|
+
total_timeout = other.instance_variable_get(:@total_timeout) || @total_timeout
|
43
|
+
Timeout.new(loop_timeout: loop_timeout, total_timeout: total_timeout)
|
44
|
+
else
|
45
|
+
raise ArgumentError, "can't merge with #{other.class}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def reset_counter
|
52
|
+
@time_left = @total_timeout
|
53
|
+
end
|
54
|
+
|
55
|
+
def reset_timer
|
56
|
+
@started = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
57
|
+
end
|
58
|
+
|
59
|
+
def log_time
|
60
|
+
return unless @time_left
|
61
|
+
return reset_timer unless @started
|
62
|
+
@time_left -= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - @started)
|
63
|
+
raise TimeoutError, "Timed out after #{@total_timeout} seconds" if @time_left <= 0
|
64
|
+
|
65
|
+
reset_timer
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module HTTPX::Transcoder
|
6
|
+
module Body
|
7
|
+
module_function
|
8
|
+
|
9
|
+
class Encoder
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
def_delegator :@raw, :to_s
|
13
|
+
|
14
|
+
def initialize(body)
|
15
|
+
@raw = body
|
16
|
+
end
|
17
|
+
|
18
|
+
def bytesize
|
19
|
+
if @raw.respond_to?(:bytesize)
|
20
|
+
@raw.bytesize
|
21
|
+
elsif @raw.respond_to?(:to_ary)
|
22
|
+
@raw.map(&:bytesize).reduce(0, :+)
|
23
|
+
elsif @raw.respond_to?(:size)
|
24
|
+
@raw.size || Float::INFINITY
|
25
|
+
elsif @raw.respond_to?(:each)
|
26
|
+
Float::INFINITY
|
27
|
+
else
|
28
|
+
raise Error, "cannot determine size of body: #{@raw.inspect}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def content_type
|
33
|
+
"application/octet-stream"
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def respond_to_missing?(meth, *args)
|
39
|
+
@raw.respond_to?(meth, *args) || super
|
40
|
+
end
|
41
|
+
|
42
|
+
def method_missing(meth, *args, &block)
|
43
|
+
if @raw.respond_to?(meth)
|
44
|
+
@raw.__send__(meth, *args, &block)
|
45
|
+
else
|
46
|
+
super
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def encode(body)
|
52
|
+
Encoder.new(body)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
register "body", Body
|
56
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module HTTPX::Transcoder
|
6
|
+
module Chunker
|
7
|
+
module_function
|
8
|
+
|
9
|
+
class Encoder
|
10
|
+
extend Forwardable
|
11
|
+
|
12
|
+
def_delegator :@raw, :readpartial
|
13
|
+
|
14
|
+
CRLF = "\r\n"
|
15
|
+
|
16
|
+
def initialize(body)
|
17
|
+
@raw = body
|
18
|
+
end
|
19
|
+
|
20
|
+
def each
|
21
|
+
return enum_for(__method__) unless block_given?
|
22
|
+
@raw.each do |chunk|
|
23
|
+
yield "#{chunk.bytesize.to_s(16)}#{CRLF}#{chunk}#{CRLF}"
|
24
|
+
end
|
25
|
+
yield "0#{CRLF}#{CRLF}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def respond_to_missing?(meth, *args)
|
29
|
+
@raw.respond_to?(meth, *args) || super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def encode(chunks)
|
34
|
+
Encoder.new(chunks)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
register "chunker", Chunker
|
38
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
require "http/form_data"
|
5
|
+
|
6
|
+
module HTTPX::Transcoder
|
7
|
+
module Form
|
8
|
+
module_function
|
9
|
+
|
10
|
+
class Encoder
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
def_delegator :@raw, :content_type
|
14
|
+
|
15
|
+
def_delegator :@raw, :to_s
|
16
|
+
|
17
|
+
def_delegator :@raw, :read
|
18
|
+
|
19
|
+
def initialize(form)
|
20
|
+
@raw = HTTP::FormData.create(form)
|
21
|
+
end
|
22
|
+
|
23
|
+
def bytesize
|
24
|
+
@raw.content_length
|
25
|
+
end
|
26
|
+
|
27
|
+
def force_encoding(*args)
|
28
|
+
@raw.to_s.force_encoding(*args)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_str
|
32
|
+
@raw.to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def encode(form)
|
37
|
+
Encoder.new(form)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
register "form", Form
|
41
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module HTTPX::Transcoder
|
7
|
+
module JSON
|
8
|
+
module_function
|
9
|
+
|
10
|
+
class Encoder
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
def_delegator :@raw, :to_str
|
14
|
+
|
15
|
+
def_delegator :@raw, :to_s
|
16
|
+
|
17
|
+
def_delegator :@raw, :bytesize
|
18
|
+
|
19
|
+
def_delegator :@raw, :force_encoding
|
20
|
+
|
21
|
+
def initialize(json)
|
22
|
+
@raw = ::JSON.dump(json)
|
23
|
+
@charset = @raw.encoding.name.downcase
|
24
|
+
end
|
25
|
+
|
26
|
+
def content_type
|
27
|
+
"application/json; charset=#{@charset}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def encode(json)
|
32
|
+
Encoder.new(json)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
register "json", JSON
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: httpx
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tiago Cardoso
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-02-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: http-2
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.9.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.9.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: http-form_data
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.0.0
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '3'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 2.0.0
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: http_parser.rb
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.6.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.6.0
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: http-cookie
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '1.0'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '1.0'
|
75
|
+
description: A client library for making HTTP requests from Ruby.
|
76
|
+
email:
|
77
|
+
- cardoso_tiago@hotmail.com
|
78
|
+
executables: []
|
79
|
+
extensions: []
|
80
|
+
extra_rdoc_files: []
|
81
|
+
files:
|
82
|
+
- LICENSE.txt
|
83
|
+
- README.md
|
84
|
+
- lib/httpx.rb
|
85
|
+
- lib/httpx/buffer.rb
|
86
|
+
- lib/httpx/callbacks.rb
|
87
|
+
- lib/httpx/chainable.rb
|
88
|
+
- lib/httpx/channel.rb
|
89
|
+
- lib/httpx/channel/http1.rb
|
90
|
+
- lib/httpx/channel/http2.rb
|
91
|
+
- lib/httpx/client.rb
|
92
|
+
- lib/httpx/connection.rb
|
93
|
+
- lib/httpx/errors.rb
|
94
|
+
- lib/httpx/extensions.rb
|
95
|
+
- lib/httpx/headers.rb
|
96
|
+
- lib/httpx/io.rb
|
97
|
+
- lib/httpx/loggable.rb
|
98
|
+
- lib/httpx/options.rb
|
99
|
+
- lib/httpx/plugins/authentication.rb
|
100
|
+
- lib/httpx/plugins/basic_authentication.rb
|
101
|
+
- lib/httpx/plugins/compression.rb
|
102
|
+
- lib/httpx/plugins/compression/brotli.rb
|
103
|
+
- lib/httpx/plugins/compression/deflate.rb
|
104
|
+
- lib/httpx/plugins/compression/gzip.rb
|
105
|
+
- lib/httpx/plugins/cookies.rb
|
106
|
+
- lib/httpx/plugins/digest_authentication.rb
|
107
|
+
- lib/httpx/plugins/follow_redirects.rb
|
108
|
+
- lib/httpx/plugins/h2c.rb
|
109
|
+
- lib/httpx/plugins/proxy.rb
|
110
|
+
- lib/httpx/plugins/proxy/http.rb
|
111
|
+
- lib/httpx/plugins/proxy/socks4.rb
|
112
|
+
- lib/httpx/plugins/proxy/socks5.rb
|
113
|
+
- lib/httpx/plugins/push_promise.rb
|
114
|
+
- lib/httpx/plugins/stream.rb
|
115
|
+
- lib/httpx/registry.rb
|
116
|
+
- lib/httpx/request.rb
|
117
|
+
- lib/httpx/response.rb
|
118
|
+
- lib/httpx/selector.rb
|
119
|
+
- lib/httpx/timeout.rb
|
120
|
+
- lib/httpx/transcoder.rb
|
121
|
+
- lib/httpx/transcoder/body.rb
|
122
|
+
- lib/httpx/transcoder/chunker.rb
|
123
|
+
- lib/httpx/transcoder/form.rb
|
124
|
+
- lib/httpx/transcoder/json.rb
|
125
|
+
- lib/httpx/version.rb
|
126
|
+
homepage: https://gitlab.com/honeyryderchuck/httpx
|
127
|
+
licenses:
|
128
|
+
- Apache 2.0
|
129
|
+
metadata: {}
|
130
|
+
post_install_message:
|
131
|
+
rdoc_options: []
|
132
|
+
require_paths:
|
133
|
+
- lib
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
requirements: []
|
145
|
+
rubyforge_project:
|
146
|
+
rubygems_version: 2.7.6
|
147
|
+
signing_key:
|
148
|
+
specification_version: 4
|
149
|
+
summary: HTTPX, to the future, and beyond
|
150
|
+
test_files: []
|