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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 39b0e0eb362ea37ce9f2b7359ec439bfc422ca12b2f89ba4483a8cba20c99a2f
4
- data.tar.gz: 7c7fc713e6de0386789cabbde9a68183d52c8cba268bce30ca9a76c503cb8f3b
3
+ metadata.gz: 351ec9fd506f974e564ef51c452e47c6bb4a113022b0ec04e708245cd70fe12c
4
+ data.tar.gz: 7244b2989c36d827d86ef16aa84920e8f91bae6bf9009f4d503f2aaf6f58f164
5
5
  SHA512:
6
- metadata.gz: 5473de710bff683d5dc610a8eb43207353853280c5a4d5a81eccefb1cf40ea924811b5cc7ad16b9b155ebc43186a8691a32c0648cd6dc80c1bb44ba697b38507
7
- data.tar.gz: 8121c16e2f3e3d1b8776aff30d34415a1d29699d9725208621c5af62b17f2664011cea4a03f7b2de370c676dfd0c3f3ec274de9700ba1980ad4e749864cb5bb6
6
+ metadata.gz: 549415e26de1d5919b1f97a8cfa289e07b5510ae8d54640079a213d4fa93e395e55a804d019c8342d8a6c97c1ee1d033beea534fb9d33814431a35eb21d72501
7
+ data.tar.gz: e1f0d467271a09288471cc1c3251fc6719b7d54e2cbe1f50a044cf118616a05bf2c989bc6b6ad5c70d82d71cdb74e8cac452c01be107e9181c3021d06831a2f4
@@ -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'
@@ -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
- def request(data = {}, &callback)
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 = @mutex_r.synchronize do
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 #{@authorizer.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
- response.write_to(@io_w)
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
- response = @responses[request.id.to_s]
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
- attr_reader :clients
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
- @clients = []
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
- @clients.each(&:close)
49
- @clients = []
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
- client = IORequest::Client.new authorizer: @authorizer
80
- begin
81
- client.open read_write: ssl_socket
82
- client.on_request { |data| @requests_handler.call(data, client) }
83
- @clients << client
84
- client.on_close do
85
- @clients.select!(&:open?)
86
- end
87
- rescue StandardError
88
- IORequest.debug "Failed to open client: #{e}"
89
- ssl_socket.close
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.warn "Unknown error while handling sockets: #{e}"
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module IORequest
4
4
  # Gem version.
5
- VERSION = '2.3.0'
5
+ VERSION = '2.5.1'
6
6
  end
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.3.0
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-19 00:00:00.000000000 Z
11
+ date: 2020-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler