httpx 0.2.1 → 0.3.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.
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HTTPX
4
+ module Plugins
5
+ module Multipart
6
+ module FormTranscoder
7
+ module_function
8
+
9
+ class Encoder
10
+ extend Forwardable
11
+
12
+ def_delegator :@raw, :content_type
13
+
14
+ def_delegator :@raw, :to_s
15
+
16
+ def_delegator :@raw, :read
17
+
18
+ def initialize(form)
19
+ @raw = HTTP::FormData.create(form)
20
+ end
21
+
22
+ def bytesize
23
+ @raw.content_length
24
+ end
25
+
26
+ def force_encoding(*args)
27
+ @raw.to_s.force_encoding(*args)
28
+ end
29
+
30
+ def to_str
31
+ @raw.to_s
32
+ end
33
+ end
34
+
35
+ def encode(form)
36
+ Encoder.new(form)
37
+ end
38
+ end
39
+
40
+ def self.load_dependencies(*)
41
+ require "http/form_data"
42
+ end
43
+
44
+ def self.configure(*)
45
+ Transcoder.register("form", FormTranscoder)
46
+ end
47
+ end
48
+ register_plugin :multipart, Multipart
49
+ end
50
+ end
@@ -50,11 +50,12 @@ module HTTPX
50
50
  def find_channel(request, **options)
51
51
  uri = URI(request.uri)
52
52
  proxy = proxy_params(uri)
53
- return super unless proxy
53
+ raise Error, "Failed to connect to proxy" unless proxy
54
54
  @connection.find_channel(proxy) || build_channel(proxy, options)
55
55
  end
56
56
 
57
57
  def build_channel(proxy, options)
58
+ return super if proxy.is_a?(URI::Generic)
58
59
  channel = build_proxy_channel(proxy, **options)
59
60
  set_channel_callbacks(channel, options)
60
61
  channel
@@ -118,6 +119,10 @@ module HTTPX
118
119
  @pending << [request, args]
119
120
  end
120
121
 
122
+ def connecting?
123
+ super || @state == :connecting || @state == :connected
124
+ end
125
+
121
126
  def to_io
122
127
  case @state
123
128
  when :idle
@@ -5,6 +5,7 @@ require "forwardable"
5
5
  module HTTPX
6
6
  class Request
7
7
  extend Forwardable
8
+ using URIExtensions
8
9
 
9
10
  METHODS = [
10
11
  # RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1
@@ -71,10 +72,14 @@ module HTTPX
71
72
  path
72
73
  end
73
74
 
75
+ # https://bugs.ruby-lang.org/issues/15278
74
76
  def authority
75
- host = @uri.host
76
- port_string = @uri.port == @uri.default_port ? nil : ":#{@uri.port}"
77
- "#{host}#{port_string}"
77
+ @uri.authority
78
+ end
79
+
80
+ # https://bugs.ruby-lang.org/issues/15278
81
+ def origin
82
+ @uri.origin
78
83
  end
79
84
 
80
85
  def query
@@ -41,11 +41,7 @@ module HTTPX
41
41
 
42
42
  def bodyless?
43
43
  @request.verb == :head ||
44
- @status < 200 ||
45
- @status == 201 ||
46
- @status == 204 ||
47
- @status == 205 ||
48
- @status == 304
44
+ no_data?
49
45
  end
50
46
 
51
47
  def content_type
@@ -65,6 +61,19 @@ module HTTPX
65
61
  raise HTTPError, self
66
62
  end
67
63
 
64
+ private
65
+
66
+ def no_data?
67
+ @status < 200 ||
68
+ @status == 204 ||
69
+ @status == 205 ||
70
+ @status == 304 || begin
71
+ content_length = @headers["content-length"]
72
+ return false if content_length.nil?
73
+ content_length == "0"
74
+ end
75
+ end
76
+
68
77
  class Body
69
78
  def initialize(response, threshold_size:, window_size: 1 << 14)
70
79
  @response = response
@@ -170,7 +179,7 @@ module HTTPX
170
179
  when :memory
171
180
  if @length > @threshold_size
172
181
  aux = @buffer
173
- @buffer = Tempfile.new("palanca", encoding: @encoding, mode: File::RDWR)
182
+ @buffer = Tempfile.new("httpx", encoding: @encoding, mode: File::RDWR)
174
183
  aux.rewind
175
184
  ::IO.copy_stream(aux, @buffer)
176
185
  # TODO: remove this if/when minor ruby is 2.3
@@ -109,7 +109,7 @@ class HTTPX::Selector
109
109
 
110
110
  readers, writers = IO.select(r, w, nil, interval)
111
111
 
112
- raise HTTPX::TimeoutError, "timed out while waiting on select" if readers.nil? && writers.nil?
112
+ raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") if readers.nil? && writers.nil?
113
113
  rescue IOError, SystemCallError
114
114
  @lock.synchronize do
115
115
  @readers.reject! { |io, _| io.closed? }
@@ -4,28 +4,42 @@ require "timeout"
4
4
 
5
5
  module HTTPX
6
6
  class Timeout
7
- LOOP_TIMEOUT = 5
7
+ CONNECT_TIMEOUT = 60
8
+ OPERATION_TIMEOUT = 60
8
9
 
9
10
  def self.new(opts = {})
10
11
  return opts if opts.is_a?(Timeout)
11
12
  super
12
13
  end
13
14
 
14
- def initialize(loop_timeout: LOOP_TIMEOUT, total_timeout: nil)
15
- @loop_timeout = loop_timeout
15
+ attr_reader :connect_timeout, :operation_timeout
16
+
17
+ def initialize(connect_timeout: CONNECT_TIMEOUT,
18
+ operation_timeout: OPERATION_TIMEOUT,
19
+ total_timeout: nil,
20
+ loop_timeout: nil)
21
+ @connect_timeout = connect_timeout
22
+ @operation_timeout = operation_timeout
16
23
  @total_timeout = total_timeout
24
+ if loop_timeout
25
+ warn ":loop_timeout is deprecated, use :operation_timeout instead"
26
+ @operation_timeout = loop_timeout
27
+ end
17
28
  reset_counter
29
+ @state = :idle # this is here not to trigger the log
30
+ transition(:idle)
18
31
  end
19
32
 
20
33
  def timeout
21
- @loop_timeout || @total_timeout
34
+ @timeout || @total_timeout
22
35
  ensure
23
36
  log_time
24
37
  end
25
38
 
26
39
  def ==(other)
27
40
  if other.is_a?(Timeout)
28
- @loop_timeout == other.instance_variable_get(:@loop_timeout) &&
41
+ @connect_timeout == other.instance_variable_get(:@connect_timeout) &&
42
+ @operation_timeout == other.instance_variable_get(:@operation_timeout) &&
29
43
  @total_timeout == other.instance_variable_get(:@total_timeout)
30
44
  else
31
45
  super
@@ -38,14 +52,29 @@ module HTTPX
38
52
  timeout = Timeout.new(other)
39
53
  merge(timeout)
40
54
  when Timeout
41
- loop_timeout = other.instance_variable_get(:@loop_timeout) || @loop_timeout
55
+ connect_timeout = other.instance_variable_get(:@connect_timeout) || @connect_timeout
56
+ operation_timeout = other.instance_variable_get(:@operation_timeout) || @operation_timeout
42
57
  total_timeout = other.instance_variable_get(:@total_timeout) || @total_timeout
43
- Timeout.new(loop_timeout: loop_timeout, total_timeout: total_timeout)
58
+ Timeout.new(connect_timeout: connect_timeout,
59
+ operation_timeout: operation_timeout,
60
+ total_timeout: total_timeout)
44
61
  else
45
62
  raise ArgumentError, "can't merge with #{other.class}"
46
63
  end
47
64
  end
48
65
 
66
+ def transition(nextstate)
67
+ return if @state == nextstate
68
+ case nextstate
69
+ # when :idle
70
+ when :idle
71
+ @timeout = @connect_timeout
72
+ when :open
73
+ @timeout = @operation_timeout
74
+ end
75
+ @state = nextstate
76
+ end
77
+
49
78
  private
50
79
 
51
80
  def reset_counter
@@ -60,7 +89,7 @@ module HTTPX
60
89
  return unless @time_left
61
90
  return reset_timer unless @started
62
91
  @time_left -= (Process.clock_gettime(Process::CLOCK_MONOTONIC) - @started)
63
- raise TimeoutError, "Timed out after #{@total_timeout} seconds" if @time_left <= 0
92
+ raise TimeoutError.new(@total_timeout, "Timed out after #{@total_timeout} seconds") if @time_left <= 0
64
93
 
65
94
  reset_timer
66
95
  end
@@ -4,15 +4,14 @@ require "forwardable"
4
4
 
5
5
  module HTTPX::Transcoder
6
6
  module Chunker
7
- module_function
7
+ Error = Class.new(HTTPX::Error)
8
+ CRLF = "\r\n".b
8
9
 
9
10
  class Encoder
10
11
  extend Forwardable
11
12
 
12
13
  def_delegator :@raw, :readpartial
13
14
 
14
- CRLF = "\r\n"
15
-
16
15
  def initialize(body)
17
16
  @raw = body
18
17
  end
@@ -30,6 +29,80 @@ module HTTPX::Transcoder
30
29
  end
31
30
  end
32
31
 
32
+ class Decoder
33
+ extend Forwardable
34
+
35
+ def_delegator :@buffer, :empty?
36
+
37
+ def_delegator :@buffer, :<<
38
+
39
+ def_delegator :@buffer, :clear
40
+
41
+ def initialize(buffer, trailers = false)
42
+ @buffer = buffer
43
+ @chunk_length = nil
44
+ @chunk_buffer = "".b
45
+ @finished = false
46
+ @state = :length
47
+ @trailers = trailers
48
+ end
49
+
50
+ def to_s
51
+ @buffer
52
+ end
53
+
54
+ def each
55
+ loop do
56
+ case @state
57
+ when :length
58
+ index = @buffer.index(CRLF)
59
+ return unless index && index.positive?
60
+ # Read hex-length
61
+ hexlen = @buffer.slice!(0, index)
62
+ hexlen[/\h/] || raise(Error, "wrong chunk size line: #{hexlen}")
63
+ @chunk_length = hexlen.hex
64
+ # check if is last chunk
65
+ @finished = @chunk_length.zero?
66
+ nextstate(:crlf)
67
+ when :crlf
68
+ crlf_size = @finished && !@trailers ? 4 : 2
69
+ # consume CRLF
70
+ return if @buffer.bytesize < crlf_size
71
+ raise Error, "wrong chunked encoding format" unless @buffer.start_with?(CRLF * (crlf_size / 2))
72
+ @buffer.slice!(0, crlf_size)
73
+ if @chunk_length.nil?
74
+ nextstate(:length)
75
+ else
76
+ return if @finished
77
+ nextstate(:data)
78
+ end
79
+ when :data
80
+ @chunk_buffer << (slice = @buffer.slice!(0, @chunk_length))
81
+ @chunk_length -= slice.bytesize
82
+ if @chunk_length.zero?
83
+ yield @chunk_buffer unless @chunk_buffer.empty?
84
+ @chunk_buffer.clear
85
+ @chunk_length = nil
86
+ nextstate(:crlf)
87
+ end
88
+ end
89
+ break if @buffer.empty?
90
+ end
91
+ end
92
+
93
+ def finished?
94
+ @finished
95
+ end
96
+
97
+ private
98
+
99
+ def nextstate(state)
100
+ @state = state
101
+ end
102
+ end
103
+
104
+ module_function
105
+
33
106
  def encode(chunks)
34
107
  Encoder.new(chunks)
35
108
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "forwardable"
4
- require "http/form_data"
4
+ require "uri"
5
5
 
6
6
  module HTTPX::Transcoder
7
7
  module Form
@@ -10,22 +10,18 @@ module HTTPX::Transcoder
10
10
  class Encoder
11
11
  extend Forwardable
12
12
 
13
- def_delegator :@raw, :content_type
14
-
15
13
  def_delegator :@raw, :to_s
16
14
 
17
- def_delegator :@raw, :read
15
+ def_delegator :@raw, :bytesize
18
16
 
19
- def initialize(form)
20
- @raw = HTTP::FormData.create(form)
21
- end
17
+ def_delegator :@raw, :force_encoding
22
18
 
23
- def bytesize
24
- @raw.content_length
19
+ def initialize(form)
20
+ @raw = URI.encode_www_form(form)
25
21
  end
26
22
 
27
- def force_encoding(*args)
28
- @raw.to_s.force_encoding(*args)
23
+ def content_type
24
+ "application/x-www-form-urlencoded"
29
25
  end
30
26
 
31
27
  def to_str
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HTTPX
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httpx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tiago Cardoso
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-24 00:00:00.000000000 Z
11
+ date: 2018-12-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http-2
@@ -34,7 +34,7 @@ dependencies:
34
34
  - - "<"
35
35
  - !ruby/object:Gem::Version
36
36
  version: '3'
37
- type: :runtime
37
+ type: :development
38
38
  prerelease: false
39
39
  version_requirements: !ruby/object:Gem::Requirement
40
40
  requirements:
@@ -44,20 +44,6 @@ dependencies:
44
44
  - - "<"
45
45
  - !ruby/object:Gem::Version
46
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
47
  - !ruby/object:Gem::Dependency
62
48
  name: http-cookie
63
49
  requirement: !ruby/object:Gem::Requirement
@@ -82,6 +68,7 @@ files:
82
68
  - LICENSE.txt
83
69
  - README.md
84
70
  - lib/httpx.rb
71
+ - lib/httpx/altsvc.rb
85
72
  - lib/httpx/buffer.rb
86
73
  - lib/httpx/callbacks.rb
87
74
  - lib/httpx/chainable.rb
@@ -100,6 +87,7 @@ files:
100
87
  - lib/httpx/io/unix.rb
101
88
  - lib/httpx/loggable.rb
102
89
  - lib/httpx/options.rb
90
+ - lib/httpx/parser/http1.rb
103
91
  - lib/httpx/plugins/authentication.rb
104
92
  - lib/httpx/plugins/basic_authentication.rb
105
93
  - lib/httpx/plugins/compression.rb
@@ -110,6 +98,7 @@ files:
110
98
  - lib/httpx/plugins/digest_authentication.rb
111
99
  - lib/httpx/plugins/follow_redirects.rb
112
100
  - lib/httpx/plugins/h2c.rb
101
+ - lib/httpx/plugins/multipart.rb
113
102
  - lib/httpx/plugins/proxy.rb
114
103
  - lib/httpx/plugins/proxy/http.rb
115
104
  - lib/httpx/plugins/proxy/socks4.rb
@@ -154,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
154
143
  version: '0'
155
144
  requirements: []
156
145
  rubyforge_project:
157
- rubygems_version: 2.7.6
146
+ rubygems_version: 2.7.8
158
147
  signing_key:
159
148
  specification_version: 4
160
149
  summary: HTTPX, to the future, and beyond