opal-up 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE +209 -0
- data/README.md +97 -29
- data/bin/up_ruby +4 -0
- data/bin/up_ruby_cluster +4 -0
- data/ext/up_ext/App.h +606 -0
- data/ext/up_ext/AsyncSocket.h +355 -0
- data/ext/up_ext/AsyncSocketData.h +87 -0
- data/ext/up_ext/BloomFilter.h +83 -0
- data/ext/up_ext/ChunkedEncoding.h +236 -0
- data/ext/up_ext/ClientApp.h +36 -0
- data/ext/up_ext/HttpContext.h +502 -0
- data/ext/up_ext/HttpContextData.h +56 -0
- data/ext/up_ext/HttpErrors.h +53 -0
- data/ext/up_ext/HttpParser.h +680 -0
- data/ext/up_ext/HttpResponse.h +578 -0
- data/ext/up_ext/HttpResponseData.h +95 -0
- data/ext/up_ext/HttpRouter.h +380 -0
- data/ext/up_ext/Loop.h +204 -0
- data/ext/up_ext/LoopData.h +112 -0
- data/ext/up_ext/MoveOnlyFunction.h +377 -0
- data/ext/up_ext/PerMessageDeflate.h +315 -0
- data/ext/up_ext/ProxyParser.h +163 -0
- data/ext/up_ext/QueryParser.h +120 -0
- data/ext/up_ext/TopicTree.h +363 -0
- data/ext/up_ext/Utilities.h +66 -0
- data/ext/up_ext/WebSocket.h +381 -0
- data/ext/up_ext/WebSocketContext.h +434 -0
- data/ext/up_ext/WebSocketContextData.h +109 -0
- data/ext/up_ext/WebSocketData.h +86 -0
- data/ext/up_ext/WebSocketExtensions.h +256 -0
- data/ext/up_ext/WebSocketHandshake.h +145 -0
- data/ext/up_ext/WebSocketProtocol.h +506 -0
- data/ext/up_ext/bsd.c +767 -0
- data/ext/up_ext/bsd.h +109 -0
- data/ext/up_ext/context.c +524 -0
- data/ext/up_ext/epoll_kqueue.c +458 -0
- data/ext/up_ext/epoll_kqueue.h +67 -0
- data/ext/up_ext/extconf.rb +5 -0
- data/ext/up_ext/internal.h +224 -0
- data/ext/up_ext/libusockets.h +350 -0
- data/ext/up_ext/libuwebsockets.cpp +1344 -0
- data/ext/up_ext/libuwebsockets.h +396 -0
- data/ext/up_ext/loop.c +386 -0
- data/ext/up_ext/loop_data.h +38 -0
- data/ext/up_ext/socket.c +231 -0
- data/ext/up_ext/up_ext.c +930 -0
- data/lib/up/bun/rack_env.rb +1 -13
- data/lib/up/bun/server.rb +93 -19
- data/lib/up/cli.rb +3 -0
- data/lib/up/client.rb +68 -0
- data/lib/up/ruby/cluster.rb +39 -0
- data/lib/up/ruby/cluster_cli.rb +10 -0
- data/lib/up/{node → ruby}/rack_cluster.rb +5 -4
- data/lib/up/{node → ruby}/rack_server.rb +4 -4
- data/lib/up/ruby/server_cli.rb +10 -0
- data/lib/up/u_web_socket/cluster.rb +18 -3
- data/lib/up/u_web_socket/server.rb +108 -15
- data/lib/up/version.rb +1 -1
- metadata +72 -30
- data/.gitignore +0 -5
- data/Gemfile +0 -2
- data/bin/up_node +0 -12
- data/bin/up_node_cluster +0 -12
- data/example_rack_app/Gemfile +0 -3
- data/example_rack_app/config.ru +0 -6
- data/example_rack_app/rack_app.rb +0 -5
- data/example_roda_app/Gemfile +0 -6
- data/example_roda_app/config.ru +0 -6
- data/example_roda_app/roda_app.rb +0 -37
- data/example_sinatra_app/Gemfile +0 -6
- data/example_sinatra_app/config.ru +0 -6
- data/example_sinatra_app/sinatra_app.rb +0 -7
- data/lib/up/node/cluster.rb +0 -39
- data/lib/up/node/cluster_cli.rb +0 -15
- data/lib/up/node/rack_env.rb +0 -106
- data/lib/up/node/server.rb +0 -84
- data/lib/up/node/server_cli.rb +0 -15
- data/lib/up/u_web_socket/rack_env.rb +0 -101
- data/opal-up.gemspec +0 -27
- data/up_logo.svg +0 -256
data/lib/up/bun/rack_env.rb
CHANGED
@@ -23,26 +23,14 @@ module Up
|
|
23
23
|
self[key] = case key
|
24
24
|
when 'rack.errors'
|
25
25
|
STDERR
|
26
|
-
when 'rack.hijack'
|
27
|
-
nil
|
28
|
-
when 'rack.hijack?'
|
29
|
-
false
|
30
|
-
when 'rack.input'
|
31
|
-
::IO.new
|
32
26
|
when 'rack.logger'
|
33
|
-
|
27
|
+
@config[:logger]
|
34
28
|
when 'rack.multipart.buffer_size'
|
35
29
|
4096
|
36
30
|
when 'rack.multipart.tempfile_factory'
|
37
31
|
proc { |_filename, _content_type| File.new }
|
38
|
-
when 'rack.response_finished'
|
39
|
-
[]
|
40
32
|
when 'rack.session'
|
41
33
|
{}
|
42
|
-
when 'rack.upgrade'
|
43
|
-
nil
|
44
|
-
when 'rack.upgrade?'
|
45
|
-
nil
|
46
34
|
when 'rack.url_scheme'
|
47
35
|
@config[:scheme]
|
48
36
|
when 'PATH_INFO'
|
data/lib/up/bun/server.rb
CHANGED
@@ -1,36 +1,37 @@
|
|
1
1
|
# backtick_javascript: true
|
2
|
+
require 'logger'
|
2
3
|
require 'up/cli'
|
3
|
-
require 'up/
|
4
|
+
require 'up/client'
|
4
5
|
|
5
6
|
module Up
|
6
7
|
module Bun
|
7
8
|
class Server
|
8
|
-
def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil)
|
9
|
+
def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil, logger: Logger.new(STDERR))
|
9
10
|
@app = app
|
10
11
|
@scheme = scheme || 'http'
|
11
12
|
raise "unsupported scheme #{@scheme}" unless %w[http https].include?(@scheme)
|
12
13
|
@host = host || 'localhost'
|
13
14
|
@port = port&.to_i || 3000
|
14
|
-
@config = { handler: self.class.name, engine: "bun/#{`process.version`}", port: port, scheme: scheme, host: host }.freeze
|
15
|
+
@config = { handler: self.class.name, engine: "bun/#{`process.version`}", port: port, scheme: scheme, host: host, logger: logger }.freeze
|
15
16
|
@ca_file = ca_file
|
16
17
|
@cert_file = cert_file
|
17
18
|
@key_file = key_file
|
18
|
-
@
|
19
|
+
@default_input = IO.new
|
20
|
+
@server = nil
|
21
|
+
@logger = logger
|
22
|
+
@t_factory = proc { |filename, _content_type| File.new(filename, 'a+') }
|
19
23
|
end
|
20
24
|
|
21
25
|
%x{
|
22
26
|
self.handle_headers = function(rack_headers, bun_hdr) {
|
23
27
|
if (rack_headers.$$is_hash) {
|
24
|
-
var header,
|
28
|
+
var header, v;
|
25
29
|
for(header of rack_headers) {
|
26
|
-
|
27
|
-
if (
|
28
|
-
v =
|
29
|
-
if (v.$$is_array) {
|
30
|
-
v = v.join("\n");
|
31
|
-
}
|
32
|
-
bun_hdr.set(k, v);
|
30
|
+
v = header[1];
|
31
|
+
if (v.$$is_array) {
|
32
|
+
v = v.join("\n");
|
33
33
|
}
|
34
|
+
bun_hdr.set(header[0].toLowerCase(), v);
|
34
35
|
}
|
35
36
|
}
|
36
37
|
}
|
@@ -49,20 +50,93 @@ module Up
|
|
49
50
|
def listen
|
50
51
|
raise "already running" if @server
|
51
52
|
%x{
|
52
|
-
const oubr = Opal.Up.Bun.RackEnv;
|
53
53
|
const oubs = Opal.Up.Bun.Server;
|
54
|
-
|
54
|
+
const ouwc = Opal.Up.Client;
|
55
|
+
const deco = new TextDecoder();
|
55
56
|
var server_options = {
|
56
57
|
port: #@port,
|
57
58
|
hostname: #@host,
|
58
59
|
development: false,
|
59
|
-
fetch(req) {
|
60
|
-
const
|
61
|
-
const
|
62
|
-
|
60
|
+
fetch(req, server) {
|
61
|
+
const upgrade = req.headers.get('Upgrade');
|
62
|
+
const env = new Map();
|
63
|
+
env.set('rack.errors',#{STDERR});
|
64
|
+
env.set('rack.input', #@default_input);
|
65
|
+
env.set('rack.logger', #@logger);
|
66
|
+
env.set('rack.multipart.buffer_size', 4096);
|
67
|
+
env.set('rack.multipart.tempfile_factory', #@t_factory);
|
68
|
+
if (upgrade) {
|
69
|
+
env.set('rack.upgrade?', #{:websocket});
|
70
|
+
}
|
71
|
+
env.set('rack.url_scheme', #@scheme);
|
72
|
+
env.set('SCRIPT_NAME', "");
|
73
|
+
env.set('SERVER_PROTOCOL', req.httpVersion);
|
74
|
+
env.set('HTTP_VERSION', req.httpVersion);
|
75
|
+
env.set('SERVER_NAME', #@host);
|
76
|
+
env.set('SERVER_PORT', #@port);
|
77
|
+
env.set('QUERY_STRING', "");
|
78
|
+
env.set('REQUEST_METHOD', req.method);
|
79
|
+
env.set('PATH_INFO', req.url);
|
80
|
+
req.headers.forEach((k, v) => { env.set('HTTP_' + k.toUpperCase().replaceAll('-', '_'), v) });
|
81
|
+
const rack_res = #@app.$call(env);
|
82
|
+
if (upgrade) {
|
83
|
+
const handler = env.get('rack.upgrade');
|
84
|
+
if (rack_res[0] < 300 && handler && handler !== nil) {
|
85
|
+
const client = ouwc.$new();
|
86
|
+
client.env = env;
|
87
|
+
client.open = false;
|
88
|
+
client.handler = handler
|
89
|
+
client.protocol = #{:websocket};
|
90
|
+
client.server = server;
|
91
|
+
client.timeout = 120;
|
92
|
+
server.upgrade(req, { data: { client: client }});
|
93
|
+
return;
|
94
|
+
}
|
95
|
+
}
|
96
|
+
const hdrs = new Headers();
|
97
|
+
oubs.handle_headers(rack_res[1], hdrs);
|
63
98
|
var body = '';
|
64
99
|
body = oubs.handle_response(rack_res[2], body);
|
65
|
-
return new Response(body, {status: rack_res[0], statusText: 'OK', headers:
|
100
|
+
return new Response(body, {status: rack_res[0], statusText: 'OK', headers: hdrs});
|
101
|
+
},
|
102
|
+
websocket: {
|
103
|
+
close: (ws) => {
|
104
|
+
const client = ws.data.client;
|
105
|
+
if (typeof(client.handler.$on_close) === 'function') {
|
106
|
+
client.ws = ws;
|
107
|
+
client.open = false;
|
108
|
+
client.handler.$on_close(client);
|
109
|
+
client.ws = null;
|
110
|
+
}
|
111
|
+
},
|
112
|
+
drain: (ws) => {
|
113
|
+
const client = ws.data.client;
|
114
|
+
if (typeof(client.handler.$on_drained) === 'function') {
|
115
|
+
client.ws = ws;
|
116
|
+
client.handler.$on_drained(client);
|
117
|
+
client.ws = null;
|
118
|
+
}
|
119
|
+
},
|
120
|
+
message: (ws, message) => {
|
121
|
+
const client = ws.data.client;
|
122
|
+
if (typeof(client.handler.$on_message) === 'function') {
|
123
|
+
if (typeof(message) !== 'string') {
|
124
|
+
message = deco.decode(message);
|
125
|
+
}
|
126
|
+
client.ws = ws;
|
127
|
+
client.handler.$on_message(client, message);
|
128
|
+
client.ws = null;
|
129
|
+
}
|
130
|
+
},
|
131
|
+
open: (ws) => {
|
132
|
+
const client = ws.data.client;
|
133
|
+
if (typeof(client.handler.$on_open) === 'function') {
|
134
|
+
client.ws = ws;
|
135
|
+
client.open = true;
|
136
|
+
client.handler.$on_open(client);
|
137
|
+
client.ws = null;
|
138
|
+
}
|
139
|
+
}
|
66
140
|
}
|
67
141
|
};
|
68
142
|
if (#@scheme === 'https') {
|
data/lib/up/cli.rb
CHANGED
@@ -32,6 +32,9 @@ module Up
|
|
32
32
|
on('-k', '--key-file FILE', String, 'File with the servers certificate') do |key_file|
|
33
33
|
options[:key_file] = key_file
|
34
34
|
end
|
35
|
+
on('-l', '--log-file FILE', String, 'log file') do |log_file|
|
36
|
+
options[:logger] = Logger.new(File.new(log_file, 'a+'))
|
37
|
+
end
|
35
38
|
on('-v', '--version', 'Show version') do
|
36
39
|
puts "Up! v#{Up::VERSION}"
|
37
40
|
exit
|
data/lib/up/client.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# backtick_javascript: true
|
2
|
+
if RUBY_ENGINE == 'opal'
|
3
|
+
%x{
|
4
|
+
const process = require('node:process');
|
5
|
+
}
|
6
|
+
end
|
7
|
+
|
8
|
+
module Up
|
9
|
+
class Client
|
10
|
+
# instance vars are set by the server
|
11
|
+
|
12
|
+
attr_reader :env, :handler, :protocol, :timeout
|
13
|
+
|
14
|
+
def handler=(h)
|
15
|
+
@handler.on_close(self)
|
16
|
+
@handler = h
|
17
|
+
@handler.on_open(self)
|
18
|
+
end
|
19
|
+
|
20
|
+
def open?
|
21
|
+
@open
|
22
|
+
end
|
23
|
+
|
24
|
+
def pubsub?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
if RUBY_ENGINE == 'opal'
|
29
|
+
def close
|
30
|
+
@open = false
|
31
|
+
`#@ws?.close()`
|
32
|
+
end
|
33
|
+
|
34
|
+
def pending
|
35
|
+
return -1 unless @open
|
36
|
+
`#@ws?.getBufferedAmount()`
|
37
|
+
end
|
38
|
+
|
39
|
+
def publish(channel, message, engine = nil)
|
40
|
+
res = false
|
41
|
+
raise 'publish engine not supported' if engine
|
42
|
+
%x{
|
43
|
+
if (!message.$$is_string) {
|
44
|
+
message = JSON.stringify(message);
|
45
|
+
}
|
46
|
+
res = #@server?.publish(channel, message);
|
47
|
+
if (engine !== false && self.worker) {
|
48
|
+
process.send({c: channel, m: message});
|
49
|
+
}
|
50
|
+
}
|
51
|
+
res
|
52
|
+
end
|
53
|
+
|
54
|
+
def subscribe(channel, is_pattern = false, &block)
|
55
|
+
@sub_block = block
|
56
|
+
`#@ws?.subscribe(channel)`
|
57
|
+
end
|
58
|
+
|
59
|
+
def unsubscribe(channel, is_pattern = false)
|
60
|
+
`#@ws?.unsubscribe(channel)`
|
61
|
+
end
|
62
|
+
|
63
|
+
def write(data)
|
64
|
+
`#@ws?.send(data, false)`
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# backtick_javascript: true
|
2
|
+
require 'etc'
|
3
|
+
require 'random/formatter'
|
4
|
+
require 'up_ext'
|
5
|
+
|
6
|
+
module Up
|
7
|
+
module Ruby
|
8
|
+
class Cluster < Up::Ruby::Server
|
9
|
+
def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil, logger: Logger.new(STDERR), workers: nil)
|
10
|
+
super(app: app, host: host, port: port)
|
11
|
+
@secret = Random.uuid
|
12
|
+
@workers = workers || Etc.nprocessors
|
13
|
+
@members = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def listen
|
17
|
+
raise "already running" unless @members.empty?
|
18
|
+
@workers.times do
|
19
|
+
@members << fork do
|
20
|
+
@member_id = @members.size + 1
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
Process.waitall
|
26
|
+
@members.each do |member|
|
27
|
+
Process.kill("KILL", member)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def stop
|
32
|
+
if Up::CLI::stoppable?
|
33
|
+
@members.each { |m| Process.kill(m) }
|
34
|
+
@members.clear
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,11 +1,12 @@
|
|
1
|
-
require '
|
1
|
+
require 'up_ext'
|
2
|
+
require 'up/ruby/cluster'
|
2
3
|
|
3
4
|
module Up
|
4
|
-
module
|
5
|
+
module Ruby
|
5
6
|
module RackCluster
|
6
7
|
def self.run(app, options = {})
|
7
8
|
raise "already running" if @server
|
8
|
-
@server = Up::
|
9
|
+
@server = Up::Ruby::Cluster.new(app: app, **options).listen
|
9
10
|
true
|
10
11
|
end
|
11
12
|
|
@@ -20,6 +21,6 @@ end
|
|
20
21
|
ENV['RACK_HANDLER'] ||= 'up'
|
21
22
|
|
22
23
|
begin
|
23
|
-
::Rackup::Handler.register('up', Up::
|
24
|
+
::Rackup::Handler.register('up', Up::Ruby::RackCluster) if defined?(::Rackup::Handler)
|
24
25
|
rescue StandardError
|
25
26
|
end
|
@@ -1,11 +1,11 @@
|
|
1
|
-
require '
|
1
|
+
require 'up_ext'
|
2
2
|
|
3
3
|
module Up
|
4
|
-
module
|
4
|
+
module Ruby
|
5
5
|
module RackServer
|
6
6
|
def self.run(app, options = {})
|
7
7
|
raise "already running" if @server
|
8
|
-
@server = Up::
|
8
|
+
@server = Up::Ruby::Server.new(app: app, **options).listen
|
9
9
|
true
|
10
10
|
end
|
11
11
|
|
@@ -20,6 +20,6 @@ end
|
|
20
20
|
ENV['RACK_HANDLER'] ||= 'up'
|
21
21
|
|
22
22
|
begin
|
23
|
-
::Rackup::Handler.register('up', Up::
|
23
|
+
::Rackup::Handler.register('up', Up::Ruby::RackServer) if defined?(::Rackup::Handler)
|
24
24
|
rescue StandardError
|
25
25
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
require 'up/u_web_socket/server'
|
3
3
|
|
4
4
|
%x{
|
5
|
+
const process = require('node:process');
|
5
6
|
const cluster = require('node:cluster');
|
6
7
|
const num_workers = require('node:os').availableParallelism();
|
7
8
|
}
|
@@ -9,8 +10,8 @@ require 'up/u_web_socket/server'
|
|
9
10
|
module Up
|
10
11
|
module UWebSocket
|
11
12
|
class Cluster < Up::UWebSocket::Server
|
12
|
-
def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil, workers: nil)
|
13
|
-
super(app: app, host: host, port: port, scheme: scheme, ca_file: ca_file, cert_file: cert_file, key_file: key_file)
|
13
|
+
def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil, logger: Logger.new(STDERR), workers: nil)
|
14
|
+
super(app: app, host: host, port: port, scheme: scheme, ca_file: ca_file, cert_file: cert_file, key_file: key_file, logger: logger)
|
14
15
|
@workers = workers || `num_workers`
|
15
16
|
@members = []
|
16
17
|
end
|
@@ -19,10 +20,24 @@ module Up
|
|
19
20
|
raise "already running" unless @members.empty?
|
20
21
|
%x{
|
21
22
|
if (cluster.isPrimary) {
|
23
|
+
cluster.on('message', (worker, message, handle) => {
|
24
|
+
if (message.c && message.m) {
|
25
|
+
for (let member of #@members) {
|
26
|
+
if (member !== worker) {
|
27
|
+
member.send(message);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
});
|
22
32
|
for (let i = 0; i < #@workers; i++) {
|
23
|
-
#@members
|
33
|
+
#@members[i] = cluster.fork();
|
24
34
|
}
|
25
35
|
} else {
|
36
|
+
self.worker = true;
|
37
|
+
function process_message_handler(message, handle) {
|
38
|
+
self.server.publish(message.c, message.m);
|
39
|
+
}
|
40
|
+
process.on('message', process_message_handler);
|
26
41
|
#{super}
|
27
42
|
}
|
28
43
|
}
|
@@ -1,8 +1,10 @@
|
|
1
1
|
# backtick_javascript: true
|
2
|
+
require 'logger'
|
2
3
|
require 'up/cli'
|
3
|
-
require 'up/
|
4
|
+
require 'up/client'
|
4
5
|
|
5
6
|
%x{
|
7
|
+
const process = require('node:process');
|
6
8
|
module.paths.push(process.cwd() + '/node_modules');
|
7
9
|
const uws = require('uWebSockets.js');
|
8
10
|
}
|
@@ -10,32 +12,32 @@ require 'up/u_web_socket/rack_env'
|
|
10
12
|
module Up
|
11
13
|
module UWebSocket
|
12
14
|
class Server
|
13
|
-
def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil)
|
15
|
+
def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil, logger: Logger.new(STDERR))
|
14
16
|
@app = app
|
15
17
|
@scheme = scheme || 'http'
|
16
18
|
raise "unsupported scheme #{@scheme}" unless %w[http https].include?(@scheme)
|
17
19
|
@host = host || 'localhost'
|
18
20
|
@port = port&.to_i || 3000
|
19
|
-
@config = { handler: self.class.name, engine: "node/#{`process.version`}"
|
21
|
+
@config = { handler: self.class.name, engine: "node/#{`process.version`}" }.freeze
|
20
22
|
@ca_file = ca_file
|
21
23
|
@cert_file = cert_file
|
22
24
|
@key_file = key_file
|
23
|
-
@
|
25
|
+
@default_input = IO.new
|
26
|
+
@server = nil
|
27
|
+
@logger = logger
|
28
|
+
@t_factory = proc { |filename, _content_type| File.new(filename, 'a+') }
|
24
29
|
end
|
25
30
|
|
26
31
|
%x{
|
27
32
|
self.handle_headers = function(rack_headers, uws_res) {
|
28
33
|
if (rack_headers.$$is_hash) {
|
29
|
-
var header,
|
34
|
+
var header, v;
|
30
35
|
for(header of rack_headers) {
|
31
|
-
|
32
|
-
if (
|
33
|
-
v =
|
34
|
-
if (v.$$is_array) {
|
35
|
-
v = v.join("\n");
|
36
|
-
}
|
37
|
-
uws_res.writeHeader(k, v);
|
36
|
+
v = header[1];
|
37
|
+
if (v.$$is_array) {
|
38
|
+
v = v.join("\n");
|
38
39
|
}
|
40
|
+
uws_res.writeHeader(header[0].toLowerCase(), v);
|
39
41
|
}
|
40
42
|
}
|
41
43
|
}
|
@@ -48,25 +50,116 @@ module Up
|
|
48
50
|
}
|
49
51
|
#{`parts`.close if `parts`.respond_to?(:close)}
|
50
52
|
}
|
53
|
+
|
54
|
+
self.prepare_env = function(req) {
|
55
|
+
const env = new Map();
|
56
|
+
env.set('rack.errors',#{STDERR});
|
57
|
+
env.set('rack.input', #@default_input);
|
58
|
+
env.set('rack.logger', #@logger);
|
59
|
+
env.set('rack.multipart.buffer_size', 4096);
|
60
|
+
env.set('rack.multipart.tempfile_factory', #@t_factory);
|
61
|
+
env.set('rack.url_scheme', #@scheme);
|
62
|
+
env.set('SCRIPT_NAME', "");
|
63
|
+
env.set('SERVER_PROTOCOL', 'HTTP/1.1');
|
64
|
+
env.set('HTTP_VERSION', 'HTTP/1.1');
|
65
|
+
env.set('SERVER_NAME', #@host);
|
66
|
+
env.set('SERVER_PORT', #@port);
|
67
|
+
env.set('QUERY_STRING', req.getQuery());
|
68
|
+
env.set('REQUEST_METHOD', req.getMethod().toUpperCase());
|
69
|
+
env.set('PATH_INFO', req.getUrl());
|
70
|
+
req.forEach((k, v) => { env.set('HTTP_' + k.toUpperCase().replaceAll('-', '_'), v) });
|
71
|
+
return env;
|
72
|
+
}
|
51
73
|
}
|
52
74
|
|
53
75
|
def listen
|
54
76
|
raise "already running" if @server
|
55
77
|
%x{
|
56
|
-
const ouwr = Opal.Up.UWebSocket.RackEnv;
|
57
78
|
const ouws = Opal.Up.UWebSocket.Server;
|
79
|
+
const ouwc = Opal.Up.Client;
|
80
|
+
const deco = new TextDecoder();
|
58
81
|
if (#@scheme == 'https') {
|
59
82
|
#@server = uws.SSLApp({ ca_file_name: #@ca_file, cert_file_name: #@cert_file, key_file_name: #@key_file });
|
60
83
|
} else {
|
61
84
|
#@server = uws.App();
|
62
85
|
}
|
63
86
|
#@server.any('/*', (res, req) => {
|
64
|
-
const rack_res = #@app.$call(
|
65
|
-
res.writeStatus(
|
87
|
+
const rack_res = #@app.$call(ouws.prepare_env(req));
|
88
|
+
res.writeStatus(rack_res[0].toString() + ' OK');
|
66
89
|
ouws.handle_headers(rack_res[1], res);
|
67
90
|
ouws.handle_response(rack_res[2], res);
|
68
91
|
res.end();
|
69
92
|
});
|
93
|
+
#@server.ws('/*', {
|
94
|
+
close: (ws, code, message) => {
|
95
|
+
const user_data = ws.getUserData();
|
96
|
+
if (typeof(user_data.client.handler.$on_close) === 'function') {
|
97
|
+
user_data.client.ws = ws;
|
98
|
+
user_data.client.open = false;
|
99
|
+
user_data.client.handler.$on_close(user_data.client);
|
100
|
+
user_data.client.ws = null;
|
101
|
+
}
|
102
|
+
},
|
103
|
+
drain: (ws) => {
|
104
|
+
const user_data = ws.getUserData();
|
105
|
+
if (typeof(user_data.client.handler.$on_drained) === 'function') {
|
106
|
+
user_data.client.ws = ws;
|
107
|
+
user_data.client.handler.$on_drained(user_data.client);
|
108
|
+
user_data.client.ws = null;
|
109
|
+
}
|
110
|
+
},
|
111
|
+
message: (ws, message, isBinary) => {
|
112
|
+
const user_data = ws.getUserData();
|
113
|
+
if (typeof(user_data.client.handler.$on_message) === 'function') {
|
114
|
+
const msg = deco.decode(message);
|
115
|
+
user_data.client.ws = ws;
|
116
|
+
user_data.client.handler.$on_message(user_data.client, msg);
|
117
|
+
user_data.client.ws = null;
|
118
|
+
}
|
119
|
+
},
|
120
|
+
open: (ws) => {
|
121
|
+
const user_data = ws.getUserData();
|
122
|
+
if (typeof(user_data.client.handler.$on_open) === 'function') {
|
123
|
+
user_data.client.ws = ws;
|
124
|
+
user_data.client.open = true;
|
125
|
+
user_data.client.handler.$on_open(user_data.client);
|
126
|
+
user_data.client.ws = null;
|
127
|
+
}
|
128
|
+
},
|
129
|
+
sendPingsAutomatically: true,
|
130
|
+
upgrade: (res, req, context) => {
|
131
|
+
const env = ouws.prepare_env(req);
|
132
|
+
env.set('rack.upgrade?', #{:websocket});
|
133
|
+
const rack_res = #@app.$call(env);
|
134
|
+
const handler = env.get('rack.upgrade');
|
135
|
+
if (rack_res[0] < 300 && handler && handler !== nil) {
|
136
|
+
const client = ouwc.$new();
|
137
|
+
client.env = env;
|
138
|
+
client.open = false;
|
139
|
+
client.handler = handler
|
140
|
+
client.protocol = #{:websocket};
|
141
|
+
client.server = self.server;
|
142
|
+
client.timeout = 120;
|
143
|
+
if (self.worker) {
|
144
|
+
client.worker = true;
|
145
|
+
}
|
146
|
+
res.upgrade({ client: client },
|
147
|
+
req.getHeader('sec-websocket-key'),
|
148
|
+
req.getHeader('sec-websocket-protocol'),
|
149
|
+
req.getHeader('sec-websocket-extensions'),
|
150
|
+
context);
|
151
|
+
} else {
|
152
|
+
if (rack_res[0] >= 300) {
|
153
|
+
env.delete('rack.upgrade');
|
154
|
+
}
|
155
|
+
res.writeStatus(rack_res[0].toString() + ' OK');
|
156
|
+
ouws.handle_headers(rack_res[1], res);
|
157
|
+
ouws.handle_response(rack_res[2], res);
|
158
|
+
res.end();
|
159
|
+
}
|
160
|
+
},
|
161
|
+
|
162
|
+
});
|
70
163
|
#@server.listen(#@port, #@host, () => { console.log(`Server is running on ${#@scheme}://${#@host}:${#@port}`)});
|
71
164
|
}
|
72
165
|
end
|
data/lib/up/version.rb
CHANGED