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 |
# File 'lib/ftw/websocket/rack.rb', line 106 def each connection = @env["ftw.connection"] 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.
125 126 127 128 |
# File 'lib/ftw/websocket/rack.rb', line 125 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 |