polyphony 0.23 → 0.24
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 +4 -4
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +4 -10
- data/README.md +0 -4
- data/TODO.md +5 -56
- data/docs/README.md +4 -7
- data/examples/core/{channel_echo.rb → xx-channels.rb} +0 -0
- data/examples/core/{defer.rb → xx-deferring-an-operation.rb} +0 -0
- data/examples/core/{genserver.rb → xx-erlang-style-genserver.rb} +2 -2
- data/examples/core/{fork.rb → xx-forking.rb} +2 -0
- data/examples/core/xx-move_on.rb +23 -0
- data/examples/core/{pulse.rb → xx-recurrent-timer.rb} +3 -2
- data/examples/core/{resource_cancel.rb → xx-resource_cancel.rb} +1 -2
- data/examples/core/{resource_delegate.rb → xx-resource_delegate.rb} +0 -0
- data/examples/core/{wait_for_signal.rb → xx-signals.rb} +0 -0
- data/examples/core/{sleep.rb → xx-sleeping.rb} +0 -0
- data/examples/core/{spin_error_backtrace.rb → xx-spin_error_backtrace.rb} +5 -2
- data/examples/core/{supervisor.rb → xx-supervisors.rb} +0 -0
- data/examples/core/{thread_cancel.rb → xx-thread_cancel.rb} +0 -0
- data/examples/core/{thread_pool.rb → xx-thread_pool.rb} +0 -0
- data/examples/core/{throttle.rb → xx-throttling.rb} +0 -0
- data/examples/core/{timeout.rb → xx-timeout.rb} +0 -0
- data/examples/core/{lock.rb → xx-using-a-mutex.rb} +0 -0
- data/examples/io/{backticks.rb → xx-backticks.rb} +0 -0
- data/examples/io/{echo_client.rb → xx-echo_client.rb} +0 -1
- data/examples/io/{echo_client_from_stdin.rb → xx-echo_client_from_stdin.rb} +0 -1
- data/examples/io/{echo_pipe.rb → xx-echo_pipe.rb} +0 -0
- data/examples/io/{echo_server.rb → xx-echo_server.rb} +0 -0
- data/examples/io/{echo_server_with_timeout.rb → xx-echo_server_with_timeout.rb} +0 -0
- data/examples/io/{echo_stdin.rb → xx-echo_stdin.rb} +0 -0
- data/examples/io/xx-httparty.rb +13 -0
- data/examples/io/{irb.rb → xx-irb.rb} +0 -0
- data/examples/io/{net-http.rb → xx-net-http.rb} +0 -0
- data/examples/io/{open.rb → xx-open.rb} +0 -1
- data/examples/io/{system.rb → xx-system.rb} +0 -0
- data/examples/io/{tcpserver.rb → xx-tcpserver.rb} +0 -0
- data/examples/io/{tcpsocket.rb → xx-tcpsocket.rb} +0 -1
- data/examples/{fs/read.rb → performance/fs_read.rb} +0 -0
- data/examples/{core → performance}/mem-usage.rb +1 -0
- data/examples/performance/multi_snooze.rb +2 -0
- data/examples/performance/snooze.rb +2 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +5 -3
- data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -1
- data/examples/{io/httparty_multi.rb → performance/thread-vs-fiber/xx-httparty_multi.rb} +16 -13
- data/examples/{io/httparty_threaded.rb → performance/thread-vs-fiber/xx-httparty_threaded.rb} +2 -2
- data/examples/performance/thread.rb +27 -0
- data/examples/{core → performance}/thread_pool_perf.rb +0 -0
- data/ext/gyro/extconf.rb +1 -0
- data/lib/polyphony/extensions/core.rb +0 -5
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +3 -9
- metadata +59 -167
- data/bin/poly +0 -11
- data/examples/core/cancel.rb +0 -13
- data/examples/core/enumerator.rb +0 -15
- data/examples/core/error_bubbling.rb +0 -35
- data/examples/core/fiber_error.rb +0 -9
- data/examples/core/fiber_error_with_backtrace.rb +0 -73
- data/examples/core/move_on.rb +0 -11
- data/examples/core/move_on_twice.rb +0 -16
- data/examples/core/move_on_with_ensure.rb +0 -13
- data/examples/core/move_on_with_value.rb +0 -14
- data/examples/core/multiple_spin.rb +0 -18
- data/examples/core/nested_cancel.rb +0 -40
- data/examples/core/nested_multiple_spin.rb +0 -20
- data/examples/core/nested_spin.rb +0 -19
- data/examples/core/pingpong.rb +0 -21
- data/examples/core/resource.rb +0 -30
- data/examples/core/sleep_spin.rb +0 -21
- data/examples/core/snooze.rb +0 -32
- data/examples/core/spin_error.rb +0 -17
- data/examples/core/spin_uncaught_error.rb +0 -16
- data/examples/core/supervisor_with_cancel_scope.rb +0 -23
- data/examples/core/supervisor_with_error.rb +0 -24
- data/examples/core/supervisor_with_manual_move_on.rb +0 -23
- data/examples/core/suspend.rb +0 -13
- data/examples/core/thread.rb +0 -27
- data/examples/http/config.ru +0 -7
- data/examples/http/cuba.ru +0 -22
- data/examples/http/happy_eyeballs.rb +0 -37
- data/examples/http/http2_raw.rb +0 -135
- data/examples/http/http_client.rb +0 -28
- data/examples/http/http_get.rb +0 -33
- data/examples/http/http_parse_experiment.rb +0 -123
- data/examples/http/http_proxy.rb +0 -83
- data/examples/http/http_server.js +0 -24
- data/examples/http/http_server.rb +0 -28
- data/examples/http/http_server_forked.rb +0 -29
- data/examples/http/http_server_graceful.rb +0 -27
- data/examples/http/http_server_simple.rb +0 -11
- data/examples/http/http_server_throttled.rb +0 -15
- data/examples/http/http_ws_server.rb +0 -37
- data/examples/http/https_raw_client.rb +0 -12
- data/examples/http/https_server.rb +0 -22
- data/examples/http/https_wss_server.rb +0 -39
- data/examples/http/rack_server.rb +0 -12
- data/examples/http/rack_server_https.rb +0 -19
- data/examples/http/rack_server_https_forked.rb +0 -27
- data/examples/http/websocket_secure_server.rb +0 -27
- data/examples/http/websocket_server.rb +0 -24
- data/examples/http/ws_page.html +0 -34
- data/examples/http/wss_page.html +0 -34
- data/examples/io/cat.rb +0 -12
- data/examples/io/httparty.rb +0 -10
- data/examples/io/io_read.rb +0 -9
- data/lib/ev_ext.bundle +0 -0
- data/lib/polyphony/http.rb +0 -16
- data/lib/polyphony/http/client/agent.rb +0 -131
- data/lib/polyphony/http/client/http1.rb +0 -129
- data/lib/polyphony/http/client/http2.rb +0 -180
- data/lib/polyphony/http/client/response.rb +0 -32
- data/lib/polyphony/http/client/site_connection_manager.rb +0 -109
- data/lib/polyphony/http/server.rb +0 -49
- data/lib/polyphony/http/server/http1.rb +0 -268
- data/lib/polyphony/http/server/http2.rb +0 -78
- data/lib/polyphony/http/server/http2_stream.rb +0 -136
- data/lib/polyphony/http/server/rack.rb +0 -64
- data/lib/polyphony/http/server/request.rb +0 -118
- data/lib/polyphony/websocket.rb +0 -59
- data/test/test_http_server.rb +0 -313
@@ -1,78 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
export_default :Protocol
|
4
|
-
|
5
|
-
require 'http/2'
|
6
|
-
|
7
|
-
Stream = import './http2_stream'
|
8
|
-
|
9
|
-
# HTTP2 API
|
10
|
-
class Protocol
|
11
|
-
def self.upgrade_each(socket, opts, headers, &block)
|
12
|
-
adapter = new(socket, opts, headers)
|
13
|
-
adapter.each(&block)
|
14
|
-
end
|
15
|
-
|
16
|
-
def initialize(conn, opts, upgrade_headers = nil)
|
17
|
-
@conn = conn
|
18
|
-
@opts = opts
|
19
|
-
@upgrade_headers = upgrade_headers
|
20
|
-
|
21
|
-
@interface = ::HTTP2::Server.new
|
22
|
-
@connection_fiber = Fiber.current
|
23
|
-
@interface.on(:frame, &method(:send_frame))
|
24
|
-
@streams = {}
|
25
|
-
end
|
26
|
-
|
27
|
-
def send_frame(data)
|
28
|
-
@conn << data
|
29
|
-
rescue Exception => e
|
30
|
-
@connection_fiber.transfer e
|
31
|
-
end
|
32
|
-
|
33
|
-
UPGRADE_MESSAGE = <<~HTTP.gsub("\n", "\r\n")
|
34
|
-
HTTP/1.1 101 Switching Protocols
|
35
|
-
Connection: Upgrade
|
36
|
-
Upgrade: h2c
|
37
|
-
|
38
|
-
HTTP
|
39
|
-
|
40
|
-
def upgrade
|
41
|
-
@conn << UPGRADE_MESSAGE
|
42
|
-
settings = @upgrade_headers['HTTP2-Settings']
|
43
|
-
Fiber.current.schedule(nil)
|
44
|
-
@interface.upgrade(settings, @upgrade_headers, '')
|
45
|
-
ensure
|
46
|
-
@upgrade_headers = nil
|
47
|
-
end
|
48
|
-
|
49
|
-
# Iterates over incoming requests
|
50
|
-
def each(&block)
|
51
|
-
@interface.on(:stream) { |stream| start_stream(stream, &block) }
|
52
|
-
upgrade if @upgrade_headers
|
53
|
-
|
54
|
-
while (data = @conn.readpartial(8192))
|
55
|
-
@interface << data
|
56
|
-
snooze
|
57
|
-
end
|
58
|
-
rescue SystemCallError, IOError
|
59
|
-
# ignore
|
60
|
-
ensure
|
61
|
-
finalize_client_loop
|
62
|
-
end
|
63
|
-
|
64
|
-
def start_stream(stream, &block)
|
65
|
-
stream = Stream.new(stream, &block)
|
66
|
-
@streams[stream] = true
|
67
|
-
end
|
68
|
-
|
69
|
-
def finalize_client_loop
|
70
|
-
@interface = nil
|
71
|
-
@streams.each_key(&:stop)
|
72
|
-
@conn.close
|
73
|
-
end
|
74
|
-
|
75
|
-
def close
|
76
|
-
@conn.close
|
77
|
-
end
|
78
|
-
end
|
@@ -1,136 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
export_default :StreamHandler
|
4
|
-
|
5
|
-
require 'http/2'
|
6
|
-
|
7
|
-
Request = import './request'
|
8
|
-
Exceptions = import '../../core/exceptions'
|
9
|
-
|
10
|
-
# Manages an HTTP 2 stream
|
11
|
-
class StreamHandler
|
12
|
-
attr_accessor :__next__
|
13
|
-
|
14
|
-
def initialize(stream, &block)
|
15
|
-
@stream = stream
|
16
|
-
@calling_fiber = Fiber.current
|
17
|
-
@stream_fiber = Fiber.new { |req| handle_request(req, &block) }
|
18
|
-
|
19
|
-
# Stream callbacks occur on the connection fiber (see HTTP2::Protocol#each).
|
20
|
-
# The request handler is run on a separate fiber for each stream, allowing
|
21
|
-
# concurrent handling of incoming requests on the same HTTP/2 connection.
|
22
|
-
#
|
23
|
-
# The different stream adapter APIs suspend the stream fiber, waiting for
|
24
|
-
# stream callbacks to be called. The callbacks, in turn, transfer control to
|
25
|
-
# the stream fiber, effectively causing the return of the adapter API calls.
|
26
|
-
#
|
27
|
-
# Note: the request handler is run once headers are received. Reading the
|
28
|
-
# request body, if present, is at the discretion of the request handler.
|
29
|
-
# This mirrors the behaviour of the HTTP/1 adapter.
|
30
|
-
stream.on(:headers, &method(:on_headers))
|
31
|
-
stream.on(:data, &method(:on_data))
|
32
|
-
stream.on(:half_close, &method(:on_half_close))
|
33
|
-
end
|
34
|
-
|
35
|
-
def handle_request(request, &block)
|
36
|
-
error = nil
|
37
|
-
block.(request)
|
38
|
-
@calling_fiber.transfer
|
39
|
-
rescue Exceptions::MoveOn
|
40
|
-
# ignore
|
41
|
-
rescue Exception => e
|
42
|
-
error = e
|
43
|
-
ensure
|
44
|
-
@done = true
|
45
|
-
@calling_fiber.transfer error
|
46
|
-
end
|
47
|
-
|
48
|
-
def on_headers(headers)
|
49
|
-
@request = Request.new(headers.to_h, self)
|
50
|
-
@stream_fiber.transfer(@request)
|
51
|
-
end
|
52
|
-
|
53
|
-
def on_data(data)
|
54
|
-
if @waiting_for_body_chunk
|
55
|
-
@waiting_for_body_chunk = nil
|
56
|
-
@stream_fiber.transfer(data)
|
57
|
-
else
|
58
|
-
@request.buffer_body_chunk(data)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def on_half_close
|
63
|
-
if @waiting_for_body_chunk
|
64
|
-
@waiting_for_body_chunk = nil
|
65
|
-
@stream_fiber.transfer(nil)
|
66
|
-
elsif @waiting_for_half_close
|
67
|
-
@waiting_for_half_close = nil
|
68
|
-
@stream_fiber.transfer(nil)
|
69
|
-
else
|
70
|
-
@request.complete!
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def protocol
|
75
|
-
'h2'
|
76
|
-
end
|
77
|
-
|
78
|
-
def get_body_chunk
|
79
|
-
# called in the context of the stream fiber
|
80
|
-
return nil if @request.complete?
|
81
|
-
|
82
|
-
@waiting_for_body_chunk = true
|
83
|
-
# the chunk (or an exception) will be returned once the stream fiber is
|
84
|
-
# resumed
|
85
|
-
suspend
|
86
|
-
ensure
|
87
|
-
@waiting_for_body_chunk = nil
|
88
|
-
end
|
89
|
-
|
90
|
-
# Wait for request to finish
|
91
|
-
def consume_request
|
92
|
-
return if @request.complete?
|
93
|
-
|
94
|
-
@waiting_for_half_close = true
|
95
|
-
suspend
|
96
|
-
ensure
|
97
|
-
@waiting_for_half_close = nil
|
98
|
-
end
|
99
|
-
|
100
|
-
# response API
|
101
|
-
def respond(chunk, headers)
|
102
|
-
headers[':status'] ||= '200'
|
103
|
-
@stream.headers(headers, end_stream: false)
|
104
|
-
@stream.data(chunk, end_stream: true)
|
105
|
-
@headers_sent = true
|
106
|
-
end
|
107
|
-
|
108
|
-
def send_headers(headers, empty_response = false)
|
109
|
-
return if @headers_sent
|
110
|
-
|
111
|
-
headers[':status'] ||= (empty_response ? 204 : 200).to_s
|
112
|
-
@stream.headers(headers, end_stream: false)
|
113
|
-
@headers_sent = true
|
114
|
-
end
|
115
|
-
|
116
|
-
def send_chunk(chunk, done: false)
|
117
|
-
send_headers({}, false) unless @headers_sent
|
118
|
-
@stream.data(chunk, end_stream: done)
|
119
|
-
end
|
120
|
-
|
121
|
-
def finish
|
122
|
-
if @headers_sent
|
123
|
-
@stream.close
|
124
|
-
else
|
125
|
-
headers[':status'] ||= '204'
|
126
|
-
@stream.headers(headers, end_stream: true)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
def stop
|
131
|
-
return if @done
|
132
|
-
|
133
|
-
@stream.close
|
134
|
-
@stream_fiber.schedule(Polyphony::MoveOn.new)
|
135
|
-
end
|
136
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
export :load
|
4
|
-
|
5
|
-
require 'rack'
|
6
|
-
|
7
|
-
def run(app)
|
8
|
-
->(req) { respond(req, app.(env(req))) }
|
9
|
-
end
|
10
|
-
|
11
|
-
def load(path)
|
12
|
-
src = IO.read(path)
|
13
|
-
instance_eval(src, path, 1)
|
14
|
-
end
|
15
|
-
|
16
|
-
# Implements a rack input stream:
|
17
|
-
# https://www.rubydoc.info/github/rack/rack/master/file/SPEC#label-The+Input+Stream
|
18
|
-
class InputStream
|
19
|
-
def initialize(request)
|
20
|
-
@request = request
|
21
|
-
end
|
22
|
-
|
23
|
-
def gets; end
|
24
|
-
|
25
|
-
def read(length = nil, outbuf = nil); end
|
26
|
-
|
27
|
-
def each(&block)
|
28
|
-
@request.each_chunk(&block)
|
29
|
-
end
|
30
|
-
|
31
|
-
def rewind; end
|
32
|
-
end
|
33
|
-
|
34
|
-
def env(request)
|
35
|
-
{
|
36
|
-
'REQUEST_METHOD' => request.method,
|
37
|
-
'SCRIPT_NAME' => '',
|
38
|
-
'PATH_INFO' => request.path,
|
39
|
-
'QUERY_STRING' => request.query_string || '',
|
40
|
-
'SERVER_NAME' => request.headers['Host'], # ?
|
41
|
-
'SERVER_PORT' => '80', # ?
|
42
|
-
'rack.version' => Rack::VERSION,
|
43
|
-
'rack.url_scheme' => 'https', # ?
|
44
|
-
'rack.input' => InputStream.new(request),
|
45
|
-
'rack.errors' => STDERR, # ?
|
46
|
-
'rack.multithread' => false,
|
47
|
-
'rack.run_once' => false,
|
48
|
-
'rack.hijack?' => false,
|
49
|
-
'rack.hijack' => nil,
|
50
|
-
'rack.hijack_io' => nil,
|
51
|
-
'rack.session' => nil,
|
52
|
-
'rack.logger' => nil,
|
53
|
-
'rack.multipart.buffer_size' => nil,
|
54
|
-
'rack.multipar.tempfile_factory' => nil
|
55
|
-
}.tap do |env|
|
56
|
-
request.headers.each { |k, v| env["HTTP_#{k.upcase}"] = v }
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def respond(request, (status_code, headers, body))
|
61
|
-
headers[':status'] = status_code.to_s
|
62
|
-
puts "headers: #{headers.inspect}"
|
63
|
-
request.respond(body.first, headers)
|
64
|
-
end
|
@@ -1,118 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
export_default :Request
|
4
|
-
|
5
|
-
require 'uri'
|
6
|
-
|
7
|
-
# HTTP request
|
8
|
-
class Request
|
9
|
-
attr_reader :headers, :adapter
|
10
|
-
attr_accessor :__next__
|
11
|
-
|
12
|
-
def initialize(headers, adapter)
|
13
|
-
@headers = headers
|
14
|
-
@adapter = adapter
|
15
|
-
end
|
16
|
-
|
17
|
-
def protocol
|
18
|
-
@protocol = @adapter.protocol
|
19
|
-
end
|
20
|
-
|
21
|
-
def method
|
22
|
-
@method ||= @headers[':method']
|
23
|
-
end
|
24
|
-
|
25
|
-
def scheme
|
26
|
-
@scheme ||= @headers[':scheme']
|
27
|
-
end
|
28
|
-
|
29
|
-
def uri
|
30
|
-
@uri ||= URI.parse(@headers[':path'] || '')
|
31
|
-
end
|
32
|
-
|
33
|
-
def path
|
34
|
-
@path ||= uri.path
|
35
|
-
end
|
36
|
-
|
37
|
-
def query_string
|
38
|
-
@query_string ||= uri.query
|
39
|
-
end
|
40
|
-
|
41
|
-
def query
|
42
|
-
return @query if @query
|
43
|
-
|
44
|
-
@query = (q = uri.query) ? split_query_string(q) : {}
|
45
|
-
end
|
46
|
-
|
47
|
-
def split_query_string(query)
|
48
|
-
query.split('&').each_with_object({}) do |kv, h|
|
49
|
-
k, v = kv.split('=')
|
50
|
-
h[k.to_sym] = URI.decode_www_form_component(v)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def buffer_body_chunk(chunk)
|
55
|
-
@buffered_body_chunks ||= []
|
56
|
-
@buffered_body_chunks << chunk
|
57
|
-
end
|
58
|
-
|
59
|
-
def each_chunk(&block)
|
60
|
-
if @buffered_body_chunks
|
61
|
-
@buffered_body_chunks.each(&block)
|
62
|
-
@buffered_body_chunks = nil
|
63
|
-
end
|
64
|
-
while !@message_complete && (chunk = @adapter.get_body_chunk)
|
65
|
-
yield chunk
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def complete!(keep_alive = nil)
|
70
|
-
@message_complete = true
|
71
|
-
@keep_alive = keep_alive
|
72
|
-
end
|
73
|
-
|
74
|
-
def complete?
|
75
|
-
@message_complete
|
76
|
-
end
|
77
|
-
|
78
|
-
def consume
|
79
|
-
@adapter.consume_request
|
80
|
-
end
|
81
|
-
|
82
|
-
def keep_alive?
|
83
|
-
@keep_alive
|
84
|
-
end
|
85
|
-
|
86
|
-
def read
|
87
|
-
buf = @buffered_body_chunks ? @buffered_body_chunks.join : +''
|
88
|
-
while (chunk = @adapter.get_body_chunk)
|
89
|
-
buf << chunk
|
90
|
-
end
|
91
|
-
buf
|
92
|
-
end
|
93
|
-
|
94
|
-
def respond(body, headers = {})
|
95
|
-
@adapter.respond(body, headers)
|
96
|
-
@headers_sent = true
|
97
|
-
end
|
98
|
-
|
99
|
-
def send_headers(headers = {}, empty_response = false)
|
100
|
-
return if @headers_sent
|
101
|
-
|
102
|
-
@headers_sent = true
|
103
|
-
@adapter.send_headers(headers, empty_response: empty_response)
|
104
|
-
end
|
105
|
-
|
106
|
-
def send_chunk(body, done: false)
|
107
|
-
send_headers({}) unless @headers_sent
|
108
|
-
|
109
|
-
@adapter.send_chunk(body, done: done)
|
110
|
-
end
|
111
|
-
alias_method :<<, :send_chunk
|
112
|
-
|
113
|
-
def finish
|
114
|
-
send_headers({}) unless @headers_sent
|
115
|
-
|
116
|
-
@adapter.finish
|
117
|
-
end
|
118
|
-
end
|
data/lib/polyphony/websocket.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
export :handler
|
4
|
-
|
5
|
-
require 'digest/sha1'
|
6
|
-
require 'websocket'
|
7
|
-
|
8
|
-
# Websocket connection
|
9
|
-
class WebsocketConnection
|
10
|
-
def initialize(client, headers)
|
11
|
-
@client = client
|
12
|
-
@headers = headers
|
13
|
-
setup(headers)
|
14
|
-
end
|
15
|
-
|
16
|
-
S_WS_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
|
17
|
-
UPGRADE_RESPONSE = <<~HTTP.gsub("\n", "\r\n")
|
18
|
-
HTTP/1.1 101 Switching Protocols
|
19
|
-
Upgrade: websocket
|
20
|
-
Connection: Upgrade
|
21
|
-
Sec-WebSocket-Accept: %<accept>s
|
22
|
-
|
23
|
-
HTTP
|
24
|
-
|
25
|
-
def setup(headers)
|
26
|
-
key = headers['Sec-WebSocket-Key']
|
27
|
-
@version = headers['Sec-WebSocket-Version'].to_i
|
28
|
-
accept = Digest::SHA1.base64digest([key, S_WS_GUID].join)
|
29
|
-
@client << format(UPGRADE_RESPONSE, accept: accept)
|
30
|
-
|
31
|
-
@reader = ::WebSocket::Frame::Incoming::Server.new(version: @version)
|
32
|
-
end
|
33
|
-
|
34
|
-
def recv
|
35
|
-
loop do
|
36
|
-
data = @client.readpartial(8192)
|
37
|
-
break nil unless data
|
38
|
-
|
39
|
-
@reader << data
|
40
|
-
if (msg = @reader.next)
|
41
|
-
break msg.to_s
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def send(data)
|
47
|
-
frame = ::WebSocket::Frame::Outgoing::Server.new(
|
48
|
-
version: @version, data: data, type: :text
|
49
|
-
)
|
50
|
-
@client << frame.to_s
|
51
|
-
end
|
52
|
-
alias_method :<<, :send
|
53
|
-
end
|
54
|
-
|
55
|
-
def handler(&block)
|
56
|
-
proc { |client, header|
|
57
|
-
block.(WebsocketConnection.new(client, header))
|
58
|
-
}
|
59
|
-
end
|