io-stream 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/io/buffered.rb +15 -13
- data/lib/io/readable.rb +5 -5
- data/lib/io/stream/buffered.rb +91 -0
- data/lib/io/stream/{buffered_stream.rb → generic.rb} +24 -45
- data/lib/io/stream/openssl.rb +31 -0
- data/lib/io/stream/version.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +4 -2
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15a89f071ddff7b47577a25a20195cb1d246f9b7105af8ee605ae01e0d267923
|
4
|
+
data.tar.gz: c29039d3ab0d1e234733ddf38f1ddc4be40cbffa92b5f9b47ba1dc247e9ea357
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 90d91151a245677dd5e7b96c2942af1b85f16d2a59c64c981e0d3d256d4af0ce5cc81115bfeb3a1981f765695e4777a0e76caf9c8f3d8995bbba204ebce8d91e
|
7
|
+
data.tar.gz: e40408fa7e69bab982229290640cb0e9c7a4efb9bc39ac40003ef1fe698f1aac962cd10a7464b5b2643bf46dddf6697e26296209527d507a0d135f112bc4bbae
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/io/buffered.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2023-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
unless IO.method_defined?(:buffered
|
6
|
+
unless IO.method_defined?(:buffered?, false)
|
7
7
|
class IO
|
8
8
|
def buffered?
|
9
9
|
return !self.sync
|
@@ -17,16 +17,19 @@ end
|
|
17
17
|
|
18
18
|
require 'socket'
|
19
19
|
|
20
|
-
unless BasicSocket.method_defined?(:buffered
|
20
|
+
unless BasicSocket.method_defined?(:buffered?, false)
|
21
21
|
class BasicSocket
|
22
|
-
|
23
|
-
|
22
|
+
def ip_protocol_tcp?
|
23
|
+
local_address = self.local_address
|
24
|
+
|
25
|
+
return (local_address.afamily == ::Socket::AF_INET || local_address.afamily == ::Socket::AF_INET6) && local_address.socktype == ::Socket::SOCK_STREAM
|
26
|
+
end
|
27
|
+
|
24
28
|
def buffered?
|
25
29
|
return false unless super
|
26
30
|
|
27
|
-
|
28
|
-
|
29
|
-
return !self.getsockopt(IPPROTO_TCP, TCP_NODELAY).bool
|
31
|
+
if ip_protocol_tcp?
|
32
|
+
return !self.getsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY).bool
|
30
33
|
else
|
31
34
|
return true
|
32
35
|
end
|
@@ -35,14 +38,13 @@ unless BasicSocket.method_defined?(:buffered?)
|
|
35
38
|
def buffered=(value)
|
36
39
|
super
|
37
40
|
|
38
|
-
|
39
|
-
when SOCK_STREAM
|
41
|
+
if ip_protocol_tcp?
|
40
42
|
# When buffered is set to true, TCP_NODELAY shold be disabled.
|
41
|
-
self.setsockopt(IPPROTO_TCP, TCP_NODELAY, value ? 0 : 1)
|
43
|
+
self.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, value ? 0 : 1)
|
42
44
|
end
|
43
|
-
rescue Errno::EINVAL
|
45
|
+
rescue ::Errno::EINVAL
|
44
46
|
# On Darwin, sometimes occurs when the connection is not yet fully formed. Empirically, TCP_NODELAY is enabled despite this result.
|
45
|
-
rescue Errno::EOPNOTSUPP
|
47
|
+
rescue ::Errno::EOPNOTSUPP
|
46
48
|
# Some platforms may simply not support the operation.
|
47
49
|
end
|
48
50
|
end
|
@@ -50,7 +52,7 @@ end
|
|
50
52
|
|
51
53
|
require 'stringio'
|
52
54
|
|
53
|
-
unless StringIO.method_defined?(:buffered
|
55
|
+
unless StringIO.method_defined?(:buffered?, false)
|
54
56
|
class StringIO
|
55
57
|
def buffered?
|
56
58
|
return !self.sync
|
data/lib/io/readable.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
# Copyright, 2023-2024, by Samuel Williams.
|
5
5
|
|
6
6
|
class IO
|
7
|
-
unless method_defined?(:readable
|
7
|
+
unless method_defined?(:readable?, false)
|
8
8
|
def readable?
|
9
9
|
# Do not call `eof?` here as it is not concurrency-safe and it can block.
|
10
10
|
!closed?
|
@@ -15,10 +15,10 @@ end
|
|
15
15
|
require 'socket'
|
16
16
|
|
17
17
|
class BasicSocket
|
18
|
-
unless method_defined?(:readable
|
18
|
+
unless method_defined?(:readable?, false)
|
19
19
|
def readable?
|
20
20
|
# If we can wait for the socket to become readable, we know that the socket may still be open.
|
21
|
-
result = self.recv_nonblock(1, MSG_PEEK, exception: false)
|
21
|
+
result = self.recv_nonblock(1, ::Socket::MSG_PEEK, exception: false)
|
22
22
|
|
23
23
|
# No data was available - newer Ruby can return nil instead of empty string:
|
24
24
|
return false if result.nil?
|
@@ -35,7 +35,7 @@ end
|
|
35
35
|
require 'stringio'
|
36
36
|
|
37
37
|
class StringIO
|
38
|
-
unless method_defined?(:readable
|
38
|
+
unless method_defined?(:readable?, false)
|
39
39
|
def readable?
|
40
40
|
!eof?
|
41
41
|
end
|
@@ -45,7 +45,7 @@ end
|
|
45
45
|
require 'openssl'
|
46
46
|
|
47
47
|
class OpenSSL::SSL::SSLSocket
|
48
|
-
unless method_defined?(:readable
|
48
|
+
unless method_defined?(:readable?, false)
|
49
49
|
def readable?
|
50
50
|
to_io.readable?
|
51
51
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2023-2024, by Samuel Williams.
|
5
|
+
|
6
|
+
require_relative 'generic'
|
7
|
+
|
8
|
+
module IO::Stream
|
9
|
+
class Buffered < Generic
|
10
|
+
def self.open(path, mode = "r+", **options)
|
11
|
+
stream = self.new(::File.open(path, mode), **options)
|
12
|
+
|
13
|
+
return stream unless block_given?
|
14
|
+
|
15
|
+
begin
|
16
|
+
yield stream
|
17
|
+
ensure
|
18
|
+
stream.close
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.wrap(io, **options)
|
23
|
+
if io.respond_to?(:buffered=)
|
24
|
+
io.buffered = false
|
25
|
+
end
|
26
|
+
|
27
|
+
stream = self.new(io, **options)
|
28
|
+
|
29
|
+
return stream unless block_given?
|
30
|
+
|
31
|
+
begin
|
32
|
+
yield stream
|
33
|
+
ensure
|
34
|
+
stream.close
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(io, ...)
|
39
|
+
super(...)
|
40
|
+
|
41
|
+
@io = io
|
42
|
+
end
|
43
|
+
|
44
|
+
attr :io
|
45
|
+
|
46
|
+
def closed?
|
47
|
+
@io.closed?
|
48
|
+
end
|
49
|
+
|
50
|
+
def close_read
|
51
|
+
@io.close_read
|
52
|
+
end
|
53
|
+
|
54
|
+
def close_write
|
55
|
+
super
|
56
|
+
ensure
|
57
|
+
@io.close_write
|
58
|
+
end
|
59
|
+
|
60
|
+
def readable?
|
61
|
+
super && @io.readable?
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
def sysclose
|
67
|
+
@io.close
|
68
|
+
end
|
69
|
+
|
70
|
+
def syswrite(buffer)
|
71
|
+
@io.write(buffer)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Reads data from the underlying stream as efficiently as possible.
|
75
|
+
def sysread(size, buffer)
|
76
|
+
# Come on Ruby, why couldn't this just return `nil`? EOF is not exceptional. Every file has one.
|
77
|
+
while true
|
78
|
+
result = @io.read_nonblock(size, buffer, exception: false)
|
79
|
+
|
80
|
+
case result
|
81
|
+
when :wait_readable
|
82
|
+
@io.wait_readable
|
83
|
+
when :wait_writable
|
84
|
+
@io.wait_writable
|
85
|
+
else
|
86
|
+
return result
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -8,6 +8,8 @@ require_relative 'string_buffer'
|
|
8
8
|
require_relative '../buffered'
|
9
9
|
require_relative '../readable'
|
10
10
|
|
11
|
+
require_relative 'openssl'
|
12
|
+
|
11
13
|
module IO::Stream
|
12
14
|
# The default block size for IO buffers. Defaults to 64KB (typical pipe buffer size).
|
13
15
|
BLOCK_SIZE = ENV.fetch('IO_STREAM_BLOCK_SIZE', 1024*64).to_i
|
@@ -15,32 +17,11 @@ module IO::Stream
|
|
15
17
|
# The maximum read size when appending to IO buffers. Defaults to 8MB.
|
16
18
|
MAXIMUM_READ_SIZE = ENV.fetch('IO_STREAM_MAXIMUM_READ_SIZE', BLOCK_SIZE * 128).to_i
|
17
19
|
|
18
|
-
class
|
19
|
-
def
|
20
|
-
stream = self.new(File.open(path, mode), **options)
|
21
|
-
|
22
|
-
return stream unless block_given?
|
23
|
-
|
24
|
-
begin
|
25
|
-
yield stream
|
26
|
-
ensure
|
27
|
-
stream.close
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.wrap(io, **options)
|
32
|
-
if io.respond_to?(:buffered=)
|
33
|
-
io.buffered = false
|
34
|
-
end
|
35
|
-
|
36
|
-
self.new(io, **options)
|
37
|
-
end
|
38
|
-
|
39
|
-
def initialize(io, block_size: BLOCK_SIZE, maximum_read_size: MAXIMUM_READ_SIZE)
|
40
|
-
@io = io
|
20
|
+
class Generic
|
21
|
+
def initialize(block_size: BLOCK_SIZE, maximum_read_size: MAXIMUM_READ_SIZE)
|
41
22
|
@eof = false
|
42
23
|
|
43
|
-
@writing = Thread::Mutex.new
|
24
|
+
@writing = ::Thread::Mutex.new
|
44
25
|
|
45
26
|
@block_size = block_size
|
46
27
|
@maximum_read_size = maximum_read_size
|
@@ -53,9 +34,7 @@ module IO::Stream
|
|
53
34
|
@input_buffer = StringBuffer.new
|
54
35
|
end
|
55
36
|
|
56
|
-
|
57
|
-
|
58
|
-
attr :block_size
|
37
|
+
attr_accessor :block_size
|
59
38
|
|
60
39
|
# Reads `size` bytes from the stream. If size is not specified, read until end of file.
|
61
40
|
def read(size = nil)
|
@@ -101,7 +80,7 @@ module IO::Stream
|
|
101
80
|
raise exception, "encountered eof while reading data"
|
102
81
|
end
|
103
82
|
|
104
|
-
# This is a compatibility shim for existing code
|
83
|
+
# This is a compatibility shim for existing code that uses `readpartial`.
|
105
84
|
def readpartial(size = nil)
|
106
85
|
read_partial(size) or raise EOFError, "Encountered eof while reading data!"
|
107
86
|
end
|
@@ -158,7 +137,7 @@ module IO::Stream
|
|
158
137
|
@write_buffer, @drain_buffer = @drain_buffer, @write_buffer
|
159
138
|
|
160
139
|
begin
|
161
|
-
|
140
|
+
syswrite(@drain_buffer)
|
162
141
|
ensure
|
163
142
|
# If the write operation fails, we still need to clear this buffer, and the data is essentially lost.
|
164
143
|
@drain_buffer.clear
|
@@ -195,34 +174,27 @@ module IO::Stream
|
|
195
174
|
flush
|
196
175
|
end
|
197
176
|
|
198
|
-
def connected?
|
199
|
-
@io.connected?
|
200
|
-
end
|
201
|
-
|
202
177
|
def closed?
|
203
|
-
|
178
|
+
false
|
204
179
|
end
|
205
180
|
|
206
181
|
def close_read
|
207
|
-
@io.close_read
|
208
182
|
end
|
209
183
|
|
210
184
|
def close_write
|
211
185
|
flush
|
212
|
-
ensure
|
213
|
-
@io.close_write
|
214
186
|
end
|
215
187
|
|
216
188
|
# Best effort to flush any unwritten data, and then close the underling IO.
|
217
189
|
def close
|
218
|
-
return if
|
190
|
+
return if closed?
|
219
191
|
|
220
192
|
begin
|
221
193
|
flush
|
222
194
|
rescue
|
223
195
|
# We really can't do anything here unless we want #close to raise exceptions.
|
224
196
|
ensure
|
225
|
-
|
197
|
+
sysclose
|
226
198
|
end
|
227
199
|
end
|
228
200
|
|
@@ -261,19 +233,26 @@ module IO::Stream
|
|
261
233
|
end
|
262
234
|
|
263
235
|
# If the underlying stream is readable, we can read more data:
|
264
|
-
return
|
236
|
+
return !closed?
|
265
237
|
end
|
266
238
|
|
267
|
-
|
239
|
+
protected
|
240
|
+
|
241
|
+
def sysclose
|
242
|
+
raise NotImplementedError
|
243
|
+
end
|
244
|
+
|
245
|
+
def syswrite(buffer)
|
246
|
+
raise NotImplementedError
|
247
|
+
end
|
268
248
|
|
269
249
|
# Reads data from the underlying stream as efficiently as possible.
|
270
250
|
def sysread(size, buffer)
|
271
|
-
|
272
|
-
@io.sysread(size, buffer)
|
273
|
-
rescue EOFError
|
274
|
-
return false
|
251
|
+
raise NotImplementedError
|
275
252
|
end
|
276
253
|
|
254
|
+
private
|
255
|
+
|
277
256
|
# Fills the buffer from the underlying stream.
|
278
257
|
def fill_read_buffer(size = @block_size)
|
279
258
|
# We impose a limit because the underlying `read` system call can fail if we request too much data in one go.
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module OpenSSL
|
4
|
+
module SSL
|
5
|
+
class SSLSocket
|
6
|
+
unless method_defined?(:close_read)
|
7
|
+
def close_read
|
8
|
+
# Ignored.
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
unless method_defined?(:close_write)
|
13
|
+
def close_write
|
14
|
+
self.stop
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
unless method_defined?(:wait_readable)
|
19
|
+
def wait_readable(...)
|
20
|
+
to_io.wait_readable(...)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
unless method_defined?(:wait_writable)
|
25
|
+
def wait_writable(...)
|
26
|
+
to_io.wait_writable(...)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/io/stream/version.rb
CHANGED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: io-stream
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -48,7 +48,9 @@ files:
|
|
48
48
|
- lib/io/buffered.rb
|
49
49
|
- lib/io/readable.rb
|
50
50
|
- lib/io/stream.rb
|
51
|
-
- lib/io/stream/
|
51
|
+
- lib/io/stream/buffered.rb
|
52
|
+
- lib/io/stream/generic.rb
|
53
|
+
- lib/io/stream/openssl.rb
|
52
54
|
- lib/io/stream/string_buffer.rb
|
53
55
|
- lib/io/stream/version.rb
|
54
56
|
- license.md
|
metadata.gz.sig
CHANGED
Binary file
|