igrigorik / em-websocket Goto Github PK
View Code? Open in Web Editor NEWEventMachine based WebSocket server
Home Page: http://www.igvita.com/2009/12/22/ruby-websockets-tcp-for-the-browser/
License: MIT License
EventMachine based WebSocket server
Home Page: http://www.igvita.com/2009/12/22/ruby-websockets-tcp-for-the-browser/
License: MIT License
I'm not sure this is an issue or if I have misunderstood:
It seems that the 'framing76' implementation checks the size of each frame and allows a max of 10 MB:
https://github.com/igrigorik/em-websocket/blob/master/lib/em-websocket/framing76.rb#L44
However, none of the other framing implementations (framing05-03) do this check. Would it then be possible for a malicious user to write a client using another framing method and fill the server's memory with junk (the other framing implementations seem to allow for up to 4GB frames)?
Thanks for any answers!
Oh.
Sorry for that. but i spent the whole day trying to go through but i'm stuck.
Maybe i'm a bit newbie so i downloaded and installed DevKit from rubyinstller.org then i tried to gem install eventmachine
- no luck, then: gem install eventmachine-win32
- ok, but i still have the one error:
gem install em-websocket
Temporarily enhancing PATH to include DevKit...
Building native extensions. This could take a while...
ERROR: Error installing em-websocket:
ERROR: Failed to build gem native extension.
C:/Ruby192/bin/ruby.exe extconf.rb
checking for rb_trap_immediate in ruby.h,rubysig.h... no
checking for rb_thread_blocking_region()... yes
checking for inotify_init() in sys/inotify.h... no
checking for __NR_inotify_init in sys/syscall.h... no
checking for writev() in sys/uio.h... no
checking for rb_thread_check_ints()... yes
checking for rb_time_new()... yes
checking for windows.h... yes
checking for winsock.h... yes
checking for main() in -lkernel32... yes
checking for main() in -lrpcrt4... yes
checking for main() in -lgdi32... yes
checking for main() in -lssl... no
creating Makefile
make
g++ -I. -IC:/Ruby192/include/ruby-1.9.1/i386-mingw32 -I/C/Ruby192/include/ruby-1
.9.1/ruby/backward -I/C/Ruby192/include/ruby-1.9.1 -I. -DBUILD_FOR_RUBY -DHAVE_R
B_THREAD_BLOCKING_REGION -DHAVE_TBR -DHAVE_RB_THREAD_CHECK_INTS -DHAVE_RB_TIME_N
EW -DOS_WIN32 -DHAVE_WINDOWS_H -DHAVE_WINSOCK_H -DWITHOUT_SSL -DHAVE_MAKE_PAIR
-O3 -g -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-
strings -Wno-missing-field-initializers -Wno-long-long -o binder.o -c binder.c
pp
In file included from project.h:137:0,
from binder.cpp:20:
binder.h:35:34: warning: type qualifiers ignored on function return type
In file included from project.h:138:0,
from binder.cpp:20:
em.h:91:47: warning: type qualifiers ignored on function return type
em.h:92:76: warning: type qualifiers ignored on function return type
em.h:93:56: warning: type qualifiers ignored on function return type
em.h:95:57: warning: type qualifiers ignored on function return type
em.h:96:60: warning: type qualifiers ignored on function return type
em.h:97:58: warning: type qualifiers ignored on function return type
em.h:98:55: warning: type qualifiers ignored on function return type
em.h:99:36: warning: type qualifiers ignored on function return type
em.h:101:47: warning: type qualifiers ignored on function return type
em.h:106:42: warning: type qualifiers ignored on function return type
em.h:123:45: warning: type qualifiers ignored on function return type
em.h:132:36: warning: type qualifiers ignored on function return type
In file included from project.h:145:0,
from binder.cpp:20:
eventmachine.h:45:61: warning: type qualifiers ignored on function return type
eventmachine.h:46:112: warning: type qualifiers ignored on function return type
eventmachine.h:47:69: warning: type qualifiers ignored on function return type
eventmachine.h:49:73: warning: type qualifiers ignored on function return type
eventmachine.h:62:75: warning: type qualifiers ignored on function return type
eventmachine.h:63:74: warning: type qualifiers ignored on function return type
eventmachine.h:64:77: warning: type qualifiers ignored on function return type
eventmachine.h:65:41: warning: type qualifiers ignored on function return type
In file included from project.h:145:0,
from binder.cpp:20:
eventmachine.h:99:60: warning: type qualifiers ignored on function return type
eventmachine.h:100:58: warning: type qualifiers ignored on function return type
eventmachine.h:102:60: warning: type qualifiers ignored on function return type
eventmachine.h:105:41: warning: type qualifiers ignored on function return type
g++ -I. -IC:/Ruby192/include/ruby-1.9.1/i386-mingw32 -I/C/Ruby192/include/ruby-1
.9.1/ruby/backward -I/C/Ruby192/include/ruby-1.9.1 -I. -DBUILD_FOR_RUBY -DHAVE_R
B_THREAD_BLOCKING_REGION -DHAVE_TBR -DHAVE_RB_THREAD_CHECK_INTS -DHAVE_RB_TIME_N
EW -DOS_WIN32 -DHAVE_WINDOWS_H -DHAVE_WINSOCK_H -DWITHOUT_SSL -DHAVE_MAKE_PAIR
-O3 -g -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-
strings -Wno-missing-field-initializers -Wno-long-long -o cmain.o -c cmain.cpp
In file included from project.h:137:0,
from cmain.cpp:20:
binder.h:35:34: warning: type qualifiers ignored on function return type
In file included from project.h:138:0,
from cmain.cpp:20:
em.h:91:47: warning: type qualifiers ignored on function return type
em.h:92:76: warning: type qualifiers ignored on function return type
em.h:93:56: warning: type qualifiers ignored on function return type
em.h:95:57: warning: type qualifiers ignored on function return type
em.h:96:60: warning: type qualifiers ignored on function return type
em.h:97:58: warning: type qualifiers ignored on function return type
em.h:98:55: warning: type qualifiers ignored on function return type
em.h:99:36: warning: type qualifiers ignored on function return type
em.h:101:47: warning: type qualifiers ignored on function return type
em.h:106:42: warning: type qualifiers ignored on function return type
em.h:123:45: warning: type qualifiers ignored on function return type
em.h:132:36: warning: type qualifiers ignored on function return type
In file included from project.h:145:0,
from cmain.cpp:20:
eventmachine.h:45:61: warning: type qualifiers ignored on function return type
eventmachine.h:46:112: warning: type qualifiers ignored on function return type
eventmachine.h:47:69: warning: type qualifiers ignored on function return type
eventmachine.h:49:73: warning: type qualifiers ignored on function return type
eventmachine.h:62:75: warning: type qualifiers ignored on function return type
eventmachine.h:63:74: warning: type qualifiers ignored on function return type
eventmachine.h:64:77: warning: type qualifiers ignored on function return type
eventmachine.h:65:41: warning: type qualifiers ignored on function return type
In file included from project.h:145:0,
from cmain.cpp:20:
eventmachine.h:99:60: warning: type qualifiers ignored on function return type
eventmachine.h:100:58: warning: type qualifiers ignored on function return type
eventmachine.h:102:60: warning: type qualifiers ignored on function return type
eventmachine.h:105:41: warning: type qualifiers ignored on function return type
cmain.cpp:98:71: warning: type qualifiers ignored on function return type
cmain.cpp:109:122: warning: type qualifiers ignored on function return type
cmain.cpp:119:79: warning: type qualifiers ignored on function return type
cmain.cpp:129:83: warning: type qualifiers ignored on function return type
cmain.cpp:260:85: warning: type qualifiers ignored on function return type
cmain.cpp:270:84: warning: type qualifiers ignored on function return type
cmain.cpp:280:88: warning: type qualifiers ignored on function return type
cmain.cpp:290:51: warning: type qualifiers ignored on function return type
cmain.cpp:300:70: warning: type qualifiers ignored on function return type
cmain.cpp:320:55: warning: type qualifiers ignored on function return type
cmain.cpp:546:70: warning: type qualifiers ignored on function return type
cmain.cpp:669:68: warning: type qualifiers ignored on function return type
cmain.cpp: In function 'int evma_send_file_data_to_connection(long unsigned int,
const char_)':
cmain.cpp:752:6: error: cannot convert 'stat_' to 'stati64' for argument '2' t
o 'int _fstati64(int, stati64)'
make: *** [cmain.o] Error 1
Gem files will remain installed in C:/Ruby192/lib/ruby/gems/1.9.1/gems/eventmach
ine-0.12.10 for inspection.
Results logged to C:/Ruby192/lib/ruby/gems/1.9.1/gems/eventmachine-0.12.10/ext/g
em_make.out
Looks like something has changed in the websockets implementation of Chrome.
I have a small em-websocket server, and it is not accepting connections from chrome a few days ago (on different machines, windows, and macos).
Still works with Safari.
(Chrome is up to date, and I also installed Chrome Canary - with no success.)
I found few differences between actual specification and em-websocket implentation of some parts. Mostly it's in handshake part(for draft 75 and 5&6) and probably(but I'm not sure) in message framing for 5&6. Everything is described in this gist:
https://gist.github.com/1002083
Could you look at it and correct me if I'm wrong? If not then I can help with some parts.
This may not be a bug in em-websocket, but rather a bug in Safari (my current hunch since it doesn't work with pywebsocket or using stunnel in front of em-websocket). I'm reporting here so that others don't scratch their heads needlessly, and in case someone thinks of something I missed!
I've submitted more details to the WebKit bugtracker https://bugs.webkit.org/show_bug.cgi?id=41419
This was repaired in commit after release 0.0.5 gem, but still no new gem available - please bump new version.
I've been having some encoding issues. By default, messages coming in are ASCII-8BIT. When I use the json gem's to_json
I get an error:
server.rb:18:in `encode': "\xC3" from ASCII-8BIT to UTF-8 (Encoding::UndefinedConversionError)
The solution to this is to force UTF-8 encoding on the message like so:
ws.onmessage { |msg|
puts "Received message: #{msg}"
msg = msg.force_encoding('UTF-8')
msg = JSON.parse([msg].to_json)[0]
ws.send msg
}
This however gives me:
em-websocket/lib/em-websocket/connection.rb:166:in `send': incompatible character encodings: UTF-8 and ASCII-8BIT (Encoding::CompatibilityError)
Which is:
def send(data)
debug [:send, data]
send_data("\x00#{data}\xff")
end
I could solve the issue by replacing that with:
def send(data)
debug [:send, data]
send_data("\x00".force_encoding('UTF-8') + data + "\xff".force_encoding('UTF-8'))
end
But I am not sure if that could break other things.
In drafts 04 and above all data sent by browsers must be XORed with 4 random bytes which is described in http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-06#section-4.2
I wrote the simplest code that could possibly work to do this. It works, but the performance stinks. Unmasking 1MB (1000 1K strings) takes 1.75s on my laptop. Does anyone have any bright ideas about nice ways to rewrite the code in https://github.com/igrigorik/em-websocket/blob/draft5and6/lib/em-websocket/masking04.rb ?
Please contribute to https://gist.github.com/902183
Hi. I use crhome & it was work, but don't know why when I update em-websocket & faced wired problem.
[[:error,#<EventMachine::WebSocket::WebSocketError: Protocol version 13 not supported>]]
here is the extra information from header
[[:inbound_headers,"GET /private/e4153380bd79444d01f67689ed3ebbca HTTP/1.1\r\nUpgrade: websocket\r\nConnection:
Upgrade\r\nHost: localhost:9002\r\nOrigin: http://localhost:8080\r\nSec-WebSocket-Key: jkNkOqF/nqA1QqjlTVJRLg==\r\nSec-WebSocket-Version:13\r\nCookie:_meetcliv_session=BAh7DEkiD3Nlc3Npb25faWQGOgZFRiIlMzUzMzZiYWIyMDA1MjBhNGJhNmY4NTBhMjI4YTQ5MmJJIhBfY3NyZl90b2tlbgY7AEZJIjFhdHZQS2JUdXR6UXB2YlRydHhlZDBmOWdJMc1Z3M5azAyeEFVci9zTDJJPQY7AEZJIhFhY2Nlc3NfdG9rZW4GOwBGSSJnQUFBQjBBbDdpcjRBQkFNQjBkSHBYRzRYU3p6U0p2eVg4WXhqZWhnU1pDOWJYbHdJbHZGZllaQlE0QWVudFpDUzJCSzg3ekhNV3psVnRTVjM2eGhuSDJpTjQ0SDdaQ2h3WkQGOwBGSSIKYWRtaW4GOwBGaQZJIgxhY2NvdW50BjsARkkiGGhpdGFrZWFuZUBnbWFpbC5jb20GOwBUSSIIbWlkBjsARmkC3gFJIglmYmlkBjsARkkiDjc1MjU4NzgwNwY7AFQ%3D--b7c3194be5b5a5fda5fa0cf5338f3a602f906b26\r\n\r\n"]]
any clue?
I tried to use socket.io.js (https://github.com/LearnBoost/Socket.IO) in a web page and make it connect to a em-websocket server. There's a simple server implementation in the examples directory of https://github.com/igrigorik/em-websocket. The server said that Connection and upgrade headers were required, apparently in the handshake. These are the ruby output:
[[:initialize]]
[[:receive_data, "GET /socket.io/xhr-polling//1300861225825 HTTP/1.1\r\nHost: localhost:8080\r\nConnection: keep-alive\r\nOrigin: null\r\nUser-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16\r\nAccept: /\r\nAccept-Encoding: gzip,deflate,sdch\r\nAccept-Language: zh-CN,zh;q=0.8\r\nAccept-Charset: GBK,utf-8;q=0.7,*;q=0.3\r\n\r\n"]]
[[:inbound_headers, "GET /socket.io/xhr-polling//1300861225825 HTTP/1.1\r\nHost: localhost:8080\r\nConnection: keep-alive\r\nOrigin: null\r\nUser-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.151 Safari/534.16\r\nAccept: /\r\nAccept-Encoding: gzip,deflate,sdch\r\nAccept-Language: zh-CN,zh;q=0.8\r\nAccept-Charset: GBK,utf-8;q=0.7,*;q=0.3\r\n\r\n"]]
[[:error, #<EventMachine::WebSocket::HandshakeError: Connection and Upgrade headers required>]]
Error: Connection and Upgrade headers required
[[:unbind, :connection]]
Any objections if I release the current master (0c94287) as 0.3.0?
There are a few optimisations we can make to the draft 6 support, but it's been running for a while on Pusher and seems solid enough.
When debug is disabled, em-websocket will swallow all exceptions, even critical stuff like non-existent classes. I suspect that the rescue in EventMachine::WebSocket::Connection is causing this. It should only catch those exceptions that are relevant.
Yes, there is the debug option, but that includes too much information.
Hi, I need to close a connection with specific user, for example in onopen callback if authentication fails. How can I do this?
Perhaps this issue already solved?
I use web-socket-js for IE which connect via flash.
but get no response, I asked web-socket-js and they said it should be support by default.
just wondering any body also face this problem?
@DaTa[pointer].to_i is used in Framing76::process_data().
Hi,
For a large number of connections, em-websocket keeps leaking memory eventually reaching 100% memory usage.
The more connections and/or the large data, the faster this happens.
Below is a gist with a simple server, and a benchmark to simulate 10k concurrent connections, notice that you can reduce the number of connections and/or data, but the memory usage will still be going up (albeit slower).
https://gist.github.com/953250
ruby server.rb
ruby benchmark.rb
This was run in machine using Red Hat Enterprise 5.6.
I reviewed the code searching for the cause, but couldnt find it :( Is this a known issue or something that can be fixed?
It is currently possible to frame a request that would cause an arbitrarily large buffer to accumulate in memory. It is also possible to send a frame such as "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00" which would cause a "bignum too big to convert into `long'" exception to be raised. I have seen the latter type occur in the wild with pusherapp.
Currently the WebSocket spec does not specify a maximum message length, however it seems reasonable to limit this. The following commit limits the frame to 10MB and if triggered calls the onerror callback and closes the connection. This solves both issues.
http://github.com/mloughran/em-websocket/commit/e62f226fb6fdc6b6cee64080a82303eca05923c9
eventmachine-0.12.10/lib/em/connection.rb:39:in block in new': undefined method
associate_callback_target' for #EventMachine::WebSocket::Connection:0x00000100b9dfb0 (NoMethodError)
I was trying to use em-websocket to do a chat program. Instead of letting everybody connect to the server, I would like to authenticate users when they try to make connections. I was wondering if that could be done when the user first tries to make a connection.
Is it possible to use a url with parameters to connect, like ws://localhost:8080/?param1=user¶m2=password ?
ws.onmessage do |msg| puts msg.encoding end
It produces "US-ASCII" even when using Ruby 1.9. It would be much better to produce a UTF-8 string (as WebSocket protocol mandates text messages under UTF-8).
sid = @channel.subscribe { |msg| ws.send msg }
Is it a block that's called each time we push onto that channel?
I want to be able to ws.send a message to a specific user vs all users. Should I create a EM::Channel for each websocket connection and keep track of the channels or create one channel and keep track of subscribers?
Hey there, I really like this project and it got me excited enough to implement one of my ideas with it. Problem is, I wired everything up just fine (I think), but when running the server I get absolutely no output, and I can't connect from Chrome.
Here's the server, just a basic one (I run it with ruby server.rb, hope that's right):
require 'em-websocket'
puts "Starting server"
EventMachine.run {
EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080, :debug => true) do |ws|
ws.onopen { ws.send "Hello Client!" }
ws.onmessage { |msg| ws.send "Pong: #{msg}" }
ws.onclose { puts "WebSocket closed" }
end
}
Now even though debug is true, all I get is
[root@dyno test]# ruby -v
ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-linux]
[root@dyno test]# ruby server.rb
Starting server
Then it hangs with no output. The client code is also basic:
// Open the WebSocket connection
var ws = new WebSocket("ws://myserver.org:8080/");
ws.onopen = function(evt) { socketOpen(evt); }
ws.onclose = function(evt) { socketClose(evt); }
ws.onmessage = function(evt) { socketMessage(evt); }
ws.onerror = function(evt) { socketError(evt); }
This runs in Chrome 11.0.696 but can't establish a connection (trying to use the native Chrome implementation, no flash)
Hi,
I have an EM WebSocket running:
EventMachine::WebSocket.start({
:host => '0.0.0.0',
:port => 443,
:secure => true,
:tls_options => {
:private_key_file => 'tmp/server.key',
:cert_chain_file => 'tmp/server.crt'
}
}) do |ws|
ws.onopen {ws.send "connected to WebSocket with port 443, SSH"}
ws.onmessage { |msg| puts "got message #{msg}"}
ws.onclose { ws.send "Websocket was closed"}
end
I can connect to this using a ws://192.168.2.2:443
EventMachine.run {
http = EventMachine::HttpRequest.new("ws://amazon:443").get :timeout => 0
http.errback { puts "oops" }
http.callback {
puts "WebSocket connected!"
http.send("Hello client")
}
http.stream { |msg|
puts "Recieved: #{msg}"
http.send "Pong: #{msg}"
}
}
The Client connection code request and server response are noted on my console, but this is not a wss:// client connecting!! Uh oh... me thinks.
I switch to Javascript quickly to verify that wss:// using Chrome connects and this is re-assuring, whereas ws:// with Javascript did not, also reassuring that my SSL WebSocket server is functioning as intended.
With em-http-request sending wss:// I get the callback Websocket Connected! but the following send is ignored and nothing makes it to the server. Is there anything obvious that I am doing wrong here? I am hoping to get wss:// via a ruby client and I am also curious as to why ws:// seems to work on an SSL connection.
I suppose this is due to em-http-request not handling SSL directly and that I have to setup a tunnel to my websocket server using SSH or something??
I am confused now. Summary: Is there a clean nice way to use WSS:// with Ruby?
Any tips most appreciated..!!!!
Apparently, to support operating behind an haproxy you can't wait for the nonce string. see the gist: https://gist.github.com/729332
I'm not 100% sure about how this works but reading references in other servers: https://github.com/Worlize/Socket.IO-node/commit/b9bef2b2660526768c1cd21ad1ef77f72ec0fc62
It appears the nonce e.g. the request body is optional? From my gist it's clear that it's not included when proxied behind HAProxy... I'm just getting setup with em-* so will take a shot at a patch but thought it'd be helpful to get some more eyes on this...
I'm using the following to manage haproxy locally for testing: https://gist.github.com/729335
I've messed around with changing the websock from mode http and mode tcp - believe with the changes in the other mentioned servers mode http would/can work... i've also tested running on the same or different ports.
Here is the commit which should fix it:
http://github.com/mcolyer/em-websocket/commit/8ae3e9d1176f0635fffc5da579be50c12eb77a5c
Protocol diff: http://tools.ietf.org/rfcdiff?url2=draft-ietf-hybi-thewebsocketprotocol-07
There is a firefox alpha available for testing: http://www.ietf.org/mail-archive/web/hybi/current/msg07362.html
Hi Ilya,
I am doing a little testing with em-websocket and noticing a strange behavior. Here is my setup, and you can see the code here:
date
" (I have backticks around the date command, but github message formatting is stripping that out)Each time I run msg-inject.rb I see the current date show up in my browser client. All good.
Now here is the weirdness:
And so on... with the number of skips incrementing by one after each reload.
So if I reload the client.rb nine times, you guessed it, I only see every ninth message in the browser client. This behaves exactly the same in both chrome and webkit.
And if I restart the server.rb, It all resets, and I'll see every incoming message, until I reload client.html and get the incrementing 'skipping' behavior again.
Thoughts?
I'm getting this with some users when they try to connect. Works on some people's Firefox4, and doesn't work on other people's. Frustrating!
EventMachine::WebSocket::HandshakeError: Invalid HTTP header
[[:initialize]]
[[:receive_data,
"GET /?room=ary_reads&p=KpQzawYSpbio&u=AJ Test HTTP/1.1\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nHost: myhost.org:4442\r\nOrigin: http://myhost.org:3001\r\nCookie: read_to=1300999519389; visits=_4196565__2427159__3877855__2196184__4467059__4489769__4610009__4613793__4616659__4618821__4635255__4640989__4652339__4655081__4658289__4668459__4676893__4705833__4714487__4734261__4737825__4758763__4763665__4; sdata=AJ+Morelli_okaa-true_p-g_okxx-true_JH-asdf; remember_me=asdf%3D%3D--asdf; clk=1\r\nSec-WebSocket-Key1: 21nd8 75 71 x: t 03 2%\r\nSec-WebSocket-Key2: 23 9_ 14@B9 N 29R U1 2G M\r\n\r\n\adsf\324\335\313"]]
[[:inbound_headers,
"GET /?room=ary_reads&p=KpQzawYSpbio&u=AJ Test HTTP/1.1\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nHost: myhost.org:4442\r\nOrigin: http://myhost.org:3001\r\nCookie: read_to=1300999519389; visits=_4196565__2427159__3877855__2196184__4467059__4489769__4610009__4613793__4616659__4618821__4635255__4640989__4652339__4655081__4658289__4668459__4676893__4705833__4714487__4734261__4737825__4758763__4763665__4; sdata=AJ+Morelli_okaa-true_p-g_okxx-true_JH-asdf; remember_me=asdf%3D%3D--asdf; clk=1\r\nSec-WebSocket-Key1: 21nd8 75 71 x: t 03 2%\r\nSec-WebSocket-Key2: 23 9_ 14@B9 N 29R U1 2G asdf5\313"]]
[[:error, #<EventMachine::WebSocket::HandshakeError: Invalid HTTP header>]
I'm using https://github.com/gimite/web-socket-js on the JS side. Any suggestions?
em-websocket fails if a HTTP GET request contains "sec-websocket-key1" instead of "Sec-WebSocket-Key1". The same for ANY header involved in websocket handshake.
This is not correct, HTTP header names are case insensitive always.
I'm having some problems using AMQP (message queuing) with EM-WebSocket.
Here's the basic idea:
EventMachine.run {
EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 8080) do |socket|
socket.onopen {
puts "WS connection opened"
AMQP.start(:host => "hostname") do
exchange = MQ.topic("exchange.name", :durable => true)
mq = MQ.new
mq.queue("queue.name").bind(exchange).subscribe do |m|
puts "handling new message: #{m}"
socket.send m
end
end
}
}
}
This works well enough, in that I see the browser (Chrome) connect to the socket server and that the socket server is able to consume messages from the AMQP queue. I run into problems though when multiple messages are waiting in the queue for pickup. Sometimes, it seems like all the messages are picked up, framed correctly, and sent through the socket to the browser. Other times, it seems like all the messages are picked up, but some number (1..n-1, n being the number of messages available) are framed correctly and sent through the socket to the browser.
In short, it seems like all the messages are correctly consumed from the queue, but not all are consistently sent to the browser (though the ones that are sent are framed correctly).
I have confirmed the actual data transmitted through the socket with a proxy, so I'm fairly confident this describes the real problem.
I ran "ruby multicast.rb" in examples/ and got "no such file to load -- lib/em-websocket". I suppose I could resolve it by running multicast.rb in parent dir instead but then what about the require of twitter/json_stream in there? Is it just non-working example code out of the box and I just need to require gems instead to make it work? Thanks.
Hi, method Framing76::process_data() says:
# This algorithm comes straight from the spec
# http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#section-4.2
But such section of the draft is about bytes sent fom the server to the client!:
4.2. Data framing
Once a WebSocket connection is established, the user agent must run
through the following state machine for the bytes sent by the server.
Shouldn't such method implemente section 5.3?:
5.3. Data framing
The server must run through the following steps to process the bytes
sent by the client.
This isn't really a specific issue, but I was playing around with making a badly behaved client and was sending the websocket server inappropriately large messages. The server uses minimal CPU when you send lots of small messages, but sending one large (hundreds of K) message per second maxes out the CPU.
I used the profiler to discover that over 99% of the time is spent in framing76.rb in line 66 which is String#slice! with a regexp argument...
I guess I'm wondering, how could we change the implementation so that it can handle arbitrarily large messages? Relatedly, I recall seeing a comment about limiting incoming messages to 10MB. Does that work, is it arbitrary, and could I lower it to protect against DOS from a rogue client?
Will be much more robust that the current implementation
WebSocket drafts >= 03 support ping/pong request/responses that are initiated from both the client and the server (see http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-06#section-4.5.2). em-websocket already supports responding to ping requests with a pong, however there is no server side API to send a ping.
Sending the ping is trivial, but it's not entirely clear what should be done with the response. A simple API could be
ws.ping(optional_string)
ws.onpong { |response_string| ... }
However it is possible to send a body in a ping request which must be echoed in the pong response. Therefore it would be possible to match up pings and pongs and expose something like
ws.ping {
# called on pong receipt
}
However as an application developer you're mostly concerned with the scenario when a pong does not arrive which suggests adding a timeout
ws.ping(10).errback {
# pong not received after 10s
}
Thoughts appreciated!
$ telnet localhost 8080
Trying 127.0.0.1...
Connected to xxxxx.com.
Escape character is '^]'.
GET /
at irb:
NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
from /opt/ruby-ee-1.8.7-2009.10/lib/ruby/gems/1.8/gems/em-websocket-0.0.5/lib/../lib/em-websocket/connection.rb:60:in new_request' from /opt/ruby-ee-1.8.7-2009.10/lib/ruby/gems/1.8/gems/em-websocket-0.0.5/lib/../lib/em-websocket/connection.rb:44:in
dispatch'
from /opt/ruby-ee-1.8.7-2009.10/lib/ruby/gems/1.8/gems/em-websocket-0.0.5/lib/../lib/em-websocket/connection.rb:31:in receive_data' from /opt/ruby-ee-1.8.7-2009.10/lib/ruby/gems/1.8/gems/eventmachine-0.12.10/lib/eventmachine.rb:256:in
run_machine'
from /opt/ruby-ee-1.8.7-2009.10/lib/ruby/gems/1.8/gems/eventmachine-0.12.10/lib/eventmachine.rb:256:in run' from /opt/ruby-ee-1.8.7-2009.10/lib/ruby/gems/1.8/gems/em-websocket-0.0.5/lib/../lib/em-websocket/websocket.rb:6:in
start'
from (irb):4
the onclose proc is never called because there is a typo in the instance variable name @onclode = blk on lib/em-websocket/connection.rb
Hi,
I found that, using a recent build of Google Chrome with em-websocket, connections were closed unexpectedly when the client sent data.
On line 69 of framing76.rb
if @DaTa[0] != 0x00
will always evaluate to true under Ruby 1.9, seemingly because @DaTa[0] is a String and 0x00 is a Fixnum. We either need to use to_i on the left side or quotes on the right.
I have yet to get the swf working with the test you provided under Firefox 3.6. Is it a known issue, or should it be working?
When Sec-WebSocket-Key1 or Sec-WebSocket-Key2 have $ char inside then request will have invalid response and connection fill fails. You can check this in code: lib/em-websocket/handler_factory.rb:5
HEADER = /^([^:]+):\s_([^$]+)/
Shouldn't it be:
HEADER = /^([^:]+):\s_(.+)$/
or $ character is forbidden? And if then please provide mi some link - http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76 don't inform about anything about "$" character.
I have successfully set up your example server and connected to it with Chrome 5 (mac,windows,linux) and Safari 5. However, there seems to be an issue with Chrome 6.
From this post, it looks like they have updated the protocol. Is this a change that needs to be made in em-websocket or an underlying gem?
http://blog.chromium.org/2010/06/websocket-protocol-updated.html
Hi,
I am trying out the idea of using channels in a simple app. I want my client to connect, establish a channel, and that way, I can be sure when I run a deferred task, the callback can send a message to the right channel.
I connect fine and logged the output from the server.
receive data
inbound headers
upgrade headers
send "connected"
receive data
message
send message
unbind connection
It is the last aspect of this handshake sequence that puzzles me. It looks as if the example unbinds and closes the connection once it has established the connection, received a message, sent a response.
I was thinking the connection would not close until either I explicitely sent a close from the server process, or the client actually disconnects.
Is there something I am reading into the example that is naive or mistaken? Any tips most appreciated. I really just want to establish a channel that persists as long as needed to complete some long running task on the server, alert the client when it is done, and then kill the connection. I am thinking this is how it probably already works, but I am not sure....
Thanks
I'd like to send messages only to particular connected computers (instead of to every computer using a EventMachine::Channel.new). Right now I'm monkey patching Connection#handshake
to pass the connection instance as a block argument to the @onopen
callback, mirroring the pattern of passing the connection to the @onclose
callback.
Happy to submit a patch/tests if you think this is valuable in general.
When running bundle install I get the following:
em-websocket at /Users/josh/.rvm/gems/ruby-1.9.2-p0/bundler/gems/em-websocket-96e85559334e did not have a valid gemspec.
This prevents bundler from installing bins or native extensions, but that may not affect its functionality.
The validation message from Rubygems was:
["README.rdoc"] are not files
When I run the code within examples/echo.rb in a Rails 3 initializer, it errors out on the first message with a process_data error. The same code runs fine outside Rails in a standalone program. What could be the issue here ?
em-websocket/framing76.rb:67:in `process_data': 134217727: /\A\x00([^\xff]*)\xff/ (SyntaxError)
(The above is a simple test case. It actually needs to be done within a thread so that the rest of the rails 3 initialization proceeds. This was just a test case which was on jRuby; client was chrome running examples/test).
On a related note, what is the best practice to integrate em-websocket within Rails 3 for just sending notification messages via a web service?
As followed the 07 draft to implement my own C++/Qt client, wich is working fine with your server.
But i think i spotted a little mistake in the draft interpretation, when sending more than 2 frame for the same packet,
http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07#section-4.4
I realized that in your implementation, you replace the opcode for each frame excluding the last one:
https://github.com/igrigorik/em-websocket/blob/master/lib/em-websocket/framing07.rb#L92
This cause every multi-frame packet containing middle frame to be considered "continuation" instead of "text" or "binary".
Instead you should store the frame_type only the first time, maybe like this:
@frame_type ||= frame_type
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.