_bushido-faye-websocket 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +56 -0
- data/README.rdoc +366 -0
- data/examples/app.rb +50 -0
- data/examples/autobahn_client.rb +44 -0
- data/examples/client.rb +30 -0
- data/examples/config.ru +17 -0
- data/examples/haproxy.conf +21 -0
- data/examples/server.rb +44 -0
- data/examples/sse.html +39 -0
- data/examples/ws.html +44 -0
- data/ext/faye_websocket_mask/FayeWebsocketMaskService.java +61 -0
- data/ext/faye_websocket_mask/extconf.rb +5 -0
- data/ext/faye_websocket_mask/faye_websocket_mask.c +33 -0
- data/lib/faye/adapters/goliath.rb +47 -0
- data/lib/faye/adapters/rainbows.rb +32 -0
- data/lib/faye/adapters/rainbows_client.rb +70 -0
- data/lib/faye/adapters/thin.rb +62 -0
- data/lib/faye/eventsource.rb +124 -0
- data/lib/faye/websocket.rb +216 -0
- data/lib/faye/websocket/adapter.rb +21 -0
- data/lib/faye/websocket/api.rb +96 -0
- data/lib/faye/websocket/api/event.rb +33 -0
- data/lib/faye/websocket/api/event_target.rb +34 -0
- data/lib/faye/websocket/client.rb +84 -0
- data/lib/faye/websocket/draft75_parser.rb +87 -0
- data/lib/faye/websocket/draft76_parser.rb +84 -0
- data/lib/faye/websocket/hybi_parser.rb +320 -0
- data/lib/faye/websocket/hybi_parser/handshake.rb +78 -0
- data/lib/faye/websocket/hybi_parser/stream_reader.rb +29 -0
- data/lib/faye/websocket/utf8_match.rb +8 -0
- data/spec/faye/websocket/client_spec.rb +179 -0
- data/spec/faye/websocket/draft75_parser_examples.rb +48 -0
- data/spec/faye/websocket/draft75_parser_spec.rb +27 -0
- data/spec/faye/websocket/draft76_parser_spec.rb +34 -0
- data/spec/faye/websocket/hybi_parser_spec.rb +156 -0
- data/spec/rainbows.conf +3 -0
- data/spec/server.crt +15 -0
- data/spec/server.key +15 -0
- data/spec/spec_helper.rb +68 -0
- metadata +158 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.expand_path('../../lib/faye/websocket', __FILE__)
|
3
|
+
require 'cgi'
|
4
|
+
|
5
|
+
EM.run {
|
6
|
+
host = 'ws://localhost:9001'
|
7
|
+
agent = "Faye (Ruby #{RUBY_VERSION})"
|
8
|
+
cases = 0
|
9
|
+
skip = []
|
10
|
+
|
11
|
+
socket = Faye::WebSocket::Client.new("#{host}/getCaseCount")
|
12
|
+
|
13
|
+
socket.onmessage = lambda do |event|
|
14
|
+
puts "Total cases to run: #{event.data}"
|
15
|
+
cases = event.data.to_i
|
16
|
+
end
|
17
|
+
|
18
|
+
socket.onclose = lambda do |event|
|
19
|
+
run_case = lambda do |n|
|
20
|
+
if n > cases
|
21
|
+
socket = Faye::WebSocket::Client.new("#{host}/updateReports?agent=#{CGI.escape agent}")
|
22
|
+
socket.onclose = lambda { |e| EM.stop }
|
23
|
+
|
24
|
+
elsif skip.include?(n)
|
25
|
+
EM.next_tick { run_case.call(n+1) }
|
26
|
+
|
27
|
+
else
|
28
|
+
puts "Running test case ##{n} ..."
|
29
|
+
socket = Faye::WebSocket::Client.new("#{host}/runCase?case=#{n}&agent=#{CGI.escape agent}")
|
30
|
+
|
31
|
+
socket.onmessage = lambda do |event|
|
32
|
+
socket.send(event.data)
|
33
|
+
end
|
34
|
+
|
35
|
+
socket.onclose = lambda do |event|
|
36
|
+
run_case.call(n + 1)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
run_case.call(1)
|
42
|
+
end
|
43
|
+
}
|
44
|
+
|
data/examples/client.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require File.expand_path('../../lib/faye/websocket', __FILE__)
|
3
|
+
require 'eventmachine'
|
4
|
+
|
5
|
+
port = ARGV[0] || 7000
|
6
|
+
secure = ARGV[1] == 'ssl'
|
7
|
+
|
8
|
+
EM.run {
|
9
|
+
scheme = secure ? 'wss' : 'ws'
|
10
|
+
url = "#{scheme}://localhost:#{port}/"
|
11
|
+
socket = Faye::WebSocket::Client.new(url)
|
12
|
+
|
13
|
+
puts "Connecting to #{socket.url}"
|
14
|
+
|
15
|
+
socket.onopen = lambda do |event|
|
16
|
+
p [:open]
|
17
|
+
socket.send("Hello, WebSocket!")
|
18
|
+
end
|
19
|
+
|
20
|
+
socket.onmessage = lambda do |event|
|
21
|
+
p [:message, event.data]
|
22
|
+
# socket.close 1002, 'Going away'
|
23
|
+
end
|
24
|
+
|
25
|
+
socket.onclose = lambda do |event|
|
26
|
+
p [:close, event.code, event.reason]
|
27
|
+
EM.stop
|
28
|
+
end
|
29
|
+
}
|
30
|
+
|
data/examples/config.ru
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# Run using your favourite async server:
|
2
|
+
#
|
3
|
+
# thin start -R examples/config.ru -p 7000
|
4
|
+
# rainbows -c spec/rainbows.conf -E production examples/config.ru -p 7000
|
5
|
+
#
|
6
|
+
# If you run using one of these commands, the webserver is loaded before this
|
7
|
+
# file, so Faye::WebSocket can figure out which adapter to load. If instead you
|
8
|
+
# run using `rackup`, you need the `load_adapter` line below.
|
9
|
+
#
|
10
|
+
# rackup -E production -s thin examples/config.ru -p 7000
|
11
|
+
|
12
|
+
require 'rubygems'
|
13
|
+
require File.expand_path('../app', __FILE__)
|
14
|
+
# Faye::WebSocket.load_adapter('thin')
|
15
|
+
|
16
|
+
run App
|
17
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
defaults
|
2
|
+
mode http
|
3
|
+
timeout client 5s
|
4
|
+
timeout connect 5s
|
5
|
+
timeout server 5s
|
6
|
+
|
7
|
+
frontend all 0.0.0.0:3000
|
8
|
+
mode http
|
9
|
+
timeout client 120s
|
10
|
+
|
11
|
+
option forwardfor
|
12
|
+
option http-server-close
|
13
|
+
option http-pretend-keepalive
|
14
|
+
|
15
|
+
default_backend sockets
|
16
|
+
|
17
|
+
backend sockets
|
18
|
+
balance uri depth 2
|
19
|
+
timeout server 120s
|
20
|
+
server socket1 127.0.0.1:7000
|
21
|
+
|
data/examples/server.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rack/content_length'
|
3
|
+
require 'rack/chunked'
|
4
|
+
|
5
|
+
port = ARGV[0] || 7000
|
6
|
+
secure = ARGV[1] == 'ssl'
|
7
|
+
engine = ARGV[2] || 'thin'
|
8
|
+
spec = File.expand_path('../../spec', __FILE__)
|
9
|
+
|
10
|
+
require File.expand_path('../app', __FILE__)
|
11
|
+
Faye::WebSocket.load_adapter(engine)
|
12
|
+
|
13
|
+
case engine
|
14
|
+
|
15
|
+
when 'goliath'
|
16
|
+
class WebSocketServer < Goliath::API
|
17
|
+
def response(env)
|
18
|
+
App.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
when 'rainbows'
|
23
|
+
rackup = Unicorn::Configurator::RACKUP
|
24
|
+
rackup[:port] = port
|
25
|
+
rackup[:set_listener] = true
|
26
|
+
options = rackup[:options]
|
27
|
+
options[:config_file] = spec + '/rainbows.conf'
|
28
|
+
Rainbows::HttpServer.new(App, options).start.join
|
29
|
+
|
30
|
+
when 'thin'
|
31
|
+
EM.run {
|
32
|
+
thin = Rack::Handler.get('thin')
|
33
|
+
thin.run(App, :Port => port) do |server|
|
34
|
+
if secure
|
35
|
+
server.ssl_options = {
|
36
|
+
:private_key_file => spec + '/server.key',
|
37
|
+
:cert_chain_file => spec + '/server.crt'
|
38
|
+
}
|
39
|
+
server.ssl = true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
data/examples/sse.html
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
5
|
+
<title>EventSource test</title>
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
|
9
|
+
<h1>EventSource test</h1>
|
10
|
+
<ul></ul>
|
11
|
+
|
12
|
+
<script type="text/javascript">
|
13
|
+
var logger = document.getElementsByTagName('ul')[0],
|
14
|
+
socket = new EventSource('/');
|
15
|
+
|
16
|
+
var log = function(text) {
|
17
|
+
logger.innerHTML += '<li>' + text + '</li>';
|
18
|
+
};
|
19
|
+
|
20
|
+
socket.onopen = function() {
|
21
|
+
log('OPEN');
|
22
|
+
};
|
23
|
+
|
24
|
+
socket.onmessage = function(event) {
|
25
|
+
log('MESSAGE: ' + event.data);
|
26
|
+
};
|
27
|
+
|
28
|
+
socket.addEventListener('update', function(event) {
|
29
|
+
log('UPDATE(' + event.lastEventId + '): ' + event.data);
|
30
|
+
});
|
31
|
+
|
32
|
+
socket.onerror = function(event) {
|
33
|
+
log('ERROR: ' + event.message);
|
34
|
+
};
|
35
|
+
</script>
|
36
|
+
|
37
|
+
</body>
|
38
|
+
</html>
|
39
|
+
|
data/examples/ws.html
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
5
|
+
<title>WebSocket test</title>
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
|
9
|
+
<h1>WebSocket test</h1>
|
10
|
+
<ul></ul>
|
11
|
+
|
12
|
+
<script type="text/javascript">
|
13
|
+
var logger = document.getElementsByTagName('ul')[0],
|
14
|
+
Socket = window.MozWebSocket || window.WebSocket,
|
15
|
+
protos = ['foo', 'bar', 'xmpp'],
|
16
|
+
socket = new Socket('ws://' + location.hostname + ':' + location.port + '/', protos),
|
17
|
+
index = 0;
|
18
|
+
|
19
|
+
var log = function(text) {
|
20
|
+
logger.innerHTML += '<li>' + text + '</li>';
|
21
|
+
};
|
22
|
+
|
23
|
+
socket.addEventListener('open', function() {
|
24
|
+
log('OPEN: ' + socket.protocol);
|
25
|
+
socket.send('Hello, world');
|
26
|
+
});
|
27
|
+
|
28
|
+
socket.onerror = function(event) {
|
29
|
+
log('ERROR: ' + event.message);
|
30
|
+
};
|
31
|
+
|
32
|
+
socket.onmessage = function(event) {
|
33
|
+
log('MESSAGE: ' + event.data);
|
34
|
+
setTimeout(function() { socket.send(++index + ' ' + event.data) }, 2000);
|
35
|
+
};
|
36
|
+
|
37
|
+
socket.onclose = function(event) {
|
38
|
+
log('CLOSE: ' + event.code + ', ' + event.reason);
|
39
|
+
};
|
40
|
+
</script>
|
41
|
+
|
42
|
+
</body>
|
43
|
+
</html>
|
44
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
package com.jcoglan.faye;
|
2
|
+
|
3
|
+
import java.lang.Long;
|
4
|
+
import java.io.IOException;
|
5
|
+
|
6
|
+
import org.jruby.Ruby;
|
7
|
+
import org.jruby.RubyArray;
|
8
|
+
import org.jruby.RubyClass;
|
9
|
+
import org.jruby.RubyFixnum;
|
10
|
+
import org.jruby.RubyModule;
|
11
|
+
import org.jruby.RubyObject;
|
12
|
+
import org.jruby.anno.JRubyMethod;
|
13
|
+
import org.jruby.runtime.ObjectAllocator;
|
14
|
+
import org.jruby.runtime.ThreadContext;
|
15
|
+
import org.jruby.runtime.builtin.IRubyObject;
|
16
|
+
import org.jruby.runtime.load.BasicLibraryService;
|
17
|
+
|
18
|
+
public class FayeWebsocketMaskService implements BasicLibraryService {
|
19
|
+
private Ruby runtime;
|
20
|
+
|
21
|
+
public boolean basicLoad(Ruby runtime) throws IOException {
|
22
|
+
this.runtime = runtime;
|
23
|
+
RubyModule faye = runtime.defineModule("Faye");
|
24
|
+
|
25
|
+
RubyClass webSocketMask = faye.defineClassUnder("WebSocketMask", runtime.getObject(), new ObjectAllocator() {
|
26
|
+
public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
|
27
|
+
return new WebsocketMask(runtime, rubyClass);
|
28
|
+
}
|
29
|
+
});
|
30
|
+
|
31
|
+
webSocketMask.defineAnnotatedMethods(WebsocketMask.class);
|
32
|
+
return true;
|
33
|
+
}
|
34
|
+
|
35
|
+
public class WebsocketMask extends RubyObject {
|
36
|
+
public WebsocketMask(final Ruby runtime, RubyClass rubyClass) {
|
37
|
+
super(runtime, rubyClass);
|
38
|
+
}
|
39
|
+
|
40
|
+
@JRubyMethod
|
41
|
+
public IRubyObject mask(ThreadContext context, IRubyObject payload, IRubyObject mask) {
|
42
|
+
int n = ((RubyArray)payload).getLength(), i;
|
43
|
+
long p, m;
|
44
|
+
RubyArray unmasked = RubyArray.newArray(runtime, n);
|
45
|
+
|
46
|
+
long[] maskArray = {
|
47
|
+
(Long)((RubyArray)mask).get(0),
|
48
|
+
(Long)((RubyArray)mask).get(1),
|
49
|
+
(Long)((RubyArray)mask).get(2),
|
50
|
+
(Long)((RubyArray)mask).get(3)
|
51
|
+
};
|
52
|
+
|
53
|
+
for (i = 0; i < n; i++) {
|
54
|
+
p = (Long)((RubyArray)payload).get(i);
|
55
|
+
m = maskArray[i % 4];
|
56
|
+
unmasked.set(i, p ^ m);
|
57
|
+
}
|
58
|
+
return unmasked;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
@@ -0,0 +1,33 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
|
3
|
+
VALUE Faye = Qnil;
|
4
|
+
VALUE FayeWebSocketMask = Qnil;
|
5
|
+
|
6
|
+
void Init_faye_websocket_mask();
|
7
|
+
VALUE method_faye_websocket_mask(VALUE self, VALUE payload, VALUE mask);
|
8
|
+
|
9
|
+
void Init_faye_websocket_mask() {
|
10
|
+
Faye = rb_define_module("Faye");
|
11
|
+
FayeWebSocketMask = rb_define_module_under(Faye, "WebSocketMask");
|
12
|
+
rb_define_singleton_method(FayeWebSocketMask, "mask", method_faye_websocket_mask, 2);
|
13
|
+
}
|
14
|
+
|
15
|
+
VALUE method_faye_websocket_mask(VALUE self, VALUE payload, VALUE mask) {
|
16
|
+
int n = RARRAY_LEN(payload), i, p, m;
|
17
|
+
VALUE unmasked = rb_ary_new2(n);
|
18
|
+
|
19
|
+
int mask_array[] = {
|
20
|
+
NUM2INT(rb_ary_entry(mask, 0)),
|
21
|
+
NUM2INT(rb_ary_entry(mask, 1)),
|
22
|
+
NUM2INT(rb_ary_entry(mask, 2)),
|
23
|
+
NUM2INT(rb_ary_entry(mask, 3))
|
24
|
+
};
|
25
|
+
|
26
|
+
for (i = 0; i < n; i++) {
|
27
|
+
p = NUM2INT(rb_ary_entry(payload, i));
|
28
|
+
m = mask_array[i % 4];
|
29
|
+
rb_ary_store(unmasked, i, INT2NUM(p ^ m));
|
30
|
+
}
|
31
|
+
return unmasked;
|
32
|
+
}
|
33
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Goliath::Connection
|
2
|
+
attr_accessor :socket_stream
|
3
|
+
alias :goliath_receive_data :receive_data
|
4
|
+
|
5
|
+
def receive_data(data)
|
6
|
+
if @serving == :websocket
|
7
|
+
socket_stream.receive(data) if socket_stream
|
8
|
+
else
|
9
|
+
goliath_receive_data(data)
|
10
|
+
socket_stream.receive(@parser.upgrade_data) if socket_stream
|
11
|
+
@serving = :websocket if @api.websocket?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def unbind
|
16
|
+
super
|
17
|
+
ensure
|
18
|
+
socket_stream.fail if socket_stream
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Goliath::API
|
23
|
+
include Faye::WebSocket::Adapter
|
24
|
+
end
|
25
|
+
|
26
|
+
class Goliath::Request
|
27
|
+
alias :goliath_process :process
|
28
|
+
|
29
|
+
def process
|
30
|
+
env['em.connection'] = conn
|
31
|
+
goliath_process
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Goliath::Response
|
36
|
+
alias :goliath_head :head
|
37
|
+
alias :goliath_headers_output :headers_output
|
38
|
+
|
39
|
+
def head
|
40
|
+
(status == 101) ? '' : goliath_head
|
41
|
+
end
|
42
|
+
|
43
|
+
def headers_output
|
44
|
+
(status == 101) ? '' : goliath_headers_output
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# WebSocket extensions for Rainbows
|
2
|
+
# Based on code from the Cramp project
|
3
|
+
# http://github.com/lifo/cramp
|
4
|
+
|
5
|
+
# Copyright (c) 2009-2011 Pratik Naik
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
module Faye
|
27
|
+
class WebSocket
|
28
|
+
autoload :RainbowsClient, File.expand_path('../rainbows_client', __FILE__)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
Rainbows::O[:em_client_class] = "Faye::WebSocket::RainbowsClient"
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# WebSocket extensions for Rainbows
|
2
|
+
# Based on code from the Cramp project
|
3
|
+
# http://github.com/lifo/cramp
|
4
|
+
|
5
|
+
# Copyright (c) 2009-2011 Pratik Naik
|
6
|
+
#
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
8
|
+
# a copy of this software and associated documentation files (the
|
9
|
+
# "Software"), to deal in the Software without restriction, including
|
10
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
11
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
12
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
13
|
+
# the following conditions:
|
14
|
+
#
|
15
|
+
# The above copyright notice and this permission notice shall be
|
16
|
+
# included in all copies or substantial portions of the Software.
|
17
|
+
#
|
18
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
19
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
20
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
21
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
22
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
23
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
24
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
module Faye
|
27
|
+
class WebSocket
|
28
|
+
|
29
|
+
class RainbowsClient < Rainbows::EventMachine::Client
|
30
|
+
include Faye::WebSocket::Adapter
|
31
|
+
attr_accessor :socket_stream
|
32
|
+
|
33
|
+
def receive_data(data)
|
34
|
+
return super unless @state == :websocket
|
35
|
+
socket_stream.receive(data) if socket_stream
|
36
|
+
end
|
37
|
+
|
38
|
+
def app_call(*args)
|
39
|
+
@env['em.connection'] = self
|
40
|
+
if args.first == NULL_IO and @hp.content_length == 0 and websocket?
|
41
|
+
prepare_request_body
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def on_read(data)
|
48
|
+
if @state == :body and websocket? and @hp.body_eof?
|
49
|
+
@state = :websocket
|
50
|
+
@input.rewind
|
51
|
+
app_call StringIO.new(@buf)
|
52
|
+
else
|
53
|
+
super
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def unbind
|
58
|
+
super
|
59
|
+
ensure
|
60
|
+
socket_stream.fail if socket_stream
|
61
|
+
end
|
62
|
+
|
63
|
+
def write_headers(*args)
|
64
|
+
super unless async_connection?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|