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 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