diff options
author | Peter Åstrand (astrand) <astrand@cendio.se> | 2013-11-28 09:07:30 +0100 |
---|---|---|
committer | Peter Åstrand (astrand) <astrand@cendio.se> | 2013-11-28 09:07:30 +0100 |
commit | f58b49fa084e85400dbff0c5de249c014543c507 (patch) | |
tree | 9d8c27b60ab8b49805e8cc156e81bcbd8b3c60e8 | |
parent | 131f9ea645ac6f00d98743a420d168033f99063a (diff) | |
parent | a61ae52610642ae58e914dda705df8bb9c8213ec (diff) | |
download | websockify-f58b49fa084e85400dbff0c5de249c014543c507.tar.gz |
Merge commit 'a61ae52610642ae58e914dda705df8bb9c8213ec'
* commit 'a61ae52610642ae58e914dda705df8bb9c8213ec':
fixed 1.8 compatibility bug for OpenSSL::SSL::SSLSocket#read_nonblock vs #readpartial tested in 1.8 and 2.0
adding SSL support and Ruby1.9 support
-rw-r--r-- | other/websocket.rb | 83 | ||||
-rwxr-xr-x | tests/echo.rb | 11 |
2 files changed, 69 insertions, 25 deletions
diff --git a/other/websocket.rb b/other/websocket.rb index 6bf9e63..d4551a6 100644 --- a/other/websocket.rb +++ b/other/websocket.rb @@ -9,11 +9,22 @@ # - http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10 require 'gserver' +require 'openssl' require 'stringio' require 'digest/md5' require 'digest/sha1' require 'base64' +unless OpenSSL::SSL::SSLSocket.instance_methods.index("read_nonblock") + module OpenSSL + module SSL + class SSLSocket + alias :read_nonblock :readpartial + end + end + end +end + class EClose < Exception end @@ -44,7 +55,17 @@ Sec-WebSocket-Accept: %s\r host = opts['listen_host'] || GServer::DEFAULT_HOST super(port, host) - + msg opts.inspect + if opts['server_cert'] + msg "creating ssl context" + @sslContext = OpenSSL::SSL::SSLContext.new + @sslContext.cert = OpenSSL::X509::Certificate.new(File.open(opts['server_cert'])) + @sslContext.key = OpenSSL::PKey::RSA.new(File.open(opts['server_key'])) + @sslContext.ca_file = opts['server_cert'] + @sslContext.verify_mode = OpenSSL::SSL::VERIFY_NONE + @sslContext.verify_depth = 0 + end + @@client_id = 0 # Track client number total on class @verbose = opts['verbose'] @@ -53,6 +74,18 @@ Sec-WebSocket-Accept: %s\r def serve(io) @@client_id += 1 + msg self.inspect + if @sslContext + msg "Enabling SSL context" + ssl = OpenSSL::SSL::SSLSocket.new(io, @sslContext) + #ssl.sync_close = true + #ssl.sync = true + msg "SSL accepting" + ssl.accept + io = ssl # replace the unencrypted handle with the encrypted one + end + + msg "initializing thread" # Initialize per thread state t = Thread.current @@ -61,7 +94,7 @@ Sec-WebSocket-Accept: %s\r t[:recv_part] = nil t[:base64] = nil - puts "in serve, client: #{t[:client].inspect}" + puts "in serve, client: #{t[:my_client_id].inspect}" begin t[:client] = do_handshake(io) @@ -85,12 +118,12 @@ Sec-WebSocket-Accept: %s\r if @verbose then print token; STDOUT.flush; end end - def msg(msg) - puts "% 3d: %s" % [Thread.current[:my_client_id], msg] + def msg(m) + printf("% 3d: %s\n", Thread.current[:my_client_id] || 0, m) end - def vmsg(msg) - if @verbose then msg(msg) end + def vmsg(m) + if @verbose then msg(m) end end # @@ -110,12 +143,11 @@ Sec-WebSocket-Accept: %s\r def unmask(buf, hlen, length) pstart = hlen + 4 - mask = buf[hlen...hlen+4] + mask = buf[hlen...hlen+4].each_byte.map{|b|b} data = buf[pstart...pstart+length] #data = data.bytes.zip(mask.bytes.cycle(length)).map { |d,m| d^m } - for i in (0...data.length) do - data[i] ^= mask[i%4] - end + i=-1 + data = data.each_byte.map{|b| i+=1; (b ^ mask[i % 4]).chr}.join("") return data end @@ -237,7 +269,7 @@ Sec-WebSocket-Accept: %s\r while t[:send_parts].length > 0 buf = t[:send_parts].shift - sent = t[:client].send(buf, 0) + sent = t[:client].write(buf) if sent == buf.length traffic "<" @@ -257,7 +289,7 @@ Sec-WebSocket-Accept: %s\r closed = false bufs = [] - buf = t[:client].recv(@@Buffer_size) + buf = t[:client].read_nonblock(@@Buffer_size) if buf.length == 0 return bufs, "Client closed abrubtly" @@ -286,10 +318,10 @@ Sec-WebSocket-Accept: %s\r end end else - if buf[0...2] == "\xff\x00": + if buf[0...2] == "\xff\x00" closed = "Client sent orderly close frame" break - elsif buf[0...2] == "\x00\xff": + elsif buf[0...2] == "\x00\xff" buf = buf[2...buf.length] continue # No-op frame elsif buf.count("\xff") == 0 @@ -308,7 +340,7 @@ Sec-WebSocket-Accept: %s\r bufs << frame['payload'] - if frame['left'] > 0: + if frame['left'] > 0 buf = buf[-frame['left']...buf.length] else buf = '' @@ -328,10 +360,10 @@ Sec-WebSocket-Accept: %s\r end buf, lenh, lent = encode_hybi(msg, opcode=0x08, base64=false) - t[:client].send(buf, 0) + t[:client].write(buf) elsif t[:version] == "hixie-76" buf = "\xff\x00" - t[:client].send(buf, 0) + t[:client].write(buf) end end @@ -344,16 +376,21 @@ Sec-WebSocket-Accept: %s\r raise EClose, "ignoring socket not ready" end - handshake = sock.recv(1024, Socket::MSG_PEEK) - #msg "Handshake [#{handshake.inspect}]" + handshake = "" + msg "About to read from sock [#{sock.inspect}]" + handshake = sock.read_nonblock(1024) + msg "Handshake [#{handshake.inspect}]" - if handshake == "" + if handshake == nil or handshake == "" raise(EClose, "ignoring empty handshake") else stype = "Plain non-SSL (ws://)" scheme = "ws" + if sock.class == OpenSSL::SSL::SSLSocket + stype = "SSL (wss://)" + scheme = "wss" + end retsock = sock - sock.recv(1024) end h = t[:headers] = {} @@ -365,7 +402,7 @@ Sec-WebSocket-Accept: %s\r hsplit = hline.match(/^([^:]+):\s*(.+)$/) h[hsplit[1].strip.downcase] = hsplit[2] end - #puts "Headers: #{h.inspect}" + puts "Headers: #{h.inspect}" unless h.has_key?('upgrade') && h['upgrade'].downcase == 'websocket' @@ -445,7 +482,7 @@ Sec-WebSocket-Accept: %s\r if t[:path] then msg "Path: '%s'" % [t[:path]] end #puts "sending reponse #{response.inspect}" - retsock.send(response, 0) + retsock.write(response) # Return the WebSocket socket which may be SSL wrapped return retsock diff --git a/tests/echo.rb b/tests/echo.rb index ea34db5..6a5493c 100755 --- a/tests/echo.rb +++ b/tests/echo.rb @@ -50,10 +50,17 @@ class WebSocketEcho < WebSocketServer end end -port = ARGV[0].to_i +port = ARGV[0].to_i || 8080 puts "Starting server on port #{port}" +server_cert = nil +server_key = nil +if ARGV.length > 2 + server_cert = ARGV[1] + server_key = ARGV[2] +end -server = WebSocketEcho.new('listen_port' => port, 'verbose' => true) +server = WebSocketEcho.new('listen_port' => port, 'verbose' => true, + 'server_cert' => server_cert, 'server_key' => server_key) server.start server.join |