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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b2bd42dabd7c03ae27f9003f19fb7b128c86a01e
4
- data.tar.gz: 01f3b6fb4a4915d4dc7ce053e85ab9447688cdd8
3
+ metadata.gz: 1e948ea3cceaa995b5dc7a9c496432e5ef1c0660
4
+ data.tar.gz: 9c70d2dbb9291d65500daa109a751e6228785d50
5
5
  SHA512:
6
- metadata.gz: 91abf0160f443aa3a97fce4f3d107656d1afd79c4aaeb5d608b4b1aa506687248830cc36fe302c8d91f467d6059499d117d638d7aae47a525eb5c688a6ed4d0e
7
- data.tar.gz: 12c0442612e8806bf5307b819bcf44f3536670c5135f3f93ff7120a1420652a0dbd072bb9ddf96093de3d8818d9e95a505ff93446f9bd94d88d72742cfba11a8
6
+ metadata.gz: 9ef50d068ce5fb0f806d7216daea2cd458b4aa5d93534ca56d2a15b7f2324e9638f1e7dbf3318accf957bd59f691b2bcfc45a82a3ea0db6e570d479cfa5af3da
7
+ data.tar.gz: eb1ca84b326181a96bab242927640c1d4fc1663db4ce6f433b1d4d386dd7cc748fc923bf97c948a076c032212c9ddc41931f4254951969db828a8fd85dc36802
@@ -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
- disconnect
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
- @locker.synchronize do
65
- unless @out_que.empty?
66
- @out_que.each { |d| _send d rescue disconnect }
67
- @out_que.clear
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
- (_send data rescue disconnect) if data
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
- # the proc used to remove the connection from the IO stack.
96
- REMOVE_CONNECTION_PROC = Proc.new {|old_io| EventMachine.remove_io old_io }
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
- @out_que.each { |d| _send d rescue true}
102
- @out_que.clear
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
- # the async disconnect proc
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| Plezi.run_async { c.disconnect rescue true } } }
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.dup)
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.dup)
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.dup, 2)
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.dup, 1)
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.send_nonblock self.class.frame_data('', 8)
76
- service.disconnect
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
- # fragment big data chuncks into smaller frames - op-code reset for 0 for all future frames.
87
- [frame << frame_data(data.slice!(0..1048576), op_code, false), op_code = 0] while data.length > 1048576
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
@@ -1,3 +1,3 @@
1
1
  module Plezi
2
- VERSION = "0.8.2"
2
+ VERSION = "0.8.3"
3
3
  end
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.2
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-29 00:00:00.000000000 Z
11
+ date: 2015-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler