io_request 2.3.0 → 2.5.1
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/lib/io_request.rb +6 -0
- data/lib/io_request/client.rb +31 -17
- data/lib/io_request/connection/ssl_sockets.rb +36 -17
- data/lib/io_request/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 351ec9fd506f974e564ef51c452e47c6bb4a113022b0ec04e708245cd70fe12c
|
4
|
+
data.tar.gz: 7244b2989c36d827d86ef16aa84920e8f91bae6bf9009f4d503f2aaf6f58f164
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 549415e26de1d5919b1f97a8cfa289e07b5510ae8d54640079a213d4fa93e395e55a804d019c8342d8a6c97c1ee1d033beea534fb9d33814431a35eb21d72501
|
7
|
+
data.tar.gz: e1f0d467271a09288471cc1c3251fc6719b7d54e2cbe1f50a044cf118616a05bf2c989bc6b6ad5c70d82d71cdb74e8cac452c01be107e9181c3021d06831a2f4
|
data/lib/io_request.rb
CHANGED
@@ -4,6 +4,12 @@
|
|
4
4
|
module IORequest
|
5
5
|
# Client received message of zero size.
|
6
6
|
class ZeroSizeMessageError < RuntimeError; end
|
7
|
+
|
8
|
+
# Authorization failed.
|
9
|
+
class AuthorizationFailureError < RuntimeError; end
|
10
|
+
|
11
|
+
# Request timed out.
|
12
|
+
class RequestTimeoutError < RuntimeError; end
|
7
13
|
end
|
8
14
|
|
9
15
|
require_relative 'io_request/version'
|
data/lib/io_request/client.rb
CHANGED
@@ -45,15 +45,17 @@ module IORequest
|
|
45
45
|
# @param r [IO] object to read from.
|
46
46
|
# @param w [IO] object to write to.
|
47
47
|
# @param rw [IO] read-write object (replaces `r` and `w` arguments).
|
48
|
+
# @return [Object] data from {Authorizer}
|
48
49
|
def open(read: nil, write: nil, read_write: nil)
|
49
50
|
@io_r = read_write || read
|
50
51
|
@io_w = read_write || write
|
51
52
|
|
52
53
|
IORequest.logger.debug(prog_name) { 'Starting connection' }
|
53
54
|
|
54
|
-
authorization
|
55
|
+
auth_data = authorization
|
55
56
|
@open = true
|
56
57
|
@data_transition_thread = in_thread(name: 'connection') { data_transition_loop }
|
58
|
+
auth_data
|
57
59
|
end
|
58
60
|
|
59
61
|
def open?
|
@@ -75,6 +77,7 @@ module IORequest
|
|
75
77
|
end
|
76
78
|
alias respond on_request
|
77
79
|
|
80
|
+
# Code to execute after connection is closed.
|
78
81
|
def on_close(&block)
|
79
82
|
IORequest.logger.debug(prog_name) { 'Saved on_close block' }
|
80
83
|
@on_close = block
|
@@ -82,17 +85,18 @@ module IORequest
|
|
82
85
|
|
83
86
|
# If callback block is provided, request will be sent asynchroniously.
|
84
87
|
# @param data [Hash]
|
85
|
-
|
88
|
+
# @param timeout [Integer] timeout in seconds.
|
89
|
+
def request(data, timeout: nil, &callback)
|
86
90
|
message = Message.new(data, type: :request)
|
87
91
|
|
88
92
|
if block_given?
|
89
93
|
# Async execution of request
|
90
94
|
in_thread(callback, name: 'requesting') do |cb|
|
91
|
-
cb.call(send_request_and_wait_for_response(message).data)
|
95
|
+
cb.call(send_request_and_wait_for_response(message, timeout).data)
|
92
96
|
end
|
93
97
|
nil
|
94
98
|
else
|
95
|
-
send_request_and_wait_for_response(message).data
|
99
|
+
send_request_and_wait_for_response(message, timeout).data
|
96
100
|
end
|
97
101
|
end
|
98
102
|
|
@@ -124,18 +128,19 @@ module IORequest
|
|
124
128
|
end
|
125
129
|
|
126
130
|
def authorization
|
127
|
-
auth_successful =
|
131
|
+
auth_successful = false
|
132
|
+
data = nil
|
133
|
+
@mutex_r.synchronize do
|
128
134
|
@mutex_w.synchronize do
|
129
135
|
IORequest.logger.debug(prog_name) { 'Authorizing new client' }
|
130
|
-
@authorizer.authorize(@io_r, @io_w)
|
136
|
+
auth_successful = @authorizer.authorize(@io_r, @io_w)
|
137
|
+
data = @authorizer.data
|
131
138
|
end
|
132
139
|
end
|
133
|
-
unless auth_successful
|
134
|
-
IORequest.logger.debug(prog_name) { 'Authorization failed' }
|
135
|
-
raise 'Authorization failed'
|
136
|
-
end
|
140
|
+
raise AuthorizationFailureError unless auth_successful
|
137
141
|
|
138
|
-
IORequest.logger.debug(prog_name) { "New client authorized with data #{
|
142
|
+
IORequest.logger.debug(prog_name) { "New client authorized with data #{data}" }
|
143
|
+
data
|
139
144
|
end
|
140
145
|
|
141
146
|
def data_transition_loop
|
@@ -179,7 +184,11 @@ module IORequest
|
|
179
184
|
def send_response(response)
|
180
185
|
@mutex_w.synchronize do
|
181
186
|
IORequest.logger.debug(prog_name) { "Sending response: #{response}" }
|
182
|
-
|
187
|
+
begin
|
188
|
+
response.write_to(@io_w)
|
189
|
+
rescue IOError => e
|
190
|
+
IORequest.logger.debug(prog_name) { "Failed to write response message: #{e}" }
|
191
|
+
end
|
183
192
|
end
|
184
193
|
end
|
185
194
|
|
@@ -192,21 +201,26 @@ module IORequest
|
|
192
201
|
IORequest.logger.debug(prog_name) { "Failed to send zero-sized message(#{e})" }
|
193
202
|
end
|
194
203
|
|
195
|
-
def send_request_and_wait_for_response(request)
|
204
|
+
def send_request_and_wait_for_response(request, timeout)
|
196
205
|
@mutex_w.synchronize do
|
197
206
|
IORequest.logger.debug(prog_name) { "Sending message: #{request}" }
|
198
207
|
request.write_to(@io_w)
|
199
208
|
end
|
200
|
-
wait_for_response(request)
|
209
|
+
wait_for_response(request, timeout)
|
201
210
|
end
|
202
211
|
|
203
|
-
def wait_for_response(request)
|
212
|
+
def wait_for_response(request, timeout)
|
204
213
|
IORequest.logger.debug(prog_name) { "Waiting for response for #{request}" }
|
214
|
+
waiting_start_time = Time.now
|
205
215
|
@responses_access_mutex.synchronize do
|
206
216
|
response = nil
|
207
217
|
until response
|
208
|
-
@responses_access_cv.wait(@responses_access_mutex)
|
209
|
-
|
218
|
+
@responses_access_cv.wait(@responses_access_mutex, 1)
|
219
|
+
if @responses_access_mutex.owned?
|
220
|
+
# NOTE: Only accessing responses hash if thread owns access mutex
|
221
|
+
response = @responses.delete(request.id.to_s)
|
222
|
+
end
|
223
|
+
raise RequestTimeoutError if timeout && (Time.now - waiting_start_time >= timeout)
|
210
224
|
end
|
211
225
|
IORequest.logger.debug(prog_name) { "Found response: #{response}" }
|
212
226
|
response
|
@@ -32,11 +32,20 @@ module IORequest
|
|
32
32
|
end
|
33
33
|
|
34
34
|
# @return [Array<IORequest::Client>]
|
35
|
-
|
35
|
+
def clients
|
36
|
+
@clients_data.keys
|
37
|
+
end
|
38
|
+
|
39
|
+
# @param client [IORequest::Client]
|
40
|
+
# @return [Hash, nil] you are free to store anything you want in hash.
|
41
|
+
# Only field you will find in it is `auth` with authenticator data.
|
42
|
+
def data(client)
|
43
|
+
@clients_data[client]
|
44
|
+
end
|
36
45
|
|
37
46
|
# Start server.
|
38
47
|
def start
|
39
|
-
@
|
48
|
+
@clients_data = {}
|
40
49
|
|
41
50
|
@server = TCPServer.new(@port)
|
42
51
|
|
@@ -45,8 +54,8 @@ module IORequest
|
|
45
54
|
|
46
55
|
# Fully stop server.
|
47
56
|
def stop
|
48
|
-
|
49
|
-
@
|
57
|
+
clients.each(&:close)
|
58
|
+
@clients_data.clear
|
50
59
|
|
51
60
|
@server.close
|
52
61
|
@server = nil
|
@@ -68,7 +77,7 @@ module IORequest
|
|
68
77
|
while (socket = @server.accept)
|
69
78
|
handle_socket(socket)
|
70
79
|
end
|
71
|
-
rescue
|
80
|
+
rescue StandardError
|
72
81
|
stop
|
73
82
|
end
|
74
83
|
|
@@ -76,20 +85,21 @@ module IORequest
|
|
76
85
|
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, @ctx)
|
77
86
|
ssl_socket.accept
|
78
87
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
88
|
+
handle_client(ssl_socket, IORequest::Client.new(authorizer: @authorizer))
|
89
|
+
rescue StandardError => e
|
90
|
+
IORequest.logger.warn "Unknown error while handling sockets: #{e}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def handle_client(ssl_socket, client)
|
94
|
+
auth_data = client.open read_write: ssl_socket
|
95
|
+
client.on_request { |data| @requests_handler.call(data, client) }
|
96
|
+
@clients_data[client] = { auth: auth_data }
|
97
|
+
client.on_close do
|
98
|
+
@clients_data.select! { |c, _d| c.open? }
|
90
99
|
end
|
91
100
|
rescue StandardError => e
|
92
|
-
IORequest.
|
101
|
+
IORequest.logger.debug "Failed to open client: #{e}"
|
102
|
+
ssl_socket.close
|
93
103
|
end
|
94
104
|
end
|
95
105
|
|
@@ -108,9 +118,15 @@ module IORequest
|
|
108
118
|
@authorizer = authorizer
|
109
119
|
@requests_handler = requests_handler
|
110
120
|
|
121
|
+
@client = nil
|
122
|
+
|
111
123
|
initialize_ssl_context(certificate, key)
|
112
124
|
end
|
113
125
|
|
126
|
+
def connected?
|
127
|
+
!@client.nil?
|
128
|
+
end
|
129
|
+
|
114
130
|
# Connect to server.
|
115
131
|
# @param host [String] host of server.
|
116
132
|
# @param port [Integer] port of server.
|
@@ -125,6 +141,9 @@ module IORequest
|
|
125
141
|
begin
|
126
142
|
@client.open read_write: ssl_socket
|
127
143
|
@client.on_request(&@requests_handler)
|
144
|
+
@client.on_close do
|
145
|
+
@client = nil
|
146
|
+
end
|
128
147
|
rescue StandardError
|
129
148
|
ssl_socket.close
|
130
149
|
@client = nil
|
data/lib/io_request/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: io_request
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fizvlad
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-07-
|
11
|
+
date: 2020-07-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|