Class: Tep::WebSocket::Connection
- Inherits:
-
Object
- Object
- Tep::WebSocket::Connection
- Defined in:
- lib/tep/websocket/connection.rb
Instance Attribute Summary collapse
-
#driver ⇒ Object
Returns the value of attribute driver.
-
#fd ⇒ Object
Returns the value of attribute fd.
-
#idle_timeout_seconds ⇒ Object
Returns the value of attribute idle_timeout_seconds.
Class Method Summary collapse
- .dispatch_close(driver, code, reason) ⇒ Object
-
.dispatch_frame(driver, frame) ⇒ Object
Route a parsed frame to the right handler.
- .dispatch_message(driver, data, text) ⇒ Object
- .dispatch_open(driver) ⇒ Object
- .dispatch_ping(driver, data) ⇒ Object
- .dispatch_pong(driver, data) ⇒ Object
Instance Method Summary collapse
-
#initialize(driver) ⇒ Connection
constructor
A new instance of Connection.
-
#run ⇒ Object
Drive the recv loop.
- #set_idle_timeout(seconds) ⇒ Object
Constructor Details
#initialize(driver) ⇒ Connection
Returns a new instance of Connection.
22 23 24 25 26 |
# File 'lib/tep/websocket/connection.rb', line 22 def initialize(driver) @driver = driver @fd = driver.fd @idle_timeout_seconds = 300 end |
Instance Attribute Details
#driver ⇒ Object
Returns the value of attribute driver.
20 21 22 |
# File 'lib/tep/websocket/connection.rb', line 20 def driver @driver end |
#fd ⇒ Object
Returns the value of attribute fd.
20 21 22 |
# File 'lib/tep/websocket/connection.rb', line 20 def fd @fd end |
#idle_timeout_seconds ⇒ Object
Returns the value of attribute idle_timeout_seconds.
20 21 22 |
# File 'lib/tep/websocket/connection.rb', line 20 def idle_timeout_seconds @idle_timeout_seconds end |
Class Method Details
.dispatch_close(driver, code, reason) ⇒ Object
145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/tep/websocket/connection.rb', line 145 def self.dispatch_close(driver, code, reason) evt = Tep::WebSocket::Event.new evt.code = code evt.reason = reason driver.h_close.handle_event(evt) # Auto-cleanup: any Broadcast subscription or Presence row # keyed on this connection's fd gets dropped. Both calls # are no-op-safe when nothing was tracked (zero matches). # Apps that still call unsubscribe_fd / untrack_by_fd # explicitly stay correct -- the second call finds 0 matches. Tep::Broadcast.unsubscribe_fd(driver.fd) Tep::Presence.untrack_by_fd(driver.fd) 0 end |
.dispatch_frame(driver, frame) ⇒ Object
Route a parsed frame to the right handler.
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/tep/websocket/connection.rb', line 90 def self.dispatch_frame(driver, frame) op = frame.opcode if op == Tep::WebSocket::OPCODE_TEXT Connection.(driver, frame.payload, true) elsif op == Tep::WebSocket::OPCODE_BINARY Connection.(driver, frame.payload, false) elsif op == Tep::WebSocket::OPCODE_PING # Auto-pong with the ping's payload (§5.5.3). driver.pong(frame.payload) Connection.dispatch_ping(driver, frame.payload) elsif op == Tep::WebSocket::OPCODE_PONG Connection.dispatch_pong(driver, frame.payload) elsif op == Tep::WebSocket::OPCODE_CLOSE code = 0 reason = "" if frame.payload.length >= 2 code = (frame.payload[0].ord << 8) | frame.payload[1].ord if frame.payload.length > 2 reason = frame.payload[2, frame.payload.length - 2] end end # Echo the close back (§5.5.1) then dispatch. driver.close(code == 0 ? Tep::WebSocket::CLOSE_NORMAL : code, reason) Connection.dispatch_close(driver, code, reason) end 0 end |
.dispatch_message(driver, data, text) ⇒ Object
124 125 126 127 128 129 |
# File 'lib/tep/websocket/connection.rb', line 124 def self.(driver, data, text) evt = Tep::WebSocket::Event.new evt.data = data driver..handle_event(evt) 0 end |
.dispatch_open(driver) ⇒ Object
118 119 120 121 122 |
# File 'lib/tep/websocket/connection.rb', line 118 def self.dispatch_open(driver) evt = Tep::WebSocket::Event.new driver.h_open.handle_event(evt) 0 end |
Instance Method Details
#run ⇒ Object
Drive the recv loop. Returns 0 on clean close, -1 on error. Idempotent across multiple frames per recv: a single sphttp_recv_into_frame fill may contain several complete frames; Connection consumes them all before parking again.
The caller (Tep::Server::Scheduled.write_response) owns the fd lifecycle – run() never calls sphttp_close. On clean close OR error the server’s handle_connection closes the fd via its usual exit path.
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/tep/websocket/connection.rb', line 41 def run # Synthetic open event before the first recv -- handlers # often want to send a welcome message. Connection.dispatch_open(@driver) while true ready = Tep::Scheduler.io_wait(@fd, Tep::Scheduler::READ, @idle_timeout_seconds) if ready == 0 # Timeout: close 1001 going-away. @driver.close(Tep::WebSocket::CLOSE_GOING_AWAY, "idle timeout") return 0 end n = Sock.sphttp_recv_into_frame(@fd) if n <= 0 # EOF or error: dispatch close without sending one back # (peer already gone) and exit. Connection.dispatch_close(@driver, Tep::WebSocket::CLOSE_GOING_AWAY, "") if n == 0 return 0 end return -1 end # Parse + dispatch as many complete frames as possible # from this recv. state = Tep::WebSocket::ConnectionState.new state.start = 0 state.avail = n while true r = Tep::WebSocket::Frame.parse_from_buf(state.start, state.avail) if r.outcome == "need" break end if r.outcome == "close" @driver.close(r.close_code, "protocol error") return 0 end Connection.dispatch_frame(@driver, r.frame) state.start = state.start + r.consumed if state.start >= state.avail break end end end 0 end |
#set_idle_timeout(seconds) ⇒ Object
28 29 30 |
# File 'lib/tep/websocket/connection.rb', line 28 def set_idle_timeout(seconds) @idle_timeout_seconds = seconds end |