io_request 2.3.0 → 2.5.1

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