webrick-websocket 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);