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