webrocket 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6f34536b978cbb71a56ae12ea296681c3f09e1d1
4
+ data.tar.gz: 210d6e96989a3657513fabc93278250800bfe491
5
+ SHA512:
6
+ metadata.gz: 0e2587f050883ca1fee5a31c50b9a2c2e863aa2f4268e0bada83d66ddb159ef44be21aa1d8e8ff5e9cd358309f1b7f2654ce3283ad413647b82b6980625d58fb
7
+ data.tar.gz: 9aafb6ae9a6ae54dcd78a04e36222a06f7022c51bad724669d01b8d7c05ba5ee9be354455fffef982287d5536ea9298ff8127308b7bdbecc29ce13438c280c4e
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - 2.0.0
5
+ - 1.9.3
6
+ script:
7
+ - bundle exec rake build test
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in WEBrocket.gemspec
4
+ gemspec
@@ -0,0 +1,104 @@
1
+ [![Build Status](https://img.shields.io/travis/unak/WEBrocket.svg)](https://travis-ci.org/unak/WEBrocket)
2
+ [![Version ](https://img.shields.io/gem/v/webrocket.svg)](https://rubygems.org/gems/webrocket)
3
+
4
+ WEBrocket
5
+ =========
6
+
7
+ WebSocket extension for WEBrick.
8
+ This library is under development.
9
+
10
+ WebSocket specification is based on [RFC6455](http://www.rfc-editor.org/rfc/rfc6455.txt).
11
+
12
+
13
+ Requirements
14
+ ------------
15
+
16
+ + Ruby 1.9.3 or later.
17
+
18
+
19
+ Installation
20
+ ------------
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'WEBrocket'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install WEBrocket
33
+
34
+
35
+ How to use
36
+ ----------
37
+
38
+ require "webrick"
39
+ require "webrocket"
40
+
41
+ # write listner
42
+ class Listener
43
+ def on_open(websocket)
44
+ # do somthing
45
+ end
46
+
47
+ def on_close(websocket)
48
+ # do somthing
49
+ end
50
+
51
+ def on_recv(websocket, data, type)
52
+ # do something
53
+ end
54
+
55
+ def on_shutdown
56
+ # do something
57
+ end
58
+ end
59
+
60
+ server = WEBrick::HTTPServer.new
61
+ server.mount_websocket("sample", Listener.new, "test")
62
+ server.start
63
+
64
+ See sample directory for more details.
65
+
66
+
67
+ Limitations
68
+ -----------
69
+
70
+ + Fragmentation is not supported yet.
71
+ + TLS is not supported yet.
72
+
73
+
74
+ License
75
+ -------
76
+
77
+ Copyright (c) 2013 NAKAMURA Usaku usa@garbagecollect.jp
78
+
79
+ Redistribution and use in source and binary forms, with or without
80
+ modification, are permitted provided that the following conditions are met:
81
+
82
+ 1. Redistributions of source code must retain the above copyright notice,
83
+ this list of conditions and the following disclaimer.
84
+ 2. Redistributions in binary form must reproduce the above copyright notice,
85
+ this list of conditions and the following disclaimer in the documentation
86
+ and/or other materials provided with the distribution.
87
+
88
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
89
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
90
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
91
+ DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
92
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
93
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
94
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
95
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
96
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
97
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
98
+
99
+
100
+ Supplimentary
101
+ -------------
102
+
103
+ I've found that there are many projects named webrocket, but, sorry, I can't
104
+ stop naming this one the same name :)
@@ -0,0 +1,7 @@
1
+ # -*- Ruby -*-
2
+ require "bundler/gem_tasks"
3
+
4
+ desc "Runs test"
5
+ task :test do
6
+ ruby 'test/test_webrocket.rb', '-v'
7
+ end
@@ -0,0 +1,56 @@
1
+ require "base64"
2
+ require "digest/sha1"
3
+ require "webrocket/webrick-extender"
4
+ require "webrocket/websocket"
5
+ require "webrocket/version"
6
+
7
+ module WEBrocket
8
+ def self.attach(server, req, res, listener, subprotocol)
9
+ key = req["Sec-WebSocket-Key"]
10
+ unless key
11
+ res.status = WEBrick::HTTPStatus::RC_BAD_REQUEST
12
+ return
13
+ end
14
+ key += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
15
+ res["Sec-WebSocket-Accept"] = Base64.strict_encode64(Digest::SHA1.digest(key))
16
+
17
+ if req["Sec-WebSocket-Version"].to_i < 13
18
+ res.status = WEBrick::HTTPStatus::RC_BAD_REQUEST
19
+ res["Sec-WebSocket-Version"] = 13
20
+ return
21
+ end
22
+
23
+ if req["Sec-WebSocket-Protocol"]
24
+ protocols = req["Sec-WebSocket-Protocol"].split(/\s*,\s*/)
25
+ if protocols.include?(subprotocol)
26
+ res["Sec-WebSocket-Protocol"] = subprotocol
27
+ elsif protocols.include?("null")
28
+ res["Sec-WebSocket-Protocol"] = "null"
29
+ end
30
+ end
31
+
32
+ res.status = WEBrick::HTTPStatus::RC_SWITCHING_PROTOCOLS
33
+ res["Upgrade"] = "websocket"
34
+ #res["Connection"] = "Upgrade"
35
+
36
+ @keep_alive = true
37
+
38
+ res.define_singleton_method(:send_response) do |sock|
39
+ begin
40
+ setup_header
41
+ @header["connection"] = "Upgrade"
42
+ send_header(sock)
43
+ ws = WEBrocket::WebSocket.new(sock, listener, subprotocol)
44
+ server.add_shutdown_listener(ws.method(:on_shutdown))
45
+ ws.start
46
+ @keep_alive = false
47
+ rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex
48
+ @logger.debug(ex)
49
+ @keep_alive = false
50
+ rescue StandardError => ex
51
+ @logger.error(ex)
52
+ @keep_alive = false
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module WEBrocket
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ require "webrick/httpserver"
2
+ require "webrocket"
3
+
4
+ module WEBrick
5
+ class HTTPServer
6
+ def mount_websocket(path, listener, subprotocol)
7
+ mount_proc(path) do |req, res|
8
+ WEBrocket.attach(self, req, res, listener, subprotocol)
9
+ end
10
+ end
11
+
12
+ def add_shutdown_listener(prc = nil, &block)
13
+ @shutdown_listeners ||= []
14
+ @shutdown_listeners.push(prc || block)
15
+ end
16
+
17
+ alias _original_shutdown shutdown
18
+ def shutdown
19
+ if @shutdown_listeners
20
+ @shutdown_listeners.each do |listener|
21
+ listener.call
22
+ end
23
+ end
24
+ _original_shutdown
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,164 @@
1
+ require "webrocket"
2
+
3
+ module WEBrocket
4
+ class WebSocket
5
+ class InternalError < StandardError; end
6
+
7
+ OP_CONTINUE = 0x0
8
+ OP_TEXT = 0x1
9
+ OP_BINARY = 0x2
10
+ OP_CLOSE = 0x8
11
+ OP_PING = 0x9
12
+ OP_PONG = 0xA
13
+
14
+ CLOSE_OK = [1000].pack("n")
15
+ CLOSE_CLOSED = [1001].pack("n")
16
+ CLOSE_PROTOCOL_ERROR = [1002].pack("n")
17
+ CLOSE_BROKEN_DATA = [1003].pack("n")
18
+ CLOSE_BROKEN_MESSAGE = [1007].pack("n")
19
+ CLOSE_POLICY_ERROR = [1008].pack("n")
20
+ CLOSE_TOO_BIG = [1009].pack("n")
21
+ CLOSE_UNKNOWN_ERROR = [1011].pack("n")
22
+
23
+ def initialize(sock, listener, subprotocol)
24
+ @sock = sock
25
+ @listener = listener
26
+ @subprotocol = subprotocol
27
+ @type = nil
28
+ @reason = nil
29
+ end
30
+
31
+ attr_reader :subprotocol
32
+
33
+ def start
34
+ @listener.on_open(self) if @listener.respond_to?(:on_open)
35
+ begin
36
+ while true
37
+ r = IO.select([@sock])
38
+ recv_frame if r
39
+ end
40
+ rescue IOError
41
+ @reason ||= CLOSE_CLOSED
42
+ ensure
43
+ @reason ||= CLOSE_UNKNOWN_ERROR
44
+ send_close_frame(@reason) unless @sock.closed?
45
+ @listener.on_close(self) if @listener.respond_to?(:on_close)
46
+ end
47
+ end
48
+
49
+ def send(data, type = :text)
50
+ nil until IO.select([], [@sock])
51
+ case type
52
+ when :text
53
+ op = OP_TEXT
54
+ when :binary
55
+ op = OP_BINARY
56
+ else
57
+ raise InternalError, "unknown type: #{type}"
58
+ end
59
+ send_frame(op, data)
60
+ end
61
+
62
+ def on_shutdown
63
+ send_close_frame unless @sock.closed?
64
+ @listener.on_shutdown if @listener.respond_to?(:on_shutdown)
65
+ end
66
+
67
+ private
68
+ def send_close_frame(data = "")
69
+ send_frame(OP_CLOSE, data)
70
+ end
71
+
72
+ def send_frame(opcode, data = "")
73
+ data = data.dup.force_encoding('binary')
74
+ if data.bytesize <= 125
75
+ @sock.write([0x80 | opcode, data.bytesize].pack("cc") + data)
76
+ elsif data.bytesize <= 0xffff
77
+ @sock.write([0x80 | opcode, 126, data.bytesize].pack("ccn") + data)
78
+ else # under 64bit
79
+ @sock.write([0x80 | opcode, 127, data.bytesize / 0x1_0000_0000, data.bytesize % 0x1_0000_0000].pack("ccNN") + data)
80
+ end
81
+ end
82
+
83
+ def recv_frame
84
+ header = @sock.read(2)
85
+ return unless header
86
+ if header.bytesize != 2
87
+ @reason = CLOSE_PROTOCOL_ERROR
88
+ raise InternalError, "something wrong: header = '#{header}'"
89
+ end
90
+ h0, h1 = header.unpack("cc")
91
+ len = h1 & 0x7F
92
+ if len == 126
93
+ tmp = @sock.read(2)
94
+ if !tmp || tmp.bytesize != 2
95
+ @reason = CLOSE_PROTOCOL_ERROR
96
+ raise InternalError, "something wrong: size = '#{tmp}'"
97
+ end
98
+ len = tmp.unpack("n")
99
+ elsif len == 127
100
+ tmp = @sock.read(8)
101
+ if !tmp || tmp.bytesize != 8
102
+ @reason = CLOSE_PROTOCOL_ERROR
103
+ raise InternalError, "something wrong: size = '#{tmp}'"
104
+ end
105
+ l0, l1 = tmp.unpack("NN")
106
+ len = l0 * 0x1_0000_0000 + l1
107
+ end
108
+ if len > 0
109
+ if h1 & 0x80 != 0
110
+ key = @sock.read(4)
111
+ if !key || key.bytesize != 4
112
+ @reason = CLOSE_PROTOCOL_ERROR
113
+ raise InternalError, "something wrong: key = '#{key}'"
114
+ end
115
+ end
116
+
117
+ data = @sock.read(len)
118
+ if !data || data.bytesize != len
119
+ @reason = CLOSE_PROTOCOL_ERROR
120
+ raise InternalError, "something wrong"
121
+ end
122
+
123
+ if h1 & 0x80 != 0
124
+ data = data.bytes.map.with_index{|b, i| (b ^ (key[i % 4].ord)).chr}.join
125
+ end
126
+ end
127
+
128
+ op = h0 & 0xF
129
+
130
+ if h0 & 0x80 == 0
131
+ # XXX
132
+ @reason = CLOSE_TOO_BIG
133
+ raise InternalError, "fragmented data is not supported yet"
134
+ end
135
+
136
+ case op
137
+ when OP_CONTINUE, OP_TEXT, OP_BINARY
138
+ if op == OP_TEXT
139
+ @type = :text
140
+ elsif op == OP_BINARY
141
+ @type = :binary
142
+ elsif !@type
143
+ @reason = CLOSE_PROTOCOL_ERROR
144
+ raise InternalError, "continued data at the first"
145
+ end
146
+ if @type == :text
147
+ data.force_encoding('utf-8')
148
+ raise InternalError, "invalid encoding" unless data.valid_encoding?
149
+ end
150
+ @listener.on_message(self, data, @type) if @listener.respond_to?(:on_message)
151
+ when OP_CLOSE
152
+ @reason = CLOSE_OK
153
+ raise IOError
154
+ when OP_PING
155
+ send_frame(OP_PONG, data)
156
+ when OP_PONG
157
+ # nothing to do
158
+ else
159
+ @reason = CLOSE_PROTOCOL_ERROR
160
+ raise InternalError, "unknown opcode: 0x%x" % op
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,47 @@
1
+ <html lang="ja">
2
+ <head>
3
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
4
+ <title>chat sample</title>
5
+ <script type="text/javascript"><!--
6
+ var conn;
7
+
8
+ function escapeHTML(str) {
9
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
10
+ }
11
+
12
+ function login() {
13
+ console.log("login");
14
+ var user = document.getElementById("text").value;
15
+ if (user != "") {
16
+ document.getElementById("text").value = "";
17
+ document.getElementById("send").innerHTML = "send";
18
+ document.getElementById("send").setAttribute("onclick", "message();");
19
+
20
+ conn = new WebSocket("ws://localhost:10080/chat", "chat");
21
+ conn.onopen = function() {
22
+ conn.send("\uFEFF" + user);
23
+ };
24
+ conn.onerror = function(error) {
25
+ var elem = document.getElementById("server");
26
+ elem.innerHTML = "<b>WebSocket error: " + escapeHTML(error) + "</b><br>\n" + elem.innerHTML;
27
+ };
28
+ conn.onmessage = function(v) {
29
+ var elem = document.getElementById("server");
30
+ elem.innerHTML = escapeHTML(v.data) + "<br>\n" + elem.innerHTML;
31
+ };
32
+ }
33
+ }
34
+
35
+ function message() {
36
+ console.log("message");
37
+ conn.send(document.getElementById("text").value);
38
+ document.getElementById("text").value = "";
39
+ }
40
+ --></script>
41
+ </head>
42
+ <body>
43
+ <input id="text" value=""/><button id="send" onclick="login();">login</button>
44
+ <p id="server">
45
+ </p>
46
+ </body>
47
+ </html>
@@ -0,0 +1,60 @@
1
+ #!ruby
2
+ $LOAD_PATH.unshift(File.expand_path("../lib", File.dirname(__FILE__)))
3
+ require "webrick"
4
+ require "webrocket"
5
+
6
+ # write listner
7
+ class Listener
8
+ def initialize
9
+ @clients = []
10
+ @m = Mutex.new
11
+ end
12
+
13
+ def on_open(websocket)
14
+ p [:open, websocket]
15
+ @m.synchronize do
16
+ @clients.push(websocket)
17
+ end
18
+ end
19
+
20
+ def on_close(websocket)
21
+ p [:close, websocket]
22
+ @m.synchronize do
23
+ user = websocket.instance_variable_get(:@chat_user)
24
+ if user
25
+ @clients.each do |cl|
26
+ cl.send("logout #{user}", :text)
27
+ end
28
+ end
29
+ @clients.delete(websocket)
30
+ end
31
+ end
32
+
33
+ def on_message(websocket, data, type)
34
+ user = websocket.instance_variable_get(:@chat_user)
35
+ @m.synchronize do
36
+ @clients.each do |cl|
37
+ if user
38
+ cl.send("#{user}: #{data}", :text)
39
+ else
40
+ cl.send("login #{data}", :text)
41
+ end
42
+ end
43
+ end
44
+ unless user
45
+ websocket.instance_variable_set(:@chat_user, data)
46
+ end
47
+ end
48
+
49
+ def on_shutdown
50
+ p [:shutdown]
51
+ # nothing to do
52
+ end
53
+ end
54
+
55
+ server = WEBrick::HTTPServer.new(Port: 10080)
56
+ server.mount_websocket("/chat", Listener.new, "chat")
57
+ trap(:INT) do
58
+ server.shutdown
59
+ end
60
+ server.start
@@ -0,0 +1,29 @@
1
+ <html lang="ja">
2
+ <head>
3
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
4
+ <script type="text/javascript"><!--
5
+ function escapeHTML(str){
6
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
7
+ }
8
+
9
+ var conn = new WebSocket("ws://localhost:10080/echo", "echo");
10
+ conn.onopen = function(){
11
+ var elem = document.getElementById("server");
12
+ elem.innerHTML = "<b>connected</b><br>\n" + elem.innerHTML;
13
+ };
14
+ conn.onerror = function(error){
15
+ var elem = document.getElementById("server");
16
+ elem.innerHTML = "<b>WebSocket error: " + escapeHTML(error) + "</b><br>\n" + elem.innerHTML;
17
+ };
18
+ conn.onmessage = function(v){
19
+ var elem = document.getElementById("server");
20
+ elem.innerHTML = escapeHTML(v.data) + "<br>\n" + elem.innerHTML;
21
+ }
22
+ --></script>
23
+ </head>
24
+ <body>
25
+ <input id="text" value="hello!"/><button onclick="conn.send(document.getElementById('text').value);">send</button>
26
+ <p id="server">
27
+ </p>
28
+ </body>
29
+ </html>
@@ -0,0 +1,35 @@
1
+ #!ruby
2
+ $LOAD_PATH.unshift(File.expand_path("../lib", File.dirname(__FILE__)))
3
+ require "webrick"
4
+ require "webrocket"
5
+
6
+ # write listner
7
+ class Listener
8
+ def on_open(websocket)
9
+ # do somthing
10
+ p :open
11
+ end
12
+
13
+ def on_close(websocket)
14
+ # do somthing
15
+ p :close
16
+ end
17
+
18
+ def on_message(websocket, data, type)
19
+ # do something
20
+ p [:message, data, type]
21
+ websocket.send(data, type)
22
+ end
23
+
24
+ def on_shutdown
25
+ # do something
26
+ p :shutdown
27
+ end
28
+ end
29
+
30
+ server = WEBrick::HTTPServer.new(Port: 10080)
31
+ server.mount_websocket("/echo", Listener.new, "echo")
32
+ trap(:INT) do
33
+ server.shutdown
34
+ end
35
+ server.start
@@ -0,0 +1,115 @@
1
+ require 'base64'
2
+ require 'net/http'
3
+ require 'socket'
4
+ require 'test/unit'
5
+ require 'webrick'
6
+ $LOAD_PATH.unshift(File.expand_path('../lib', File.dirname(__FILE__)))
7
+ require 'webrocket'
8
+
9
+ class TestWEBrocket < Test::Unit::TestCase
10
+ def start_test_server(&block)
11
+ logger = Object.new
12
+ logger.instance_eval do
13
+ def <<(msg)
14
+ @log ||= ''
15
+ @log << msg
16
+ end
17
+
18
+ def log
19
+ @log ||= ''
20
+ end
21
+ end
22
+
23
+ server = WEBrick::HTTPServer.new(
24
+ :BindAddress => "127.0.0.1",
25
+ :Port => 0,
26
+ :DocumentRoot => File.dirname(__FILE__),
27
+ :ShutdownSocketWithoutClose => true,
28
+ :ServerType => Thread,
29
+ :Logger => WEBrick::Log.new(logger),
30
+ :AccessLog => [[logger, ""]]
31
+ )
32
+
33
+ begin
34
+ th = server.start
35
+ addr = server.listeners[0].addr
36
+ block.call(server, addr[3], addr[1], logger)
37
+ ensure
38
+ server.shutdown
39
+ th.join
40
+ end
41
+
42
+ logger.log
43
+ end
44
+
45
+ class Listener
46
+ def initialize(open = proc{}, close = proc{}, message = proc{}, shutdown = proc{})
47
+ @open = open
48
+ @close = close
49
+ @message = message
50
+ @shutdown = shutdown
51
+ end
52
+
53
+ def on_open(websocket)
54
+ @open.call(websocket)
55
+ end
56
+
57
+ def on_close(websocket)
58
+ @close.call(websocket)
59
+ end
60
+
61
+ def on_message(websocket, data, type)
62
+ @message.call(websocket, data, type)
63
+ end
64
+
65
+ def on_shutdown
66
+ @shutdown.call
67
+ end
68
+ end
69
+
70
+ def test_websocket
71
+ shutdowned = false # yes, I know shutdown is a noun
72
+ start_test_server do |server, host, port, logger|
73
+ http = Net::HTTP.new(host, port)
74
+
75
+ req = Net::HTTP::Get.new('/')
76
+ http.request(req) do |res|
77
+ assert_kind_of Net::HTTPOK, res.response
78
+ end
79
+
80
+ opened = false
81
+ open = lambda {|websocket|
82
+ opened = true
83
+ }
84
+ close = lambda {|websocket|
85
+ opened = false
86
+ }
87
+ message = lambda {|websocket, data, type|
88
+ }
89
+ shutdown = lambda {
90
+ shutdowned = true
91
+ }
92
+ server.mount_websocket('/test', Listener.new(open, close, message, shutdown), 'test')
93
+
94
+ req = Net::HTTP::Get.new('/test')
95
+ req['Upgrade'] = 'websocket'
96
+ req['Connection'] = 'Upgrade'
97
+ req['Sec-WebSocket-Version'] = '13'
98
+ req['Sec-WebSocket-Key'] = Base64.encode64('0'*16)
99
+
100
+ refute opened
101
+ sock = nil
102
+ http.request(req) do |res|
103
+ assert_kind_of Net::HTTPSwitchProtocol, res.response
104
+ sock = http.instance_variable_get(:@socket).io.dup
105
+ end
106
+
107
+ assert opened
108
+
109
+ sock.shutdown
110
+
111
+ refute shutdowned
112
+ end
113
+ assert shutdowned
114
+ end
115
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'webrocket/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "webrocket"
8
+ spec.version = WEBrocket::VERSION
9
+ spec.authors = ["U.Nakamura"]
10
+ spec.email = ["usa@garbagecollect.jp"]
11
+ spec.description = %q{WebSocket extension for WEBrick}
12
+ spec.summary = %q{WebSocket extension for WEBrick}
13
+ spec.homepage = "https://github.com/unak/WEBRocket"
14
+ spec.license = "BSD-2-Clause"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "test-unit"
24
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: webrocket
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - U.Nakamura
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: WebSocket extension for WEBrick
56
+ email:
57
+ - usa@garbagecollect.jp
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".travis.yml"
64
+ - Gemfile
65
+ - README.md
66
+ - Rakefile
67
+ - lib/webrocket.rb
68
+ - lib/webrocket/version.rb
69
+ - lib/webrocket/webrick-extender.rb
70
+ - lib/webrocket/websocket.rb
71
+ - sample/chat.html
72
+ - sample/chat.rb
73
+ - sample/echo.html
74
+ - sample/echo.rb
75
+ - test/test_webrocket.rb
76
+ - webrocket.gemspec
77
+ homepage: https://github.com/unak/WEBRocket
78
+ licenses:
79
+ - BSD-2-Clause
80
+ metadata: {}
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubyforge_project:
97
+ rubygems_version: 2.2.2
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: WebSocket extension for WEBrick
101
+ test_files:
102
+ - test/test_webrocket.rb