webrick-websocket 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 13e2c3b9e1af9682919f83e722d34194fc7dd442
4
+ data.tar.gz: ed338c06ac763cc2bb992714dcdf44c5ed077094
5
+ SHA512:
6
+ metadata.gz: 1704b541060941ef57644532befe20c718ba0fb0d2f2bc8787b1113423001348e4b8da31de1cfd9fae85aa1e45e4712042729dfcb936c6b02bff0818b74ee0d7
7
+ data.tar.gz: 747b5dcfe41c9fd5b07ddd096cf1b1a88dd053c91b8f44098182f7b650b9f6c2232b7648ba1c3f4336e4f9d912fe538bf01383cc11944e30b6b65e06aa6cac41
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in webrick-websocket.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Kilobyte22
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # Webrick::Websocket
2
+
3
+ This gem allows you to use Websocket in your WEBrick web application.
4
+ First, create a HTTPServer with websocket support:
5
+
6
+ ```ruby
7
+ require 'webrick/websocket'
8
+ server = WEBrick::Websocket::HTTPServer.new(Port: 8080, DocumentRoot: File.dirname(__FILE))
9
+ ```
10
+
11
+ having that done do anything you would do with a regular WEBrick instance.
12
+ However, you can mount a Websocket Servlet.
13
+
14
+ ```ruby
15
+ class MyServlet < WEBrick::Websocket::Servlet
16
+ def select_protocol(available)
17
+ # method optional, if missing, it will always select first protocol.
18
+ # Will only be called if client actually requests a protocol
19
+ available.include?('myprotocol') ? 'myprotocol' : nil
20
+ end
21
+
22
+ def socket_open(sock)
23
+ # optional
24
+ sock.puts 'Welcome' # send a text frame
25
+ def
26
+
27
+ def socket_close(sock)
28
+ puts 'Poof. Socket gone.'
29
+ end
30
+
31
+ def socket_text(sock, text)
32
+ puts "Client sent '#{text}'"
33
+ end
34
+ end
35
+
36
+ server.mount('/websocket', MyServlet)
37
+ ```
38
+
39
+ Aaaaand lets start the server
40
+
41
+ ```ruby
42
+ server.start
43
+ ```
44
+
45
+ Note, that it will always use the same servlet instance for a single socket and that each socket has its own servlet instance.
46
+ This means you can safely use instance variables inside the servlet to store the sockets state
47
+
48
+ ## Installation
49
+
50
+ Add this line to your application's Gemfile:
51
+
52
+ ```ruby
53
+ gem 'webrick-websocket'
54
+ ```
55
+
56
+ And then execute:
57
+
58
+ $ bundle
59
+
60
+ Or install it yourself as:
61
+
62
+ $ gem install webrick-websocket
63
+
64
+ ## Usage
65
+
66
+ TODO: Write usage instructions here
67
+
68
+ ## Contributing
69
+
70
+ 1. Fork it ( https://github.com/[my-github-username]/webrick-websocket/fork )
71
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
72
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
73
+ 4. Push to the branch (`git push origin my-new-feature`)
74
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,6 @@
1
+ require 'webrick/httprequest'
2
+ module WEBrick
3
+ class HTTPRequest
4
+ attr_reader :socket
5
+ end
6
+ end
@@ -0,0 +1,101 @@
1
+ module WEBrick
2
+ module Websocket
3
+ ## :nodoc:
4
+ class Frame
5
+ @@ops_rev = {
6
+ cont: 0,
7
+ text: 1,
8
+ binary: 2,
9
+ close: 8,
10
+ ping: 9,
11
+ pong: 10
12
+ }
13
+ @@ops = {}
14
+ @@ops_rev.each do |op, id|
15
+ @@ops[id] = op
16
+ end
17
+ attr_reader :payload, :op
18
+ def self.parse(socket, prev = nil)
19
+ Frame.new(nil).parse(socket, prev)
20
+ end
21
+
22
+ def initialize(op, data = '')
23
+ @op = op
24
+ @payload = data
25
+ end
26
+
27
+ def parse(socket, prev)
28
+ head = socket.read(1).unpack('C')[0]
29
+ @fin = head & 0b10000000 > 0
30
+ op = head & 0b00001111
31
+ @op = @@ops[op]
32
+ @op = prev if @op == :cont
33
+ head = socket.read(1).unpack('C')[0]
34
+ @masked = head & 0b10000000 > 0
35
+ len = head & 0b01111111
36
+ if len > 125
37
+ long = len == 127
38
+ len = socket.read(long ? 8 : 2).unpack(long ? 'Q' : 'S')[0]
39
+ end
40
+ @mask = socket.read(4).unpack('C4') if @masked
41
+ @payload = socket.read(len)
42
+ @payload = mask(@payload)
43
+ self
44
+ end
45
+
46
+ def control?
47
+ @@ops_rev[@op] > 7
48
+ end
49
+ def close?
50
+ is(:close)
51
+ end
52
+ def binary?
53
+ is(:binary)
54
+ end
55
+ def text?
56
+ is(:text)
57
+ end
58
+ def ping?
59
+ is(:ping)
60
+ end
61
+ def pong?
62
+ is(:pong)
63
+ end
64
+ def fin?
65
+ @fin
66
+ end
67
+
68
+ def write(sock)
69
+ head = 0b10000000 + @@ops_rev[@op]
70
+ puts head.to_s(2)
71
+ sock.write([head].pack('C'))
72
+ @len = @payload.length
73
+ lendata = if @len > 125
74
+ if @len > 65535
75
+ [127, @len].pack('CQ')
76
+ else
77
+ [126, @len].pack('CS')
78
+ end
79
+ else
80
+ [@len].pack('C')
81
+ end
82
+ sock.write(lendata)
83
+ sock.write(@payload)
84
+ sock.flush
85
+ end
86
+
87
+ private
88
+ def is(what)
89
+ @op == what
90
+ end
91
+
92
+ def mask(what)
93
+ i = -1
94
+ what.unpack('C*').map do |el|
95
+ i += 1
96
+ el ^ @mask[i % 4]
97
+ end.pack('C*')
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,51 @@
1
+ require 'digest/sha1'
2
+ require 'webrick/websocket/socket'
3
+ require 'webrick/websocket/servlet'
4
+
5
+ module WEBrick
6
+ module Websocket
7
+ ##
8
+ # A HTTP Server with Websocket Support
9
+ class HTTPServer < WEBrick::HTTPServer
10
+ def initialize(*args, &block)
11
+ super(*args, &block)
12
+ end
13
+
14
+ ## :nodoc:
15
+ def service(req, res)
16
+ if req.unparsed_uri == "*"
17
+ if req.request_method == "OPTIONS"
18
+ do_OPTIONS(req, res)
19
+ raise HTTPStatus::OK
20
+ end
21
+ raise HTTPStatus::NotFound, "`#{req.unparsed_uri}' not found."
22
+ end
23
+
24
+ servlet, options, script_name, path_info = search_servlet(req.path)
25
+ raise HTTPStatus::NotFound, "`#{req.path}' not found." unless servlet
26
+ req.script_name = script_name
27
+ req.path_info = path_info
28
+ si = servlet.get_instance(self, *options)
29
+ @logger.debug(format("%s is invoked.", si.class.name))
30
+ if req['upgrade'] == 'websocket' && si.is_a?(Servlet)
31
+ req.header.each do |k, v|
32
+ puts "#{k} -> #{v}"
33
+ end
34
+ res.status = 101
35
+ key = req['Sec-WebSocket-Key']
36
+ res['Sec-WebSocket-Accept'] = Digest::SHA1.base64digest(key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')
37
+ res['Sec-WebSocket-Protocol'] = si.select_protocol(req['Sec-WebSocket-Protocol']).split(/[ ,\t]+/) if req['Sec-WebSocket-Protocol']
38
+ res['upgrade'] = 'websocket'
39
+ res.setup_header
40
+ res.instance_variable_get(:@header)['connection'] = 'upgrade'
41
+ res.send_header(req.socket)
42
+ sock = WEBrick::Websocket::Socket.new(req.socket, si, @logger)
43
+ sock.run
44
+ res.request_line = nil
45
+ else
46
+ si.service(req, res)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,9 @@
1
+ module WEBrick
2
+ module Websocket
3
+ class Servlet < WEBrick::HTTPServlet::AbstractServlet
4
+ def select_protocol(input)
5
+ input.first
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,78 @@
1
+ require 'webrick/websocket/frame'
2
+
3
+ module WEBrick
4
+ module Websocket
5
+ class Socket
6
+ def initialize(sock, handler, logger)
7
+ @prev = nil
8
+ @sock = sock
9
+ @handler = handler
10
+ @open = true
11
+ @logger = logger
12
+ handle(:open)
13
+ end
14
+
15
+ def puts(data)
16
+ send_frame(Frame.new(:text, data))
17
+ end
18
+
19
+ def close
20
+ handle(:close)
21
+ if @open
22
+ @open = false
23
+ send_frame(Frame.new(:close))
24
+ end
25
+ end
26
+
27
+ def send_frame(frame)
28
+ @logger.debug("Websocket Frame Sent: #{frame.op.to_s}(#{frame.payload.length} Bytes)")
29
+ if frame.close? && @open
30
+ close
31
+ else
32
+ frame.write(@sock)
33
+ end
34
+ end
35
+
36
+ def run
37
+ handle_packet while @open
38
+ end
39
+
40
+ private
41
+
42
+ def handle_packet
43
+ payload = ''
44
+ while @open
45
+ frame = read_frame
46
+ @prev = frame.op
47
+ if frame.control?
48
+ case @prev
49
+ when :ping
50
+ send_frame(Frame.new(:pong))
51
+ when :close
52
+ @open = false
53
+ close
54
+ end
55
+ else
56
+ payload += frame.payload
57
+ break if frame.fin?
58
+ end
59
+ end
60
+ return unless @open
61
+ handle(@prev, payload)
62
+ handle(:data, @prev, payload)
63
+ end
64
+
65
+ def handle(op, *args)
66
+ cb = 'socket_' + op.to_s
67
+ @logger.debug("Websocket Callback: #{@handler.class.name}##{cb}")
68
+ @handler.send(cb, self, *args) if @handler.respond_to?(cb)
69
+ end
70
+
71
+ def read_frame
72
+ f = Frame.parse(@sock, @prev)
73
+ @logger.debug("Websocket Frame Received: #{f.op.to_s}(#{f.payload.length} Bytes)")
74
+ f
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,5 @@
1
+ module WEBrick
2
+ module Websocket
3
+ VERSION = '0.0.1'
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'webrick/websocket/version'
2
+ require 'webrick'
3
+ require 'webrick/httprequest_patch'
4
+ require 'webrick/websocket/server'
5
+
6
+ module WEBrick
7
+ module Websocket
8
+
9
+ end
10
+ end
@@ -0,0 +1,45 @@
1
+ /*
2
+ * jQuery Web Sockets Plugin v0.0.1
3
+ * http://code.google.com/p/jquery-websocket/
4
+ *
5
+ * This document is licensed as free software under the terms of the
6
+ * MIT License: http://www.opensource.org/licenses/mit-license.php
7
+ *
8
+ * Copyright (c) 2010 by shootaroo (Shotaro Tsubouchi).
9
+ */
10
+ (function($){
11
+ $.extend({
12
+ websocketSettings: {
13
+ open: function(){},
14
+ close: function(){},
15
+ message: function(){},
16
+ options: {},
17
+ events: {}
18
+ },
19
+ websocket: function(url, s) {
20
+ var ws = WebSocket ? new WebSocket( url ) : {
21
+ send: function(m){ return false },
22
+ close: function(){}
23
+ };
24
+ $(ws)
25
+ .bind('open', $.websocketSettings.open)
26
+ .bind('close', $.websocketSettings.close)
27
+ .bind('message', $.websocketSettings.message)
28
+ .bind('message', function(e){
29
+ var m = $.parseJSON(e.originalEvent.data);
30
+ var h = $.websocketSettings.events[m.type];
31
+ if (h) h.call(this, m);
32
+ });
33
+ ws._settings = $.extend($.websocketSettings, s);
34
+ ws._send = ws.send;
35
+ ws.send = function(type, data) {
36
+ var m = {type: type};
37
+ m = $.extend(true, m, $.extend(true, {}, $.websocketSettings.options, m));
38
+ if (data) m['data'] = data;
39
+ return this._send(JSON.stringify(m));
40
+ };
41
+ $(window).unload(function(){ ws.close(); ws = null });
42
+ return ws;
43
+ }
44
+ });
45
+ })(jQuery);