Class: FTW::WebSocket::Rack
- Inherits:
-
Object
- Object
- FTW::WebSocket::Rack
- Defined in:
- lib/ftw/websocket/rack.rb
Overview
A websocket helper for Rack
An example with Sinatra:
get "/websocket/echo" do
ws = FTW::WebSocket::Rack.new(env)
stream(:keep_open) do |out|
ws.each do |payload|
# 'payload' is the text payload of a single websocket message
# publish it back to the client
ws.publish(payload)
end
end
ws.rack_response
end
Constant Summary
Constants included from CRLF
Constants included from Constants
Constants::OPCODE_BINARY, Constants::OPCODE_CLOSE, Constants::OPCODE_CONTINUATION, Constants::OPCODE_PING, Constants::OPCODE_PONG, Constants::OPCODE_TEXT, Constants::WEBSOCKET_ACCEPT_UUID
Instance Method Summary collapse
-
#each ⇒ Object
Enumerate each websocket payload (message).
-
#initialize(rack_env) ⇒ Rack
constructor
Create a new websocket rack helper…
-
#publish(message) ⇒ Object
Publish a message over this websocket.
-
#rack_response ⇒ number, ...
Get the response Rack is expecting.
-
#valid? ⇒ Boolean
Is this a valid handshake?.
Constructor Details
#initialize(rack_env) ⇒ Rack
Create a new websocket rack helper… thing.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/ftw/websocket/rack.rb', line 31 def initialize(rack_env) @env = rack_env @handshake_errors = [] # RFC6455 section 4.2.1 bullet 3 expect_equal("websocket", @env["HTTP_UPGRADE"], "The 'Upgrade' header must be set to 'websocket'") # RFC6455 section 4.2.1 bullet 4 # Firefox uses a multivalued 'Connection' header, that appears like this: # Connection: keep-alive, Upgrade # So we have to split this multivalue field. expect_equal(true, @env["HTTP_CONNECTION"].split(/, +/).include?("Upgrade"), "The 'Connection' header must be set to 'Upgrade'") # RFC6455 section 4.2.1 bullet 6 expect_equal("13", @env["HTTP_SEC_WEBSOCKET_VERSION"], "Sec-WebSocket-Version must be set to 13") # RFC6455 section 4.2.1 bullet 5 @key = @env["HTTP_SEC_WEBSOCKET_KEY"] @parser = FTW::WebSocket::Parser.new end |
Instance Method Details
#each ⇒ Object
Enumerate each websocket payload (message).
The payload of each message will be yielded to the block.
Example:
ws.each do |payload|
puts "Received: #{payload}"
end
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/ftw/websocket/rack.rb', line 106 def each connection = @env["ftw.connection"] # There seems to be a bug in http_parser.rb where websocket responses # lead with a newline for some reason. It's like the header terminator # CRLF still has the LF character left in the buffer. Work around it. data = connection.read if data[0] == "\n" connection.pushback(data[1..-1]) else connection.pushback(data) end while true begin data = connection.read(16384) rescue EOFError # connection shutdown, close up. break end @parser.feed(data) do |payload| yield payload if !payload.nil? end end end |
#publish(message) ⇒ Object
Publish a message over this websocket.
135 136 137 138 |
# File 'lib/ftw/websocket/rack.rb', line 135 def publish() writer = FTW::WebSocket::Writer.singleton writer.write_text(@env["ftw.connection"], ) end |
#rack_response ⇒ number, ...
Get the response Rack is expecting.
If this was a valid websocket request, it will return a response that completes the HTTP portion of the websocket handshake.
If this was an invalid websocket request, it will return a 400 status code and descriptions of what failed in the body of the response.
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/ftw/websocket/rack.rb', line 77 def rack_response if valid? # Return the status, headers, body that is expected. sec_accept = @key + WEBSOCKET_ACCEPT_UUID sec_accept_hash = Digest::SHA1.base64digest(sec_accept) headers = { "Upgrade" => "websocket", "Connection" => "Upgrade", "Sec-WebSocket-Accept" => sec_accept_hash } # See RFC6455 section 4.2.2 return 101, headers, nil else # Invalid request, tell the client why. return 400, { "Content-Type" => "text/plain" }, @handshake_errors.map { |m| "#{m}#{CRLF}" } end end |
#valid? ⇒ Boolean
Is this a valid handshake?
63 64 65 |
# File 'lib/ftw/websocket/rack.rb', line 63 def valid? return @handshake_errors.empty? end |