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