io_request 2.1.0 → 2.4.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: 3129d25d05cbc0292eb186416db04f305f3c8fa09c525daafe1926ea2c833a76
4
- data.tar.gz: 417380ff03d49890bd4161a1db3811c3e4c105c5357082579801318aadb828cf
3
+ metadata.gz: 987e7926f719a3479bf88cc0bdc76701edbc35e3d77d3c25d0f6ff8009be971c
4
+ data.tar.gz: 7ad37eab2d0718cdd100a9f16c7f272e538e39ae97939aa3e5858dbeb5f05413
5
5
  SHA512:
6
- metadata.gz: e20f44b5bd79b67d7b0d33c5edd16ba34321f1815bf3641c7bf986798e9bfc0ed3495015625b58bc541385fb4b6f35e16c080eac1e9c92404b35db255c815d23
7
- data.tar.gz: 3638cb26446495bf95154d1e3d836b2b5660023491bd53b45175fb8701012d5de6fc3e5832381f4a0fe165b3858bc52056f8087c8a8026f73c8f8c096e5683ae
6
+ metadata.gz: '089626846b0ce20d3cc9ae7c7dfeb9c4b457de3f3500cd75b7456a112e6f1f1304763a6288c4349b220c7b69c24ef5e4b5f34124235359184b5a7e5f34e50b66'
7
+ data.tar.gz: 71e6692045f5752e5bd5eda60eeea755a8ac542f465ed6a19804569f5816a3599b615986a81793a8a4248a22000696ac4a362160efc246236c701beb2dcc0383
@@ -1,7 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Main module.
4
- module IORequest; end
4
+ module IORequest
5
+ # Client received message of zero size.
6
+ class ZeroSizeMessageError < RuntimeError; end
7
+
8
+ # Authorization failed.
9
+ class AuthorizationFailureError < RuntimeError; end
10
+ end
5
11
 
6
12
  require_relative 'io_request/version'
7
13
  require_relative 'io_request/logging'
@@ -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?
@@ -63,15 +65,21 @@ module IORequest
63
65
  # Close connection.
64
66
  def close
65
67
  close_internal
68
+
66
69
  join_threads
67
- @open = false
68
70
  end
69
71
 
70
72
  # @yieldparam [Hash]
71
73
  # @yieldreturn [Hash]
72
- def respond(&block)
73
- IORequest.logger.debug(prog_name) { 'Saved responder block' }
74
- @responder = block
74
+ def on_request(&block)
75
+ IORequest.logger.debug(prog_name) { 'Saved on_request block' }
76
+ @on_request = block
77
+ end
78
+ alias respond on_request
79
+
80
+ def on_close(&block)
81
+ IORequest.logger.debug(prog_name) { 'Saved on_close block' }
82
+ @on_close = block
75
83
  end
76
84
 
77
85
  # If callback block is provided, request will be sent asynchroniously.
@@ -96,50 +104,52 @@ module IORequest
96
104
 
97
105
  def close_internal
98
106
  IORequest.logger.debug(prog_name) { 'Closing connection' }
99
- begin
100
- send_zero_size_request
101
- rescue StandardError
102
- IORequest.logger.debug(prog_name) { 'Failed to send zero-sized message. Closing anyway' }
103
- end
104
- stop_data_transition
107
+ send_zero_size_request
105
108
  close_io
106
- end
107
-
108
- def stop_data_transition
109
- return unless defined?(@data_transition_thread) && !@data_transition_thread.nil?
110
-
111
- IORequest.logger.debug(prog_name) { 'Killing data transition thread' }
112
- @data_transition_thread.kill
113
109
  @data_transition_thread = nil
110
+ @open = false
111
+ @on_close&.call if defined?(@on_close)
114
112
  end
115
113
 
116
114
  def close_io
117
- IORequest.logger.debug(prog_name) { 'Closing IO' }
118
- @mutex_r.synchronize { @io_r&.close }
119
- @mutex_w.synchronize { @io_w&.close }
115
+ begin
116
+ @io_r&.close
117
+ rescue StandardError => e
118
+ IORequest.logger.debug "Failed to close read IO: #{e}"
119
+ end
120
+ begin
121
+ @io_w&.close
122
+ rescue StandardError => e
123
+ IORequest.logger.debug "Failed to close write IO: #{e}"
124
+ end
125
+ IORequest.logger.debug(prog_name) { 'Closed IO streams' }
120
126
  end
121
127
 
122
128
  def authorization
123
- auth_successful = @mutex_r.synchronize do
129
+ auth_successful = false
130
+ data = nil
131
+ @mutex_r.synchronize do
124
132
  @mutex_w.synchronize do
125
133
  IORequest.logger.debug(prog_name) { 'Authorizing new client' }
126
- @authorizer.authorize(@io_r, @io_w)
134
+ auth_successful = @authorizer.authorize(@io_r, @io_w)
135
+ data = @authorizer.data
127
136
  end
128
137
  end
129
- unless auth_successful
130
- IORequest.logger.debug(prog_name) { 'Authorization failed' }
131
- raise 'Authorization failed'
132
- end
138
+ raise AuthorizationFailureError unless auth_successful
133
139
 
134
- IORequest.logger.debug(prog_name) { "New client authorized with data #{@authorizer.data}" }
140
+ IORequest.logger.debug(prog_name) { "New client authorized with data #{data}" }
141
+ data
135
142
  end
136
143
 
137
144
  def data_transition_loop
138
145
  IORequest.logger.debug(prog_name) { 'Starting data transition loop' }
139
146
  loop do
140
147
  data_transition_iteration
148
+ rescue ZeroSizeMessageError
149
+ IORequest.logger.debug(prog_name) { 'Connection was closed from the other side' }
150
+ break
141
151
  rescue StandardError => e
142
- IORequest.logger.debug(prog_name) { "Data transition iteration failed: #{e}" }
152
+ IORequest.logger.debug(prog_name) { "Data transition unknown error: #{e}" }
143
153
  break
144
154
  end
145
155
  close_internal
@@ -156,7 +166,8 @@ module IORequest
156
166
  end
157
167
 
158
168
  def handle_request(message)
159
- data = @responder&.call(message.data) || {}
169
+ data = {}
170
+ data = @on_request&.call(message.data) if defined?(@on_request)
160
171
  response = Message.new(data, type: :response, to: message.id)
161
172
  send_response(response)
162
173
  end
@@ -180,6 +191,8 @@ module IORequest
180
191
  IORequest.logger.debug(prog_name) { 'Sending zero size message' }
181
192
  @io_w.write([0].pack('S'))
182
193
  end
194
+ rescue StandardError => e
195
+ IORequest.logger.debug(prog_name) { "Failed to send zero-sized message(#{e})" }
183
196
  end
184
197
 
185
198
  def send_request_and_wait_for_response(request)
@@ -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,17 +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.respond(&@requests_handler)
83
- @clients << client
84
- rescue StandardError
85
- IORequest.debug "Failed to open client: #{e}"
86
- 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? }
87
99
  end
88
100
  rescue StandardError => e
89
- IORequest.warn "Unknown error while handling sockets: #{e}"
101
+ IORequest.logger.debug "Failed to open client: #{e}"
102
+ ssl_socket.close
90
103
  end
91
104
  end
92
105
 
@@ -105,9 +118,15 @@ module IORequest
105
118
  @authorizer = authorizer
106
119
  @requests_handler = requests_handler
107
120
 
121
+ @client = nil
122
+
108
123
  initialize_ssl_context(certificate, key)
109
124
  end
110
125
 
126
+ def connected?
127
+ !@client.nil?
128
+ end
129
+
111
130
  # Connect to server.
112
131
  # @param host [String] host of server.
113
132
  # @param port [Integer] port of server.
@@ -121,9 +140,11 @@ module IORequest
121
140
  @client = IORequest::Client.new authorizer: @authorizer
122
141
  begin
123
142
  @client.open read_write: ssl_socket
124
- @client.respond(&@requests_handler)
143
+ @client.on_request(&@requests_handler)
144
+ @client.on_close do
145
+ @client = nil
146
+ end
125
147
  rescue StandardError
126
- IORequest.debug "Failed to open client: #{e}"
127
148
  ssl_socket.close
128
149
  @client = nil
129
150
  end
@@ -81,8 +81,8 @@ module IORequest
81
81
  # @param io_r [:read]
82
82
  # @return [Message]
83
83
  def self.read_from(io_r)
84
- size = io_r.read(2).unpack1('S')
85
- raise '0 size received' if size.zero?
84
+ size = io_r.read(2)&.unpack1('S') || 0
85
+ raise ZeroSizeMessageError if size.zero?
86
86
 
87
87
  json_string = io_r.read(size).unpack1("a#{size}")
88
88
  msg = JSON.parse(json_string, symbolize_names: true)
@@ -2,5 +2,5 @@
2
2
 
3
3
  module IORequest
4
4
  # Gem version.
5
- VERSION = '2.1.0'
5
+ VERSION = '2.4.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.1.0
4
+ version: 2.4.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-18 00:00:00.000000000 Z
11
+ date: 2020-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler