plezi 0.8.2 → 0.8.3
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/CHANGELOG.md +9 -0
- data/lib/plezi/eventmachine/connection.rb +24 -30
- data/lib/plezi/eventmachine/io.rb +13 -7
- data/lib/plezi/handlers/controller_magic.rb +9 -0
- data/lib/plezi/server/websocket.rb +1 -1
- data/lib/plezi/server/ws_response.rb +23 -9
- data/lib/plezi/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e948ea3cceaa995b5dc7a9c496432e5ef1c0660
|
4
|
+
data.tar.gz: 9c70d2dbb9291d65500daa109a751e6228785d50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ef50d068ce5fb0f806d7216daea2cd458b4aa5d93534ca56d2a15b7f2324e9638f1e7dbf3318accf957bd59f691b2bcfc45a82a3ea0db6e570d479cfa5af3da
|
7
|
+
data.tar.gz: eb1ca84b326181a96bab242927640c1d4fc1663db4ce6f433b1d4d386dd7cc748fc923bf97c948a076c032212c9ddc41931f4254951969db828a8fd85dc36802
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,13 @@
|
|
1
1
|
#Change Log
|
2
|
+
|
3
|
+
***
|
4
|
+
|
5
|
+
Change log v.0.8.3
|
6
|
+
|
7
|
+
**Auto-ping feature**: WebSocket connections now automatically send a `ping` every ~45 seconds (approximately) before the websocket's connection would timeout. This auto-ping will keep the connection alive even if no data is exchanged.
|
8
|
+
|
9
|
+
**Minor performance updates**: Disconnection workflow was slightly optimized. Also, medium and small websocket messages (less than 131,072 UTF-8 characters) should be faster to handle.
|
10
|
+
|
2
11
|
***
|
3
12
|
|
4
13
|
Change log v.0.8.2
|
@@ -39,7 +39,7 @@ module Plezi
|
|
39
39
|
|
40
40
|
def clear?
|
41
41
|
return false unless timedout? || disconnected?
|
42
|
-
|
42
|
+
close
|
43
43
|
true
|
44
44
|
end
|
45
45
|
|
@@ -58,15 +58,21 @@ module Plezi
|
|
58
58
|
@socket
|
59
59
|
end
|
60
60
|
|
61
|
+
# the proc used to remove the connection from the IO stack.
|
62
|
+
REMOVE_CONNECTION_PROC = Proc.new {|old_io| EventMachine.remove_io old_io }
|
61
63
|
# sends data immidiately - forcing the data to be sent, flushing any pending messages in the que
|
62
64
|
def send data = nil
|
63
65
|
return false unless @out_que.any? || data
|
64
|
-
|
65
|
-
|
66
|
-
@out_que.
|
67
|
-
|
66
|
+
begin
|
67
|
+
@locker.synchronize do
|
68
|
+
unless @out_que.empty?
|
69
|
+
@out_que.each { |d| _send d }
|
70
|
+
@out_que.clear
|
71
|
+
end
|
72
|
+
(_send data rescue disconnect) if data
|
68
73
|
end
|
69
|
-
|
74
|
+
rescue => e
|
75
|
+
EventMachine.queue( [@socket], REMOVE_CONNECTION_PROC)
|
70
76
|
end
|
71
77
|
end
|
72
78
|
|
@@ -89,43 +95,31 @@ module Plezi
|
|
89
95
|
# makes sure any data in the que is send and calls `flush` on the socket, to make sure the buffer is sent.
|
90
96
|
def flush
|
91
97
|
send
|
92
|
-
io.flush
|
98
|
+
io.flush rescue true
|
93
99
|
end
|
94
100
|
|
95
|
-
#
|
96
|
-
|
97
|
-
# called once a socket is disconnected or needs to be disconnected.
|
101
|
+
# called once the connection is closed.
|
102
|
+
#
|
98
103
|
def on_disconnect
|
99
|
-
EventMachine.queue [@socket], REMOVE_CONNECTION_PROC
|
100
104
|
@locker.synchronize do
|
101
|
-
|
102
|
-
@
|
103
|
-
io.flush rescue true
|
104
|
-
io.close rescue true
|
105
|
+
EventMachine.queue [], protocol.method(:on_disconnect) if protocol && !protocol.is_a?(Class)
|
106
|
+
@protocol = false
|
105
107
|
end
|
106
|
-
EventMachine.queue [], protocol.method(:on_disconnect) if protocol && !protocol.is_a?(Class)
|
107
108
|
end
|
109
|
+
# closes the connection.
|
110
|
+
def close
|
111
|
+
flush
|
112
|
+
EventMachine.queue [@socket], REMOVE_CONNECTION_PROC
|
113
|
+
end
|
114
|
+
alias :disconnect :close
|
108
115
|
|
109
116
|
# status markers
|
110
117
|
|
111
|
-
# closes the connection
|
112
|
-
def close
|
113
|
-
@locker.synchronize do
|
114
|
-
io.flush rescue true
|
115
|
-
io.close rescue true
|
116
|
-
end
|
117
|
-
end
|
118
118
|
# returns true if the service is disconnected
|
119
119
|
def disconnected?
|
120
120
|
(@socket.closed? || socket.stat.mode == 0140222) rescue true # if mode is read only, it's the same as closed.
|
121
121
|
end
|
122
|
-
|
123
|
-
FIRE_DISCONNECT_PROC = Proc.new {|handler| handler.on_disconnect }
|
124
|
-
# disconects the service.
|
125
|
-
def disconnect
|
126
|
-
@out_que.clear
|
127
|
-
EventMachine.queue [self], FIRE_DISCONNECT_PROC
|
128
|
-
end
|
122
|
+
|
129
123
|
# returns true if the socket has content to be read.
|
130
124
|
def has_incoming_data?
|
131
125
|
(@socket.stat.size > 0) rescue false
|
@@ -5,10 +5,10 @@ module Plezi
|
|
5
5
|
class BasicIO
|
6
6
|
|
7
7
|
# attribute readers
|
8
|
-
attr_reader :io, :params
|
8
|
+
attr_reader :io, :params, :protocol
|
9
9
|
# initialize the listener with the actual socket and the properties.
|
10
10
|
def initialize io, params
|
11
|
-
@io, @params = io, params
|
11
|
+
@io, @params, @protocol = io, params, false
|
12
12
|
end
|
13
13
|
# returns true if the socket is closed and the object can be cleared.
|
14
14
|
def clear?
|
@@ -29,6 +29,13 @@ module Plezi
|
|
29
29
|
Plezi.error e
|
30
30
|
end
|
31
31
|
end
|
32
|
+
def on_disconnect
|
33
|
+
true
|
34
|
+
end
|
35
|
+
def close
|
36
|
+
false
|
37
|
+
end
|
38
|
+
alias :disconnect :close
|
32
39
|
end
|
33
40
|
module_function
|
34
41
|
|
@@ -47,9 +54,11 @@ module Plezi
|
|
47
54
|
IO_LOCKER.synchronize { EM_IO[io] = job }
|
48
55
|
end
|
49
56
|
|
57
|
+
# the proc for async IO removal, in case of IO exceptions raised by unexpectedly closed sockets.
|
58
|
+
IO_CLEAR_ASYNC_PROC = Proc.new {|c| c.on_disconnect rescue false }
|
50
59
|
# removes an IO from the event machine.
|
51
60
|
def remove_io io
|
52
|
-
IO_LOCKER.synchronize { (EM_IO.delete io).close rescue true
|
61
|
+
IO_LOCKER.synchronize { queue [(EM_IO.delete io)], IO_CLEAR_ASYNC_PROC; (io.close unless io.closed? rescue true); }
|
53
62
|
end
|
54
63
|
|
55
64
|
# deletes any connections that are closed or timed out.
|
@@ -64,7 +73,7 @@ module Plezi
|
|
64
73
|
|
65
74
|
# stops all the connections without stopping the lisntener IO's.
|
66
75
|
def stop_connections
|
67
|
-
IO_LOCKER.synchronize { EM_IO.each { |io, c|
|
76
|
+
IO_LOCKER.synchronize { EM_IO.each { |io, c| c.close } }
|
68
77
|
end
|
69
78
|
|
70
79
|
# set the default idle waiting time.
|
@@ -89,9 +98,6 @@ module Plezi
|
|
89
98
|
@io_timeout = value
|
90
99
|
end
|
91
100
|
|
92
|
-
# the proc for async IO removal, in case of IO exceptions raised by unexpectedly closed sockets.
|
93
|
-
IO_CLEAR_ASYNC_PROC = Proc.new {|c| c.protocol.on_disconnect if (c.protocol rescue false) }
|
94
|
-
|
95
101
|
# hangs for IO data or io_timeout
|
96
102
|
def review_io
|
97
103
|
return false if IO_LOCKER.locked?
|
@@ -52,6 +52,15 @@ module Plezi
|
|
52
52
|
def accepts_broadcast?
|
53
53
|
@_accepts_broadcast
|
54
54
|
end
|
55
|
+
# sets the controller to refuse "broadcasts".
|
56
|
+
#
|
57
|
+
# This allows some websocket connections to isolate themselves even before they are fully disconnected.
|
58
|
+
#
|
59
|
+
# call this method once it is clear the websocket connection should be terminated.
|
60
|
+
def refuse_broadcasts
|
61
|
+
@_accepts_broadcast = false
|
62
|
+
self
|
63
|
+
end
|
55
64
|
|
56
65
|
# this method does two things.
|
57
66
|
#
|
@@ -57,7 +57,7 @@ module Plezi
|
|
57
57
|
# called when a disconnect is fired
|
58
58
|
# (socket was disconnected / connection should be disconnected / shutdown / socket error)
|
59
59
|
def on_disconnect
|
60
|
-
Plezi.log_raw "#{@request[:client_ip]} [#{Time.now.utc}] - #{@connection.object_id} Websocket disconnected.\n"
|
60
|
+
# Plezi.log_raw "#{@request[:client_ip]} [#{Time.now.utc}] - #{@connection.object_id} Websocket disconnected.\n"
|
61
61
|
Plezi::EventMachine.queue [@connection.handler], ON_DISCONNECT_PROC if @connection.handler.methods.include?(:on_disconnect)
|
62
62
|
end
|
63
63
|
|
@@ -17,8 +17,11 @@ module Plezi
|
|
17
17
|
#the request.
|
18
18
|
attr_accessor :request
|
19
19
|
|
20
|
+
PING_PROC = Proc.new {|res| EventMachine.timed_job 45, 1, [res.ping], PING_PROC unless res.service.disconnected? }
|
21
|
+
|
20
22
|
def initialize request
|
21
23
|
@request, @service = request,request.service
|
24
|
+
PING_PROC.call(self)
|
22
25
|
end
|
23
26
|
|
24
27
|
# sends data through the websocket connection in a non-blocking way.
|
@@ -27,7 +30,7 @@ module Plezi
|
|
27
30
|
#
|
28
31
|
# This should be the preferred way.
|
29
32
|
def << str
|
30
|
-
service.send_nonblock self.class.frame_data(str
|
33
|
+
service.send_nonblock self.class.frame_data(str)
|
31
34
|
self
|
32
35
|
end
|
33
36
|
|
@@ -36,19 +39,19 @@ module Plezi
|
|
36
39
|
# Plezi will try a best guess at the type of the data (binary vs. clear text).
|
37
40
|
#
|
38
41
|
def send str
|
39
|
-
service.send self.class.frame_data(str
|
42
|
+
service.send self.class.frame_data(str)
|
40
43
|
self
|
41
44
|
end
|
42
45
|
# sends binary data through the websocket connection in a blocking way.
|
43
46
|
#
|
44
47
|
def binsend str
|
45
|
-
service.send self.class.frame_data(str
|
48
|
+
service.send self.class.frame_data(str, 2)
|
46
49
|
self
|
47
50
|
end
|
48
51
|
# sends clear text data through the websocket connection in a blocking way.
|
49
52
|
#
|
50
53
|
def txtsend str
|
51
|
-
service.send self.class.frame_data(str
|
54
|
+
service.send self.class.frame_data(str, 1)
|
52
55
|
self
|
53
56
|
end
|
54
57
|
|
@@ -72,19 +75,28 @@ module Plezi
|
|
72
75
|
|
73
76
|
# sends any pending data and closes the connection.
|
74
77
|
def close
|
75
|
-
service.
|
76
|
-
service.
|
78
|
+
service.send self.class.frame_data('', 8)
|
79
|
+
service.close
|
77
80
|
end
|
78
81
|
|
82
|
+
FRAME_SIZE_LIMIT = 131_072
|
83
|
+
|
79
84
|
# Dangerzone! ()alters the string, use `send` instead: formats the data as one or more WebSocket frames.
|
80
85
|
def self.frame_data data, op_code = nil, fin = true
|
81
86
|
# set up variables
|
82
87
|
frame = ''.force_encoding('binary')
|
83
88
|
op_code ||= (data.encoding.name == 'UTF-8' ? 1 : 2)
|
84
|
-
data.force_encoding('binary')
|
85
89
|
|
86
|
-
|
87
|
-
|
90
|
+
|
91
|
+
if data[FRAME_SIZE_LIMIT]
|
92
|
+
# fragment big data chuncks into smaller frames - op-code reset for 0 for all future frames.
|
93
|
+
data = data.dup
|
94
|
+
data.force_encoding('binary')
|
95
|
+
[frame << frame_data(data.slice!(0..FRAME_SIZE_LIMIT), op_code, false), op_code = 0] while data.length > FRAME_SIZE_LIMIT # 1048576
|
96
|
+
# frame << frame_data(data.slice!(0..1048576), op_code, false)
|
97
|
+
# data =
|
98
|
+
# op_code = 0
|
99
|
+
end
|
88
100
|
|
89
101
|
# apply extenetions to the frame
|
90
102
|
ext = 0
|
@@ -103,7 +115,9 @@ module Plezi
|
|
103
115
|
frame << 127.chr
|
104
116
|
frame << [data.length].pack('Q>')
|
105
117
|
end
|
118
|
+
frame.force_encoding(data.encoding)
|
106
119
|
frame << data
|
120
|
+
frame.force_encoding('binary')
|
107
121
|
frame
|
108
122
|
end
|
109
123
|
end
|
data/lib/plezi/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plezi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boaz Segev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|