httpx 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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