iomultiplex 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e3ac2bf50945d2563b5ba09b1343fdb23dcdeff5
4
+ data.tar.gz: 68c237d349ea45972799c40028ceeb4d03fa4e2a
5
+ SHA512:
6
+ metadata.gz: 3a32b0d814b4de29daa438646e88dcde578003dadea981c604e5fe4069f30a2bf9e8224f2f2035a4d5f1b0d937d7f1a8d706ce67f2e373fb5d2096bb69a68d53
7
+ data.tar.gz: 3f94d132f6d4d62208a9b0295b6cf462901d39292326f08280429c93a206bb1b14c97bff14d21bad281223a9c3be38e303ed01bd73d5cab1d566c7a52dfc813c
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ # Copyright 2014-2016 Jason Woods
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'cabin'
18
+ require 'iomultiplex/mixins/callback'
19
+ require 'iomultiplex/mixins/logslow'
20
+ require 'iomultiplex/mixins/post'
21
+ require 'iomultiplex/mixins/select'
22
+ require 'iomultiplex/mixins/state'
23
+ require 'iomultiplex/mixins/timer'
24
+ require 'iomultiplex/iomultiplex'
25
+ require 'iomultiplex/ioreactor'
26
+ require 'iomultiplex/tcplistener'
@@ -0,0 +1,126 @@
1
+ # encoding: utf-8
2
+
3
+ # Copyright 2014-2016 Jason Woods
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module IOMultiplex
18
+ # A single multiplexer that can process hundreds of clients in a single thread
19
+ class Multiplexer
20
+ include Mixins::Logger
21
+ include Mixins::State
22
+ include Mixins::Select
23
+ include Mixins::Post
24
+ include Mixins::Timer
25
+ include Mixins::Callback
26
+
27
+ attr_reader :id
28
+
29
+ def initialize(options = {})
30
+ @mutex = Mutex.new
31
+ @connections = 0
32
+
33
+ initialize_logger options[:logger], options[:logger_context]
34
+
35
+ initialize_state
36
+ initialize_select options
37
+ initialize_post
38
+ initialize_timers
39
+ initialize_callbacks
40
+
41
+ @id = options[:id] || object_id
42
+ add_logger_context 'multiplexer', @id
43
+ nil
44
+ end
45
+
46
+ def run
47
+ run_once until @shutdown
48
+
49
+ log_debug 'Shutdown'
50
+
51
+ # Forced shutdown
52
+ each_registered_client(&:force_close)
53
+ nil
54
+ end
55
+
56
+ def add(client)
57
+ raise ArgumentError,
58
+ 'Client must be an instance of IOMultiplex::IOReactor' \
59
+ unless client.is_a? IOReactor
60
+ raise ArgumentError,
61
+ 'Client is already attached' \
62
+ unless get_state(client).nil?
63
+
64
+ register_state client
65
+ client.attach self
66
+
67
+ @mutex.synchronize do
68
+ @connections += 1
69
+ end
70
+ nil
71
+ end
72
+
73
+ def remove(client)
74
+ must_get_state(client)
75
+
76
+ @mutex.synchronize do
77
+ @connections -= 1
78
+ end
79
+
80
+ client.detach
81
+ stop_all client
82
+ remove_post client
83
+ remove_timer client
84
+ deregister_state client
85
+ nil
86
+ end
87
+
88
+ def connections
89
+ @mutex.synchronize do
90
+ @connections
91
+ end
92
+ end
93
+
94
+ def shutdown
95
+ @shutdown = true
96
+ @nio.wakeup
97
+ nil
98
+ end
99
+
100
+ protected
101
+
102
+ def run_once
103
+ # If post processing is scheduled, do not block on select
104
+ # Otherwise, only block until next timer
105
+ # And if no timers, bock indefinitely
106
+ timeout = nil
107
+ if schedule_post_processing
108
+ timeout = 0
109
+ else
110
+ timeout = next_timer
111
+ unless timeout.nil?
112
+ timeout = (timeout - Time.now).ceil
113
+ timeout = 0 if timeout < 0
114
+ end
115
+ end
116
+
117
+ select_io timeout
118
+
119
+ trigger_post_processing
120
+
121
+ # Trigger callbacks and timers
122
+ trigger_callbacks
123
+ trigger_timers
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,118 @@
1
+ # encoding: utf-8
2
+
3
+ # Copyright 2014-2016 Jason Woods
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'iomultiplex/mixins/ioreactor/read'
18
+ require 'iomultiplex/mixins/ioreactor/write'
19
+ require 'iomultiplex/stringbuffer'
20
+
21
+ module IOMultiplex
22
+ # IOReactor - reactor style wrapper around IO objects
23
+ class IOReactor
24
+ include Mixins::Logger
25
+ include Mixins::IOReactor::Read
26
+ include Mixins::IOReactor::Write
27
+
28
+ attr_reader :id
29
+ attr_reader :io
30
+ attr_reader :mode
31
+ attr_reader :peer
32
+
33
+ def initialize(io, mode = 'rw', id = nil)
34
+ @io = io
35
+ @multiplexer = nil
36
+ @attached = false
37
+ @close_scheduled = false
38
+ @eof_scheduled = false
39
+ @exception = nil
40
+
41
+ @r = mode.index('r').nil? ? false : true
42
+ @w = mode.index('w').nil? ? false : true
43
+
44
+ if @r
45
+ @read_buffer = StringBuffer.new
46
+ @pause = false
47
+ end
48
+ if @w
49
+ @write_buffer = StringBuffer.new
50
+ @write_immediately = true
51
+ end
52
+
53
+ @id = id || calculate_id
54
+ nil
55
+ end
56
+
57
+ def addr
58
+ @io.addr
59
+ end
60
+
61
+ def peeraddr
62
+ @io.peeraddr
63
+ end
64
+
65
+ def attach(multiplexer)
66
+ raise ArgumentError, 'Socket is already attached' if @attached
67
+
68
+ @multiplexer = multiplexer
69
+ initialize_logger multiplexer.logger, multiplexer.logger_context.dup
70
+ add_logger_context 'client', @id
71
+
72
+ @multiplexer.wait_read self if @r
73
+
74
+ @attached = true
75
+ nil
76
+ end
77
+
78
+ def detach
79
+ raise ArgumentError, 'Socket is not yet attached' unless @attached
80
+ @attached = false
81
+ nil
82
+ end
83
+
84
+ def close
85
+ @read_buffer.reset
86
+ if !@w
87
+ @close_scheduled = true
88
+ else
89
+ force_close
90
+ end
91
+ nil
92
+ end
93
+
94
+ def force_close
95
+ @multiplexer.remove self
96
+ @io.close unless @io.closed?
97
+ nil
98
+ end
99
+
100
+ protected
101
+
102
+ def calculate_id
103
+ if @io.respond_to?(:peeraddr)
104
+ begin
105
+ peer = @io.peeraddr(:numeric)
106
+ # IPv4 format
107
+ return "#{peer[2]}:#{peer[1]}" if peer[2].index(':').nil?
108
+ # IPv6 format
109
+ return "[#{peer[2]}]:#{peer[1]}"
110
+ rescue NotImplementedError, Errno::ENOTCONN
111
+ return @io.inspect
112
+ end
113
+ end
114
+
115
+ @io.inspect
116
+ end
117
+ end # ::IOReactor
118
+ end # ::IOMultiplex
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ # Copyright 2014-2016 Jason Woods
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'iomultiplex/ioreactor'
18
+
19
+ module IOMultiplex
20
+ class IOReactor
21
+ # Wraps around a buffered IO object
22
+ # When the read signal triggers for the IO object, it will continuously read
23
+ # in the main loops post processing until it receives a WaitReadable signal.
24
+ # This ensures any data left in the IO buffer after a read is correctly read
25
+ # without waiting for a read signal
26
+ class Buffered < IOReactor
27
+ def initialize(io, mode = 'rw', id = nil)
28
+ super io, mode, id
29
+ end
30
+
31
+ protected
32
+
33
+ def do_read
34
+ read_action
35
+ rescue IO::WaitReadable, Errno::EINTR, Errno::EAGAIN
36
+ @wait_readable = true
37
+ else
38
+ @wait_readable = false
39
+ end
40
+
41
+ def schedule_read
42
+ @multiplexer.defer self unless @read_buffer.empty?
43
+
44
+ # Keep forcing reads until we hit a WaitReadable, in case there is
45
+ # buffered data in the IO
46
+ if @wait_readable
47
+ @multiplexer.wait_read self
48
+ else
49
+ @multiplexer.stop_read self
50
+ @multiplexer.force_read self
51
+ end
52
+ end
53
+ end # ::Buffered
54
+ end # ::IOReactor
55
+ end # ::IOMultiplex
@@ -0,0 +1,91 @@
1
+ # encoding: utf-8
2
+
3
+ # Copyright 2014-2016 Jason Woods
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require 'iomultiplex/ioreactor/buffered'
18
+ require 'iomultiplex/mixins/openssl'
19
+
20
+ module IOMultiplex
21
+ class IOReactor
22
+ # Wraps an OpenSSL IO object which receives TLS connections
23
+ class OpenSSL < Buffered
24
+ include Mixins::OpenSSL
25
+
26
+ def initialize(io, _ = nil, id = nil, ssl_ctx = nil)
27
+ # OpenSSL is implicitly read/write due to key-exchange so we ignore the
28
+ # mode parameter
29
+ super io, 'rw', id
30
+ initialize_ssl ssl_ctx
31
+ end
32
+
33
+ protected
34
+
35
+ def read_nonblock(n)
36
+ ssl_read_nonblock n
37
+ end
38
+
39
+ def write_nonblock(data)
40
+ ssl_write_nonblock data
41
+ end
42
+
43
+ def read_action
44
+ return super if @handshake_completed
45
+
46
+ process_handshake
47
+ super
48
+ end
49
+ end # ::OpenSSL
50
+
51
+ # OpenSSLUpgrading wraps an IO object that acts like a regular
52
+ # IOReactor but can be upgraded to a TLS connection mid-connection
53
+ class OpenSSLUpgrading < Buffered
54
+ include Mixins::OpenSSL
55
+
56
+ def initialize(io, _ = nil, id = nil)
57
+ # OpenSSL is implicitly read/write due to key-exchange so we ignore the
58
+ # mode parameter
59
+ super io, 'rw', id
60
+ @ssl_enabled = false
61
+ end
62
+
63
+ def start_ssl(ssl_ctx)
64
+ raise 'SSL already started', nil if @ssl_enabled
65
+ initialize_ssl ssl_ctx
66
+ @ssl_enabled = true
67
+ log_debug 'Upgrading connection to SSL'
68
+ nil
69
+ end
70
+
71
+ private
72
+
73
+ def read_nonblock(n)
74
+ return super(n) unless @ssl_enabled
75
+ ssl_read_nonblock n
76
+ end
77
+
78
+ def write_nonblock(data)
79
+ return super(data) unless @ssl_enabled
80
+ ssl_write_nonblock data
81
+ end
82
+
83
+ def read_action
84
+ return super unless @ssl_enabled && !@handshake_completed
85
+
86
+ process_handshake
87
+ super
88
+ end
89
+ end # ::OpenSSLUpgrading
90
+ end # ::IOReactor
91
+ end # ::IOMultiplex
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ # Copyright 2014-2016 Jason Woods
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ module IOMultiplex
18
+ module Mixins
19
+ # Callback methods
20
+ # Depends on Mixins::State
21
+ module Callback
22
+ # Run a callback on the IO thread
23
+ # Can be safely triggered from any thread
24
+ def callback(&block)
25
+ @callbacks.push block
26
+ @nio.wakeup
27
+ nil
28
+ end
29
+
30
+ protected
31
+
32
+ def initialize_callbacks
33
+ @callbacks = []
34
+ end
35
+
36
+ def trigger_callbacks
37
+ return if @callbacks.empty?
38
+ @callbacks.each(&:call)
39
+ @callbacks = []
40
+ nil
41
+ end
42
+ end # ::Callback
43
+ end # ::Mixins
44
+ end # ::IOMultiplex