excon 0.106.0 → 0.107.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3ae04e066b4d9b67c48f71628596d7635894b7d003100ee91dc5c7ced3ee2bd
4
- data.tar.gz: 10eca55f616e0412fce96f9b779c7510467c8d38c5d823a0b19787e1127b1fbc
3
+ metadata.gz: d921bdb8ea062bd0633ce29f929a60027ba339f7c76f04d39863dcdece71c24d
4
+ data.tar.gz: d9673b03798fdbe67afdc797423b3038d683822fa7092c460797e7d67dde2185
5
5
  SHA512:
6
- metadata.gz: 2eb675d4e3e12987e942832c601eb0116e36f63c7104b4a52ffb3570edbbcc183ec350c925128bfd11aa66ad89437612570efe18c6b48fc037fcf246864336fa
7
- data.tar.gz: 41711db6ceef1e4342387b46b5e10fc76be60e519340862f945f26e5d8cacfeae7c18ba7e09471ac2b1811e4ec91402f4be4c8e13b70b0b54c45af3d3f6d4422
6
+ metadata.gz: 0f2e429edc9541f279484cefd15717f68168e5a84af9a46fd6cb133d43f5d1aa03dff4a6814d4e9f559acb65a152ae275690cbb2961a7967e9fc902b0636177f
7
+ data.tar.gz: 5996b697980f716744a97afaa7b13833618fda6a8cdab7fd7ad90cd363a4cf0c5d470f351b50bcb8fced514e51e46348618c5c4972693560806b4454fd1dbbcc
data/lib/excon/socket.rb CHANGED
@@ -53,14 +53,16 @@ module Excon
53
53
  @nonblock = data[:nonblock]
54
54
  @port ||= @data[:port] || 80
55
55
  @read_buffer = String.new
56
+ @read_offset = 0
56
57
  @eof = false
57
58
  @backend_eof = false
59
+
58
60
  connect
59
61
  end
60
62
 
61
63
  def read(max_length = nil)
62
64
  if @eof
63
- return max_length ? nil : ''
65
+ max_length ? nil : ''
64
66
  elsif @nonblock
65
67
  read_nonblock(max_length)
66
68
  else
@@ -71,20 +73,22 @@ module Excon
71
73
  def readline
72
74
  if @nonblock
73
75
  result = String.new
74
- block = @read_buffer
75
- @read_buffer = String.new
76
+ block = consume_read_buffer
76
77
 
77
78
  loop do
78
79
  idx = block.index("\n")
80
+
79
81
  if idx.nil?
80
82
  result << block
81
83
  else
82
- result << block.slice!(0, idx+1)
83
- add_to_read_buffer(block)
84
+ result << block[..idx]
85
+ rewind_read_buffer(block, idx)
84
86
  break
85
87
  end
88
+
86
89
  block = read_nonblock(@data[:chunk_size]) || raise(EOFError)
87
90
  end
91
+
88
92
  result
89
93
  else # nonblock/legacy
90
94
  begin
@@ -204,20 +208,48 @@ module Excon
204
208
  end
205
209
  end
206
210
 
207
- def add_to_read_buffer(str)
208
- @read_buffer << str
211
+ # Consume any bytes remaining in the read buffer before making a system call.
212
+ def consume_read_buffer
213
+ block = @read_buffer[@read_offset..]
214
+
215
+ @read_offset = @read_buffer.length
216
+
217
+ block
218
+ end
219
+
220
+ # Rewind the read buffer to just after the given index.
221
+ # The offset is moved back to the start of the current chunk and then forward until just after the index.
222
+ def rewind_read_buffer(chunk, idx)
223
+ @read_offset = @read_offset - chunk.length + (idx + 1)
209
224
  @eof = false
210
225
  end
211
226
 
212
227
  def read_nonblock(max_length)
213
228
  begin
229
+ if @read_offset != 0 && @read_offset >= @read_buffer.length
230
+ # Clear the buffer so we can test for emptiness below
231
+ @read_buffer.clear
232
+ # Reset the offset so it matches the length of the buffer when empty.
233
+ @read_offset = 0
234
+ end
235
+
214
236
  if max_length
215
- until @backend_eof || @read_buffer.length >= max_length
216
- @read_buffer << @socket.read_nonblock(max_length - @read_buffer.length)
237
+ until @backend_eof || readable_bytes >= max_length
238
+ if @read_buffer.empty?
239
+ # Avoid allocating a new buffer string when the read buffer is empty
240
+ @read_buffer = @socket.read_nonblock(max_length, @read_buffer)
241
+ else
242
+ @read_buffer << @socket.read_nonblock(max_length - readable_bytes)
243
+ end
217
244
  end
218
245
  else
219
- while !@backend_eof
220
- @read_buffer << @socket.read_nonblock(@data[:chunk_size])
246
+ until @backend_eof
247
+ if @read_buffer.empty?
248
+ # Avoid allocating a new buffer string when the read buffer is empty
249
+ @read_buffer = @socket.read_nonblock(@data[:chunk_size], @read_buffer)
250
+ else
251
+ @read_buffer << @socket.read_nonblock(@data[:chunk_size])
252
+ end
221
253
  end
222
254
  end
223
255
  rescue OpenSSL::SSL::SSLError => error
@@ -237,18 +269,32 @@ module Excon
237
269
  @backend_eof = true
238
270
  end
239
271
 
240
- ret = if max_length
272
+ if max_length
241
273
  if @read_buffer.empty?
242
- nil # EOF met at beginning
274
+ # EOF met at beginning
275
+ @eof = @backend_eof
276
+ nil
243
277
  else
244
- @read_buffer.slice!(0, max_length)
278
+ start = @read_offset
279
+
280
+ # Ensure that we can seek backwards when reading until a terminator string.
281
+ # The read offset must never point past the end of the read buffer.
282
+ @read_offset += max_length > readable_bytes ? readable_bytes : max_length
283
+ @read_buffer[start...@read_offset]
245
284
  end
246
285
  else
247
286
  # read until EOFError, so return everything
248
- @read_buffer.slice!(0, @read_buffer.length)
287
+ start = @read_offset
288
+
289
+ @read_offset = @read_buffer.length
290
+ @eof = @backend_eof
291
+
292
+ @read_buffer[start..]
249
293
  end
250
- @eof = @backend_eof && @read_buffer.empty?
251
- ret
294
+ end
295
+
296
+ def readable_bytes
297
+ @read_buffer.length - @read_offset
252
298
  end
253
299
 
254
300
  def read_block(max_length)
@@ -353,7 +399,7 @@ module Excon
353
399
  def request_time_remaining
354
400
  now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
355
401
  deadline = @data[:deadline]
356
-
402
+
357
403
  raise(Excon::Errors::Timeout.new('request timeout reached')) if now >= deadline
358
404
 
359
405
  deadline - now
data/lib/excon/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Excon
4
- VERSION = '0.106.0'
4
+ VERSION = '0.107.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: excon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.106.0
4
+ version: 0.107.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dpiddy (Dan Peterson)
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2023-12-13 00:00:00.000000000 Z
13
+ date: 2023-12-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec