httpx 0.9.0 → 0.10.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.
Files changed (96) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +48 -0
  3. data/README.md +2 -0
  4. data/doc/release_notes/0_10_0.md +66 -0
  5. data/lib/httpx.rb +2 -0
  6. data/lib/httpx/adapters/faraday.rb +1 -1
  7. data/lib/httpx/chainable.rb +2 -2
  8. data/lib/httpx/connection.rb +3 -9
  9. data/lib/httpx/connection/http1.rb +1 -1
  10. data/lib/httpx/domain_name.rb +440 -0
  11. data/lib/httpx/errors.rb +1 -0
  12. data/lib/httpx/extensions.rb +21 -1
  13. data/lib/httpx/io/ssl.rb +0 -1
  14. data/lib/httpx/io/tcp.rb +6 -5
  15. data/lib/httpx/io/udp.rb +4 -1
  16. data/lib/httpx/options.rb +2 -0
  17. data/lib/httpx/parser/http1.rb +14 -17
  18. data/lib/httpx/plugins/compression.rb +28 -63
  19. data/lib/httpx/plugins/compression/brotli.rb +10 -14
  20. data/lib/httpx/plugins/compression/deflate.rb +7 -6
  21. data/lib/httpx/plugins/compression/gzip.rb +23 -5
  22. data/lib/httpx/plugins/cookies.rb +21 -60
  23. data/lib/httpx/plugins/cookies/cookie.rb +173 -0
  24. data/lib/httpx/plugins/cookies/jar.rb +74 -0
  25. data/lib/httpx/plugins/cookies/set_cookie_parser.rb +142 -0
  26. data/lib/httpx/plugins/expect.rb +3 -5
  27. data/lib/httpx/plugins/follow_redirects.rb +20 -2
  28. data/lib/httpx/plugins/h2c.rb +1 -1
  29. data/lib/httpx/plugins/multipart.rb +0 -8
  30. data/lib/httpx/plugins/persistent.rb +6 -1
  31. data/lib/httpx/plugins/proxy/socks4.rb +3 -1
  32. data/lib/httpx/plugins/rate_limiter.rb +51 -0
  33. data/lib/httpx/plugins/retries.rb +3 -2
  34. data/lib/httpx/plugins/stream.rb +109 -13
  35. data/lib/httpx/pool.rb +6 -6
  36. data/lib/httpx/request.rb +7 -19
  37. data/lib/httpx/resolver/https.rb +7 -2
  38. data/lib/httpx/resolver/native.rb +7 -3
  39. data/lib/httpx/response.rb +16 -23
  40. data/lib/httpx/selector.rb +2 -4
  41. data/lib/httpx/session.rb +17 -11
  42. data/lib/httpx/transcoder/chunker.rb +0 -2
  43. data/lib/httpx/transcoder/form.rb +0 -6
  44. data/lib/httpx/transcoder/json.rb +0 -4
  45. data/lib/httpx/utils.rb +45 -0
  46. data/lib/httpx/version.rb +1 -1
  47. data/sig/buffer.rbs +24 -0
  48. data/sig/callbacks.rbs +14 -0
  49. data/sig/chainable.rbs +37 -0
  50. data/sig/connection.rbs +2 -0
  51. data/sig/connection/http2.rbs +4 -0
  52. data/sig/domain_name.rbs +17 -0
  53. data/sig/errors.rbs +3 -0
  54. data/sig/headers.rbs +42 -0
  55. data/sig/httpx.rbs +14 -0
  56. data/sig/loggable.rbs +11 -0
  57. data/sig/missing.rbs +12 -0
  58. data/sig/options.rbs +118 -0
  59. data/sig/parser/http1.rbs +50 -0
  60. data/sig/plugins/authentication.rbs +11 -0
  61. data/sig/plugins/basic_authentication.rbs +13 -0
  62. data/sig/plugins/compression.rbs +55 -0
  63. data/sig/plugins/compression/brotli.rbs +21 -0
  64. data/sig/plugins/compression/deflate.rbs +17 -0
  65. data/sig/plugins/compression/gzip.rbs +29 -0
  66. data/sig/plugins/cookies.rbs +26 -0
  67. data/sig/plugins/cookies/cookie.rbs +50 -0
  68. data/sig/plugins/cookies/jar.rbs +27 -0
  69. data/sig/plugins/digest_authentication.rbs +33 -0
  70. data/sig/plugins/expect.rbs +19 -0
  71. data/sig/plugins/follow_redirects.rbs +37 -0
  72. data/sig/plugins/h2c.rbs +26 -0
  73. data/sig/plugins/multipart.rbs +19 -0
  74. data/sig/plugins/persistent.rbs +17 -0
  75. data/sig/plugins/proxy.rbs +47 -0
  76. data/sig/plugins/proxy/http.rbs +14 -0
  77. data/sig/plugins/proxy/socks4.rbs +33 -0
  78. data/sig/plugins/proxy/socks5.rbs +36 -0
  79. data/sig/plugins/proxy/ssh.rbs +18 -0
  80. data/sig/plugins/push_promise.rbs +22 -0
  81. data/sig/plugins/rate_limiter.rbs +11 -0
  82. data/sig/plugins/retries.rbs +48 -0
  83. data/sig/plugins/stream.rbs +39 -0
  84. data/sig/pool.rbs +2 -0
  85. data/sig/registry.rbs +9 -0
  86. data/sig/request.rbs +61 -0
  87. data/sig/response.rbs +87 -0
  88. data/sig/session.rbs +49 -0
  89. data/sig/test.rbs +9 -0
  90. data/sig/timeout.rbs +29 -0
  91. data/sig/transcoder.rbs +16 -0
  92. data/sig/transcoder/body.rbs +18 -0
  93. data/sig/transcoder/chunker.rbs +32 -0
  94. data/sig/transcoder/form.rbs +16 -0
  95. data/sig/transcoder/json.rbs +14 -0
  96. metadata +60 -17
@@ -75,11 +75,11 @@ module HTTPX
75
75
  @status == 204 ||
76
76
  @status == 205 ||
77
77
  @status == 304 || begin
78
- content_length = @headers["content-length"]
79
- return false if content_length.nil?
78
+ content_length = @headers["content-length"]
79
+ return false if content_length.nil?
80
80
 
81
- content_length == "0"
82
- end
81
+ content_length == "0"
82
+ end
83
83
  end
84
84
 
85
85
  class Body
@@ -95,6 +95,8 @@ module HTTPX
95
95
  end
96
96
 
97
97
  def write(chunk)
98
+ return if @state == :closed
99
+
98
100
  @length += chunk.bytesize
99
101
  transition
100
102
  @buffer.write(chunk)
@@ -116,7 +118,7 @@ module HTTPX
116
118
  return enum_for(__method__) unless block_given?
117
119
 
118
120
  begin
119
- unless @state == :idle
121
+ if @buffer
120
122
  rewind
121
123
  while (chunk = @buffer.read(@window_size))
122
124
  yield(chunk.force_encoding(@encoding))
@@ -155,20 +157,19 @@ module HTTPX
155
157
  if dest.respond_to?(:path) && @buffer.respond_to?(:path)
156
158
  FileUtils.mv(@buffer.path, dest.path)
157
159
  else
158
- @buffer.rewind
159
160
  ::IO.copy_stream(@buffer, dest)
160
161
  end
161
162
  end
162
163
 
163
164
  # closes/cleans the buffer, resets everything
164
165
  def close
165
- return if @state == :idle
166
-
167
- @buffer.close
168
- @buffer.unlink if @buffer.respond_to?(:unlink)
169
- @buffer = nil
166
+ if @buffer
167
+ @buffer.close
168
+ @buffer.unlink if @buffer.respond_to?(:unlink)
169
+ @buffer = nil
170
+ end
170
171
  @length = 0
171
- @state = :idle
172
+ @state = :closed
172
173
  end
173
174
 
174
175
  def ==(other)
@@ -186,7 +187,7 @@ module HTTPX
186
187
  private
187
188
 
188
189
  def rewind
189
- return if @state == :idle
190
+ return unless @buffer
190
191
 
191
192
  @buffer.rewind
192
193
  end
@@ -267,15 +268,7 @@ module HTTPX
267
268
  @error.message
268
269
  end
269
270
 
270
- def reason
271
- @error.class.name
272
- end
273
-
274
- def headers
275
- {}
276
- end
277
-
278
- def body
271
+ def to_s
279
272
  @error.backtrace.join("\n")
280
273
  end
281
274
 
@@ -285,7 +278,7 @@ module HTTPX
285
278
 
286
279
  # rubocop:disable Style/MissingRespondToMissing
287
280
  def method_missing(meth, *, &block)
288
- raise NoMethodError, "undefined response method `#{meth}' for error response" if Response.public_method_defined?(meth)
281
+ raise NoMethodError, "undefined response method `#{meth}' for error response" if @options.response_class.public_method_defined?(meth)
289
282
 
290
283
  super
291
284
  end
@@ -51,7 +51,7 @@ class HTTPX::Selector
51
51
  READ_INTERESTS = %i[r rw].freeze
52
52
  WRITE_INTERESTS = %i[w rw].freeze
53
53
 
54
- def select_many(interval)
54
+ def select_many(interval, &block)
55
55
  selectables, r, w = nil
56
56
 
57
57
  # first, we group IOs based on interest type. On call to #interests however,
@@ -102,9 +102,7 @@ class HTTPX::Selector
102
102
  writers.delete(io)
103
103
  end if readers
104
104
 
105
- writers.each do |io|
106
- yield io
107
- end if writers
105
+ writers.each(&block) if writers
108
106
  end
109
107
 
110
108
  def select_one(interval)
@@ -66,7 +66,8 @@ module HTTPX
66
66
  end
67
67
 
68
68
  def find_connection(request, connections, options)
69
- uri = URI(request.uri)
69
+ uri = request.uri
70
+
70
71
  connection = pool.find_connection(uri, options) || build_connection(uri, options)
71
72
  unless connections.nil? || connections.include?(connection)
72
73
  connections << connection
@@ -135,10 +136,10 @@ module HTTPX
135
136
  reqs.map do |verb, uri, opts = EMPTY_HASH|
136
137
  build_request(verb, uri, request_options.merge(opts))
137
138
  end
138
- when 2, 3
139
+ when 2
139
140
  verb, uris = args
140
141
  if uris.respond_to?(:each)
141
- uris.map do |uri, **opts|
142
+ uris.enum_for(:each).map do |uri, opts = EMPTY_HASH|
142
143
  build_request(verb, uri, request_options.merge(opts))
143
144
  end
144
145
  else
@@ -189,15 +190,13 @@ module HTTPX
189
190
  begin
190
191
  # guarantee ordered responses
191
192
  loop do
192
- begin
193
- request = requests.first
194
- pool.next_tick until (response = fetch_response(request, connections, request_options))
193
+ request = requests.first
194
+ pool.next_tick until (response = fetch_response(request, connections, request_options))
195
195
 
196
- responses << response
197
- requests.shift
196
+ responses << response
197
+ requests.shift
198
198
 
199
- break if requests.empty? || pool.empty?
200
- end
199
+ break if requests.empty? || pool.empty?
201
200
  end
202
201
  responses
203
202
  ensure
@@ -221,7 +220,7 @@ module HTTPX
221
220
  def plugin(pl, options = nil, &block)
222
221
  # raise Error, "Cannot add a plugin to a frozen config" if frozen?
223
222
  pl = Plugins.load_plugin(pl) if pl.is_a?(Symbol)
224
- unless @plugins.include?(pl)
223
+ if !@plugins.include?(pl)
225
224
  @plugins << pl
226
225
  pl.load_dependencies(self, &block) if pl.respond_to?(:load_dependencies)
227
226
  @default_options = @default_options.dup
@@ -245,6 +244,13 @@ module HTTPX
245
244
  opts.connection_class.__send__(:include, pl::ConnectionMethods) if defined?(pl::ConnectionMethods)
246
245
  pl.configure(self, &block) if pl.respond_to?(:configure)
247
246
 
247
+ @default_options.freeze
248
+ elsif options
249
+ # this can happen when two plugins are loaded, an one of them calls the other under the hood,
250
+ # albeit changing some default.
251
+ @default_options = @default_options.dup
252
+ @default_options = @default_options.merge(options)
253
+
248
254
  @default_options.freeze
249
255
  end
250
256
  self
@@ -10,8 +10,6 @@ module HTTPX::Transcoder
10
10
  class Encoder
11
11
  extend Forwardable
12
12
 
13
- def_delegator :@raw, :readpartial
14
-
15
13
  def initialize(body)
16
14
  @raw = body
17
15
  end
@@ -14,8 +14,6 @@ module HTTPX::Transcoder
14
14
 
15
15
  def_delegator :@raw, :bytesize
16
16
 
17
- def_delegator :@raw, :force_encoding
18
-
19
17
  def initialize(form)
20
18
  @raw = URI.encode_www_form(form)
21
19
  end
@@ -23,10 +21,6 @@ module HTTPX::Transcoder
23
21
  def content_type
24
22
  "application/x-www-form-urlencoded"
25
23
  end
26
-
27
- def to_str
28
- @raw.to_s
29
- end
30
24
  end
31
25
 
32
26
  def encode(form)
@@ -10,14 +10,10 @@ module HTTPX::Transcoder
10
10
  class Encoder
11
11
  extend Forwardable
12
12
 
13
- def_delegator :@raw, :to_str
14
-
15
13
  def_delegator :@raw, :to_s
16
14
 
17
15
  def_delegator :@raw, :bytesize
18
16
 
19
- def_delegator :@raw, :force_encoding
20
-
21
17
  def initialize(json)
22
18
  @raw = ::JSON.dump(json)
23
19
  @charset = @raw.encoding.name.downcase
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Utils
5
+ using URIExtensions
6
+
7
+ module_function
8
+
9
+ # The value of this field can be either an HTTP-date or a number of
10
+ # seconds to delay after the response is received.
11
+ def parse_retry_after(retry_after)
12
+ # first: bet on it being an integer
13
+ Integer(retry_after)
14
+ rescue ArgumentError
15
+ # Then it's a datetime
16
+ time = Time.httpdate(retry_after)
17
+ time - Time.now
18
+ end
19
+
20
+ if RUBY_VERSION < "2.3"
21
+ def uri(*args)
22
+ URI(*args)
23
+ end
24
+ else
25
+
26
+ URIParser = URI::RFC2396_Parser.new
27
+
28
+ def uri(uri)
29
+ return Kernel.URI(uri) unless uri.is_a?(String) && !uri.ascii_only?
30
+
31
+ uri = Kernel.URI(URIParser.escape(uri))
32
+
33
+ non_ascii_hostname = URIParser.unescape(uri.host)
34
+
35
+ non_ascii_hostname.force_encoding(Encoding::UTF_8)
36
+
37
+ idna_hostname = DomainName.new(non_ascii_hostname).hostname
38
+
39
+ uri.host = idna_hostname
40
+ uri.non_ascii_hostname = non_ascii_hostname
41
+ uri
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "0.9.0"
4
+ VERSION = "0.10.0"
5
5
  end
@@ -0,0 +1,24 @@
1
+ module HTTPX
2
+ class Buffer
3
+ include _ToS
4
+ include _ToStr
5
+
6
+ @buffer: String
7
+
8
+ attr_reader limit: Integer
9
+
10
+ def full?: () -> bool
11
+ def shift!: (Integer) -> void
12
+
13
+ # delegated
14
+ def <<: (string data) -> void
15
+ def empty?: () -> bool
16
+ def bytesize: () -> Integer
17
+ def clear: () -> void
18
+ def replace: (string) -> void
19
+
20
+ private
21
+
22
+ def initialize: (Integer limit) -> untyped
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ module HTTPX
2
+ interface _Callable
3
+ def call: (*untyped) -> void
4
+ end
5
+
6
+ module Callbacks
7
+ def on: (Symbol) { (*untyped) -> void } -> void
8
+ def once: (Symbol) { (*untyped) -> void } -> void
9
+ def emit: (Symbol, *untyped) -> void
10
+
11
+ def callbacks: () -> Hash[Symbol, Array[_Callable]]
12
+ | (Symbol) -> Array[_Callable]
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ module HTTPX
2
+ module Chainable
3
+ def request: (*untyped, **untyped) -> (response | Array[response])
4
+ def accept: (String) -> Session
5
+ def wrap: () { (Session) -> void } -> void
6
+ | () -> void
7
+
8
+ def with: (options) -> Session
9
+ | (options) { (Session) -> Session} -> Session
10
+
11
+
12
+
13
+
14
+ def plugin: (:authentication) -> Plugins::sessionAuthentication
15
+ | (:basic_authentication) -> Plugins::sessionBasicAuthentication
16
+ | (:digest_authentication) -> Plugins::sessionDigestAuthentication
17
+ | (:compression) -> Session
18
+ | (:cookies) -> Plugins::sessionCookies
19
+ | (:expect) -> Session
20
+ | (:follow_redirects) -> Plugins::sessionFollowRedirects
21
+ | (:h2c) -> Plugins::sessionH2C
22
+ | (:multipart) -> Session
23
+ | (:persistent) -> Plugins::sessionPersistent
24
+ | (:proxy) -> Plugins::sessionProxy
25
+ | (:push_promise) -> Plugins::sessionPushPromise
26
+ | (:retries) -> Plugins::sessionRetries
27
+ | (:stream) -> Plugins::sessionStream
28
+ | (Symbol | Module, ?options?) { (Class) -> void } -> Session
29
+ | (Symbol | Module, ?options?) -> Session
30
+
31
+ private
32
+
33
+ def default_options: () -> Options
34
+ def branch: (options) -> Session
35
+ | (options) { (Session) -> Session } -> Session
36
+ end
37
+ end
@@ -0,0 +1,2 @@
1
+ class HTTPX::Connection
2
+ end
@@ -0,0 +1,4 @@
1
+ module HTTPX
2
+ class Connection::HTTP2
3
+ end
4
+ end
@@ -0,0 +1,17 @@
1
+ module HTTPX
2
+ class DomainName
3
+ type domain = string | DomainName
4
+
5
+ include Comparable
6
+
7
+ def normalize: (String) -> String
8
+
9
+ def cookie_domain?: (domain, ?bool?) -> bool
10
+
11
+ def self.new: (domain) -> untyped
12
+
13
+ private
14
+
15
+ def initialize: (string) -> untyped
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module HTTPX
2
+ Error: singleton(StandardError)
3
+ end
@@ -0,0 +1,42 @@
1
+ module HTTPX
2
+ class Headers
3
+ include _ToS
4
+
5
+ @headers: headers_hash
6
+
7
+ def self.new: (?untyped headers) -> Headers
8
+
9
+ def ==: (untyped other) -> bool
10
+
11
+ def []: (headers_key field) -> _ToS?
12
+ def []=: (headers_key field, headers_value value) -> void
13
+
14
+ def add: (headers_key field, string value) -> void
15
+ def delete: (headers_key field) -> void
16
+
17
+ def each: () { (headers_key, String) -> void } -> void
18
+ | () -> Enumerable[[headers_key, String], void]
19
+
20
+ def get: (headers_key field) -> Array[String]
21
+ def key?: (headers_key downcased_key) -> bool
22
+
23
+ def merge: (_Each[headers_key, headers_value] other) -> Headers
24
+
25
+ def same_headers?: (untyped headers) -> bool
26
+
27
+ def to_a: () -> Array[[headers_key, String]]
28
+ def inspect: () -> String
29
+
30
+ private
31
+
32
+ def initialize: (headers?) -> untyped
33
+ def array_value: (headers_value) -> Array[String]
34
+ def downcased: (headers_key) -> String
35
+ end
36
+
37
+ type headers_key = String | Symbol
38
+ type headers_value = _ToS | Array[_ToS]
39
+ type headers_hash = Hash[headers_key, headers_value]
40
+ type headers_input = headers_hash | Array[[headers_key, string]]
41
+ type headers = Headers | headers_input
42
+ end
@@ -0,0 +1,14 @@
1
+ module HTTPX
2
+ extend Chainable
3
+
4
+ VERSION: String
5
+
6
+ type uri = URI::HTTP | URI::HTTPS | string
7
+
8
+ type verb = :options | :get | :head | :post | :put | :delete | :trace | :connect |
9
+ :propfind | :proppatch | :mkcol | :copy | :move | :lock | :unlock | :orderpatch |
10
+ :acl | :report | :patch | :search
11
+
12
+ module Plugins
13
+ end
14
+ end