From mboxrd@z Thu Jan 1 00:00:00 1970 Delivered-To: chneukirchen@gmail.com Received: by 10.142.191.1 with SMTP id o1cs60104wff; Fri, 11 Dec 2009 12:20:04 -0800 (PST) Received: from mr.google.com ([10.115.100.14]) by 10.115.100.14 with SMTP id c14mr1398024wam.8.1260562803775 (num_hops = 1); Fri, 11 Dec 2009 12:20:03 -0800 (PST) Received: by 10.115.100.14 with SMTP id c14mr187810wam.8.1260562802236; Fri, 11 Dec 2009 12:20:02 -0800 (PST) X-BeenThere: rack-devel@googlegroups.com Received: by 10.114.187.1 with SMTP id k1ls451511waf.0.p; Fri, 11 Dec 2009 12:20:00 -0800 (PST) Received: by 10.115.114.6 with SMTP id r6mr398381wam.10.1260562799441; Fri, 11 Dec 2009 12:19:59 -0800 (PST) Received: by 10.115.114.6 with SMTP id r6mr398380wam.10.1260562799413; Fri, 11 Dec 2009 12:19:59 -0800 (PST) Return-Path: Received: from dcvr.yhbt.net (dcvr.yhbt.net [64.71.152.64]) by gmr-mx.google.com with ESMTP id 23si471147pxi.0.2009.12.11.12.19.59; Fri, 11 Dec 2009 12:19:59 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of normalperson@yhbt.net designates 64.71.152.64 as permitted sender) client-ip=64.71.152.64; Received: from localhost (dcvr.yhbt.net [127.0.0.1]) (using TLSv1 with cipher DHE-RSA-AES128-SHA (128/128 bits)) (No client certificate requested) by dcvr.yhbt.net (Postfix) with ESMTPSA id 0D4261F43D; Fri, 11 Dec 2009 20:19:59 +0000 (UTC) Date: Fri, 11 Dec 2009 20:19:58 +0000 From: Eric Wong To: rack-devel@googlegroups.com Subject: [ANN/RFC] LMGTWTY - Web Sockets for Rack+Rainbows! Message-ID: <20091211201958.GD2121@dcvr.yhbt.net> MIME-Version: 1.0 User-Agent: Mutt/1.5.18 (2008-05-17) X-Original-Authentication-Results: gmr-mx.google.com; spf=pass (google.com: best guess record for domain of normalperson@yhbt.net designates 64.71.152.64 as permitted sender) smtp.mail=normalperson@yhbt.net X-Original-Sender: normalperson@yhbt.net Reply-To: rack-devel@googlegroups.com Precedence: list Mailing-list: list rack-devel@googlegroups.com; contact rack-devel+owners@googlegroups.com List-ID: List-Post: , List-Help: , List-Archive: X-Thread-Url: http://groups.google.com/group/rack-devel/t/1214d460ed982748 X-Message-Url: http://groups.google.com/group/rack-devel/msg/345e397e4d71f6de Sender: rack-devel@googlegroups.com List-Unsubscribe: , List-Subscribe: , Content-Type: text/plain; charset=us-ascii Content-Disposition: inline Hi all, I've started getting Web Sockets going for Rainbows! (git version-only). If people like it, maybe this can be put into Rack itself (and it'll have a better name and more exposure). === Demo first Just grab the echo_client.py from pywebsocket here: http://pywebsocket.googlecode.com/svn/trunk/src/example/echo_client.py (tested with r139) And run: $ python echo_client.py -p 9999 -s yhbt.net $ python echo_client.py -p 9999 -s yhbt.net -m "hello,world" ... == Code http://git.bogomips.org/cgit/lmgtwty.git git://git.bogomips.org/lmgtwty.git # s/lmgtwty/rainbows/ to get rainbows.git The only change Rainbows! I made is to expose the client IO object via: # if it becomes part of the Rack spec, then s/h/r/ :> env["hack.io"] = client Right now, env["hack.io"] needs to respond to #readpartial and #write (so it's compatible with core Ruby IO objects and also with Rainbows::Fiber::IO). == LMGTWTY code walk-through The entirety of LMGTWTY was done on a whim way past my normal bedtime. Feedback on design/code/semantics would be greatly appreciated. === Lmgtwty::IO is a wrapper for the "raw" env["hack.io"] object It responds to #gets and #write while encoding/decoding the WebSocket framing transparently to the application. For reading, Lmgtwty::IO#gets() returns a single web socket frame. So it removes the first and last bytes of (\x00 frame \xff) For writing, Lmgtwty::IO#write(buf) - writes the contents of +buf+ to the client socket, transparently framing it with "\x00" and "\xff" bytes. === Lmgtwty::Request is a subclass of Rack::Request It adds a few helper methods to handle handshaking and using Lmgtwty::IO. Handshaking/responses can't be reliably done with the normal Rack resmost Rack servers/handlers yet because client (and last I checked, the RFC) currently rely on a hard-coded response where case-sensitivity and header order matters(!). Web Sockets does NOT use HTTP chunked encoding, so the normal TeeInput class in Unicorn/Rainbows! won't work. Also the rewindable requirement of "rack.input" gives it unnecessary overhead for long-running requests. The handshake uses the "raw" env["hack.io"] object for writes, afterwards using the Lmgtwty::IO-wrapped version of env["hack.io"] is required for framing/deframing Web Socket messages. == Caveats * Web Sockets is still evolving quickly and may change incompatibly... * iobuffer is a C extension gem dependency, I expect replacing it with a pure Ruby Array of Strings with minimal/no performance loss since the Strings are expected to be short anyways. * I've only tested this with the python command-line client. I avoid GUIs as much as possible and using Chrome at this stage (without a Vimperator equivalent) would cause me too much pain. -- Eric Wong