How to decode a cookie from the header of a websocket connection handshake? (Ruby)
The answer is to use Rack::Utils.unencode
.
I now have this working
Marshal.load(rack_cookie.coder.decode(Rack::Utils.unescape(bakesale.split('--').first)))
decodes perfectly to the hash I need, allowing me to extract the user ID. W00t!
Many thanks to User spastorino over at https://github.com/rack/rack/issues/551 for pointing me in the right direction.
Any success with Sinatra working together with EventMachine WebSockets?
Did not try it, but should not be too hard:
require 'em-websocket'
require 'sinatra/base'
require 'thin'
EM.run do
class App < Sinatra::Base
# Sinatra code here
end
EM::WebSocket.start(:host => '0.0.0.0', :port => 3001) do
# Websocket code here
end
# You could also use Rainbows! instead of Thin.
# Any EM based Rack handler should do.
Thin::Server.start App, '0.0.0.0', 3000
end
Also, Cramp has a websocket implementation that works directly with Thin/Rainbows! you might be able to extract, so you won't even need to run the server on another port.
How to Complete a WebSocket Handshake?
Done!
Found the answers from http://blog.honeybadger.io/building-a-simple-websockets-server-from-scratch-in-ruby/ and It's working perfectly!
The code:
public ClientSocket(Socket socket) {
try {
this.socket = socket;
this.input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.output = new PrintWriter(socket.getOutputStream());
handshake();
}
private void handshake() {
try {
String line;
String key = "";
while (true) {
line = input.readLine();
if (line.startsWith("Sec-WebSocket-Key: ")) {
key = line.split(" ")[1];
System.out.println("'" + key + "'");
}
if (line == null || line.isEmpty())
break;
}
output.println("HTTP/1.1 101 Switching Protocols");
output.println("Upgrade: websocket");
output.println("Connection: Upgrade");
output.println("Sec-WebSocket-Accept: " + encode(key));
output.println();
output.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
private String encode(String key) throws Exception {
key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
byte[] bytes = MessageDigest.getInstance("SHA-1").digest(key.getBytes());
return DatatypeConverter.printBase64Binary(bytes);
}
Now I just have to decode the messages.
Keeping the WebSocket connection alive
The connection is closed each time after handle
. You should rather stay there reading incoming data:
# incoming connection
def setup(self):
print "connection established", self.client_address
def handle(self):
while 1:
try:
self.data = self.request.recv(1024).strip()
# incoming message
self.headers = self.headsToDict(self.data.split("\r\n"))
# its a handshake
if "Upgrade" in self.headers and self.headers["Upgrade"] == "websocket":
key = self.headers["Sec-WebSocket-Key"]
accept = b64encode(sha1(key + MAGIC).hexdigest().decode('hex'))
response = "HTTP/1.1 101 Switching Protocols\r\n"
response += "Upgrade: websocket\r\n"
response += "Connection: Upgrade\r\n"
response += "Sec-WebSocket-Accept: "+accept+"\r\n\r\n"
print response
self.request.send(response)
# its a normal message, echo it back
else:
print self.data
self.request.send(self.data)
except:
print "except"
break
Send auth_token for authentication to ActionCable
I managed to send my authentication token as a query parameter.
When creating my consumer in my javascript app, I'm passing the token in the cable server URL like this:
wss://myapp.com/cable?token=1234
In my cable connection, I can get this token
by accessing the request.params
:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
logger.add_tags 'ActionCable', current_user.name
end
protected:
def find_verified_user
if current_user = User.find_by(token: request.params[:token])
current_user
else
reject_unauthorized_connection
end
end
end
end
It's clearly not ideal, but I don't think you can send custom headers when creating the websocket.
Related Topics
Eliminate Consecutive Duplicates of List Elements
How to Decode a Cookie from the Header of a Websocket Connection Handshake? (Ruby)
Error Using PHPstorm's SASS File Watcher
How to Rescue from a Require "Gem_Name" When the Gem Is Not Installed
How to Make Id a Random 8 Digit Alphanumeric in Rails
Is the .Each Iterator in Ruby Guaranteed to Give the Same Order on the Same Elements Every Time
Calendar Events Notification Not Received on Server
How to Do Ruby Object Serialization Using JSON
Is There a Difference Between :: and . When Calling Class Methods in Ruby
Install Nokogiri 1.6.1 Under Ruby 2.0.0P353 (Rvm Based Installation) Fails (Osx Mavericks)
How to Create a Rails 4 Concern That Takes an Argument
How to Sanitize Raw SQL in Rails 4
Access Current Git Commit Number from Within Heroku App
Can't Reindex Sunspot Solr - Error - Rsolr::Error::Http - 500 Internal Server Error