socketry 0.2.0 → 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.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/CHANGES.md +7 -0
- data/README.md +2 -2
- data/lib/socketry/exceptions.rb +4 -1
- data/lib/socketry/ssl/server.rb +4 -2
- data/lib/socketry/ssl/socket.rb +22 -13
- data/lib/socketry/tcp/socket.rb +63 -6
- data/lib/socketry/timeout.rb +2 -0
- data/lib/socketry/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c144331b5b09954959008e255335d5ac43c70be3
|
4
|
+
data.tar.gz: 704cb38c89f0cc0779aaeba5859877f189f5664c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f49d7c5ee328c0c6b7f6366aca43d147a56045b1b1f3dca05219caef2216e791454585e512a7195f61dc1fb767230a2dde82f5130e1a174474d26b6ad0fbbabb
|
7
|
+
data.tar.gz: 7147ec4e25e1d4af36d27452be851adbec957ce65747f95ce873e91936ec962b3576d62b0f171304079dad998030289874a55267682c7842debb7fe7d7ac0413
|
data/.rubocop.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 0.3.0 (2016-09-24)
|
2
|
+
|
3
|
+
* Implement Socketry::TCP::Socket#read and #write
|
4
|
+
* Use StandardError as the base class for Socketry::Error
|
5
|
+
* Add Socketry::ConnectionRefusedError
|
6
|
+
* Parameterize SSL contexts
|
7
|
+
|
1
8
|
## 0.2.0 (2016-09-12)
|
2
9
|
|
3
10
|
* Rename Socketry::TCP::Socket#connected? -> #closed?
|
data/README.md
CHANGED
@@ -64,11 +64,11 @@ socket.writepartial("GET / HTTP/1.0\r\nHost: github.com\r\n\r\n")
|
|
64
64
|
p socket.readpartial(1024)
|
65
65
|
```
|
66
66
|
|
67
|
-
[TCP], [
|
67
|
+
[TCP], [SSL], and [UDP] servers and sockets also available.
|
68
68
|
|
69
69
|
[TCP]: https://github.com/socketry/socketry/wiki/TCP
|
70
|
-
[UDP]: https://github.com/socketry/socketry/wiki/UDP
|
71
70
|
[SSL]: https://github.com/socketry/socketry/wiki/SSL
|
71
|
+
[UDP]: https://github.com/socketry/socketry/wiki/UDP
|
72
72
|
|
73
73
|
## Documentation
|
74
74
|
|
data/lib/socketry/exceptions.rb
CHANGED
@@ -2,7 +2,10 @@
|
|
2
2
|
|
3
3
|
module Socketry
|
4
4
|
# Generic catch all for all Socketry errors
|
5
|
-
Error = Class.new(
|
5
|
+
Error = Class.new(StandardError)
|
6
|
+
|
7
|
+
# Failed to connect to a remote host
|
8
|
+
ConnectionRefusedError = Class.new(Socketry::Error)
|
6
9
|
|
7
10
|
# Invalid address
|
8
11
|
AddressError = Class.new(Socketry::Error)
|
data/lib/socketry/ssl/server.rb
CHANGED
@@ -12,14 +12,16 @@ module Socketry
|
|
12
12
|
hostname_or_port,
|
13
13
|
port = nil,
|
14
14
|
ssl_socket_class: OpenSSL::SSL::SSLSocket,
|
15
|
+
ssl_context: OpenSSL::SSL::SSLContext.new,
|
15
16
|
ssl_params: nil,
|
16
17
|
**args
|
17
18
|
)
|
19
|
+
raise TypeError, "invalid SSL context (#{ssl_context.class})" unless ssl_context.is_a?(OpenSSL::SSL::SSLContext)
|
18
20
|
raise TypeError, "expected Hash, got #{ssl_params.class}" if ssl_params && !ssl_params.is_a?(Hash)
|
19
21
|
|
20
22
|
@ssl_socket_class = ssl_socket_class
|
21
|
-
@ssl_context =
|
22
|
-
@ssl_context.set_params(ssl_params) if ssl_params
|
23
|
+
@ssl_context = ssl_context
|
24
|
+
@ssl_context.set_params(ssl_params) if ssl_params && !ssl_params.empty?
|
23
25
|
@ssl_context.freeze
|
24
26
|
|
25
27
|
super(hostname_or_port, port, **args)
|
data/lib/socketry/ssl/socket.rb
CHANGED
@@ -7,18 +7,27 @@ module Socketry
|
|
7
7
|
class Socket < Socketry::TCP::Socket
|
8
8
|
# Create an unconnected Socketry::SSL::Socket
|
9
9
|
#
|
10
|
-
# @param read_timeout
|
10
|
+
# @param read_timeout [Numeric] Seconds to wait before an uncompleted read errors
|
11
11
|
# @param write_timeout [Numeric] Seconds to wait before an uncompleted write errors
|
12
|
-
# @param timer
|
13
|
-
# @param resolver
|
14
|
-
# @param socket_class
|
12
|
+
# @param timer [Object] A timekeeping object to use for measuring timeouts
|
13
|
+
# @param resolver [Object] A resolver object to use for resolving DNS names
|
14
|
+
# @param socket_class [Object] Underlying socket class which implements I/O ops
|
15
|
+
# @param ssl_socket_class [Object] Class which provides the underlying SSL implementation
|
16
|
+
# @param ssl_context [OpenSSL::SSL::SSLContext] SSL configuration object
|
17
|
+
# @param ssL_params [Hash] Parameter hash to set on the given SSL context
|
15
18
|
# @return [Socketry::SSL::Socket]
|
16
|
-
def initialize(
|
19
|
+
def initialize(
|
20
|
+
ssl_socket_class: OpenSSL::SSL::SSLSocket,
|
21
|
+
ssl_context: OpenSSL::SSL::SSLContext.new,
|
22
|
+
ssl_params: nil,
|
23
|
+
**args
|
24
|
+
)
|
25
|
+
raise TypeError, "invalid SSL context (#{ssl_context.class})" unless ssl_context.is_a?(OpenSSL::SSL::SSLContext)
|
17
26
|
raise TypeError, "expected Hash, got #{ssl_params.class}" if ssl_params && !ssl_params.is_a?(Hash)
|
18
27
|
|
19
28
|
@ssl_socket_class = ssl_socket_class
|
20
|
-
@ssl_context =
|
21
|
-
@ssl_context.set_params(ssl_params) if ssl_params
|
29
|
+
@ssl_context = ssl_context
|
30
|
+
@ssl_context.set_params(ssl_params) if ssl_params && !ssl_params.empty?
|
22
31
|
@ssl_context.freeze
|
23
32
|
@ssl_socket = nil
|
24
33
|
|
@@ -27,12 +36,12 @@ module Socketry
|
|
27
36
|
|
28
37
|
# Make an SSL connection to a remote host
|
29
38
|
#
|
30
|
-
# @param remote_addr
|
31
|
-
# @param remote_port
|
32
|
-
# @param local_addr
|
33
|
-
# @param local_port
|
34
|
-
# @param timeout
|
35
|
-
# @param socket_class [Class]
|
39
|
+
# @param remote_addr [String] DNS name or IP address of the host to connect to
|
40
|
+
# @param remote_port [Fixnum] TCP port to connect to
|
41
|
+
# @param local_addr [String] DNS name or IP address to bind to locally
|
42
|
+
# @param local_port [Fixnum] Local TCP port to bind to
|
43
|
+
# @param timeout [Numeric] Number of seconds to wait before aborting connect
|
44
|
+
# @param socket_class [Class] Custom low-level socket class
|
36
45
|
# @raise [Socketry::AddressError] an invalid address was given
|
37
46
|
# @raise [Socketry::TimeoutError] connect operation timed out
|
38
47
|
# @raise [Socketry::SSL::Error] an error occurred negotiating an SSL connection
|
data/lib/socketry/tcp/socket.rb
CHANGED
@@ -98,6 +98,8 @@ module Socketry
|
|
98
98
|
# Note: `exception: false` for Socket#connect_nonblock is only supported in Ruby 2.3+
|
99
99
|
begin
|
100
100
|
socket.connect_nonblock(remote_sockaddr)
|
101
|
+
rescue Errno::ECONNREFUSED => ex
|
102
|
+
raise Socketry::ConnectionRefusedError, "connection to #{remote_addr}:#{remote_port} refused", ex.backtrace
|
101
103
|
rescue Errno::EINPROGRESS, Errno::EALREADY
|
102
104
|
# Earlier JRuby 9.x versions do not seem to correctly support Socket#wait_writable in this case
|
103
105
|
# Newer versions seem to behave correctly
|
@@ -162,14 +164,16 @@ module Socketry
|
|
162
164
|
# Read a partial amounth of data, blocking until it becomes available
|
163
165
|
#
|
164
166
|
# @param size [Fixnum] number of bytes to attempt to read
|
167
|
+
# @param outbuf [String] an output buffer to read data into
|
168
|
+
# @param timeout [Numeric] Number of seconds to wait for read operation to complete
|
165
169
|
# @raise [Socketry::Error] an I/O operation failed
|
166
|
-
# @return [String]
|
170
|
+
# @return [String, :eof] bytes read, or :eof if socket closed while reading
|
167
171
|
def readpartial(size, outbuf: nil, timeout: @read_timeout)
|
168
172
|
set_timeout(timeout)
|
169
173
|
|
170
174
|
begin
|
171
175
|
while (result = read_nonblock(size, outbuf: outbuf)) == :wait_readable
|
172
|
-
next if @socket.wait_readable(
|
176
|
+
next if @socket.wait_readable(time_remaining(timeout))
|
173
177
|
raise TimeoutError, "read timed out after #{timeout} seconds"
|
174
178
|
end
|
175
179
|
ensure
|
@@ -179,9 +183,35 @@ module Socketry
|
|
179
183
|
result || :eof
|
180
184
|
end
|
181
185
|
|
186
|
+
# Read all of the data in a given string to a socket unless timeout or EOF
|
187
|
+
#
|
188
|
+
# @param size [Fixnum] number of bytes to attempt to read
|
189
|
+
# @param outbuf [String] an output buffer to read data into
|
190
|
+
# @param timeout [Numeric] Number of seconds to wait for read operation to complete
|
191
|
+
# @raise [Socketry::Error] an I/O operation failed
|
192
|
+
# @return [String, :eof] bytes read, or :eof if socket closed while reading
|
193
|
+
def read(size, outbuf: String.new, timeout: @write_timeout)
|
194
|
+
outbuf.clear
|
195
|
+
deadline = lifetime + timeout if timeout
|
196
|
+
|
197
|
+
begin
|
198
|
+
until outbuf.size == size
|
199
|
+
time_remaining = deadline - lifetime if deadline
|
200
|
+
raise Socketry::TimeoutError, "read timed out after #{timeout} seconds" if timeout && time_remaining <= 0
|
201
|
+
|
202
|
+
chunk = readpartial(size - outbuf.size, timeout: time_remaining)
|
203
|
+
return :eof if chunk == :eof
|
204
|
+
|
205
|
+
outbuf << chunk
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
outbuf
|
210
|
+
end
|
211
|
+
|
182
212
|
# Perform a non-blocking write operation
|
183
213
|
#
|
184
|
-
# @param data [String]
|
214
|
+
# @param data [String] data to write to the socket
|
185
215
|
# @raise [Socketry::Error] an I/O operation failed
|
186
216
|
# @return [Fixnum, :wait_writable] number of bytes written, or :wait_writable if op would block
|
187
217
|
def write_nonblock(data)
|
@@ -196,15 +226,16 @@ module Socketry
|
|
196
226
|
|
197
227
|
# Write a partial amounth of data, blocking until it's completed
|
198
228
|
#
|
199
|
-
# @param data [String]
|
229
|
+
# @param data [String] data to write to the socket
|
230
|
+
# @param timeout [Numeric] Number of seconds to wait for write operation to complete
|
200
231
|
# @raise [Socketry::Error] an I/O operation failed
|
201
|
-
# @return [Fixnum, :
|
232
|
+
# @return [Fixnum, :eof] number of bytes written, or :eof if socket closed during writing
|
202
233
|
def writepartial(data, timeout: @write_timeout)
|
203
234
|
set_timeout(timeout)
|
204
235
|
|
205
236
|
begin
|
206
237
|
while (result = write_nonblock(data)) == :wait_writable
|
207
|
-
next if @socket.wait_writable(
|
238
|
+
next if @socket.wait_writable(time_remaining(timeout))
|
208
239
|
raise TimeoutError, "write timed out after #{timeout} seconds"
|
209
240
|
end
|
210
241
|
ensure
|
@@ -214,6 +245,32 @@ module Socketry
|
|
214
245
|
result || :eof
|
215
246
|
end
|
216
247
|
|
248
|
+
# Write all of the data in a given string to a socket unless timeout or EOF
|
249
|
+
#
|
250
|
+
# @param data [String] data to write to the socket
|
251
|
+
# @param timeout [Numeric] Number of seconds to wait for write operation to complete
|
252
|
+
# @raise [Socketry::Error] an I/O operation failed
|
253
|
+
# @return [Fixnum] number of bytes written, or :eof if socket closed during writing
|
254
|
+
def write(data, timeout: @write_timeout)
|
255
|
+
total_written = data.size
|
256
|
+
deadline = lifetime + timeout if timeout
|
257
|
+
|
258
|
+
begin
|
259
|
+
until data.empty?
|
260
|
+
time_remaining = deadline - lifetime if deadline
|
261
|
+
raise Socketry::TimeoutError, "write timed out after #{timeout} seconds" if timeout && time_remaining <= 0
|
262
|
+
|
263
|
+
bytes_written = writepartial(data, timeout: time_remaining)
|
264
|
+
return :eof if bytes_written == :eof
|
265
|
+
|
266
|
+
break if bytes_written == data.bytesize
|
267
|
+
data = data.byteslice(bytes_written..-1)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
total_written
|
272
|
+
end
|
273
|
+
|
217
274
|
# Check whether Nagle's algorithm has been disabled
|
218
275
|
#
|
219
276
|
# @return [true] Nagle's algorithm has been explicitly disabled
|
data/lib/socketry/timeout.rb
CHANGED
data/lib/socketry/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: socketry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tony Arcieri
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-09-
|
11
|
+
date: 2016-09-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: hitimes
|