opal-up 0.0.1 → 0.0.2
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/README.md +46 -14
- data/bin/up +5 -22
- data/bin/up_bun +5 -16
- data/bin/up_cluster +12 -0
- data/bin/up_node +12 -0
- data/bin/up_node_cluster +12 -0
- data/example_roda_app/Gemfile +2 -2
- data/example_roda_app/roda_app.rb +2 -0
- data/example_sinatra_app/Gemfile +1 -1
- data/lib/up/bun/rack_env.rb +104 -0
- data/lib/up/bun/rack_server.rb +25 -0
- data/lib/up/bun/server.rb +89 -0
- data/lib/up/bun/server_cli.rb +15 -0
- data/lib/up/cli.rb +151 -0
- data/lib/up/node/cluster.rb +39 -0
- data/lib/up/node/cluster_cli.rb +15 -0
- data/lib/up/node/rack_cluster.rb +25 -0
- data/lib/up/node/rack_env.rb +106 -0
- data/lib/up/node/rack_server.rb +25 -0
- data/lib/up/node/server.rb +84 -0
- data/lib/up/node/server_cli.rb +15 -0
- data/lib/up/u_web_socket/cluster.rb +39 -0
- data/lib/up/u_web_socket/cluster_cli.rb +15 -0
- data/lib/up/u_web_socket/rack_cluster.rb +25 -0
- data/lib/up/u_web_socket/rack_env.rb +101 -0
- data/lib/up/u_web_socket/rack_server.rb +25 -0
- data/lib/up/u_web_socket/server.rb +82 -0
- data/lib/up/u_web_socket/server_cli.rb +15 -0
- data/lib/up/version.rb +1 -1
- data/opal-up.gemspec +1 -1
- metadata +27 -13
- data/lib/up/bun.rb +0 -91
- data/lib/up/bun_cli.rb +0 -32
- data/lib/up/bun_rack.rb +0 -19
- data/lib/up/bun_rack_env.rb +0 -102
- data/lib/up/u_web_socket.rb +0 -83
- data/lib/up/u_web_socket_cluster.rb +0 -89
- data/lib/up/uws_cli.rb +0 -32
- data/lib/up/uws_rack.rb +0 -19
- data/lib/up/uws_rack_env.rb +0 -99
- data/lib/up-bun.rb +0 -7
- data/lib/up-node.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2d20b2473096aef989d7b0d966ae78805db8798bc3d0957e9785252610cb7717
|
4
|
+
data.tar.gz: 26fd5a5a1fd37c574844bfad0abefc4af06a3eae4f2d305f7e3a0fed2bf0bb23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dfe04b8cc20af1634eedb70b6d782e85ed6c2b7faf8d318c7efac9c216d11439293427d9e143656559fd26343466bd5aee4a58a51ff846ce9ca034e3c25ef980
|
7
|
+
data.tar.gz: 1feec67ff1e68b488a49bbe2d583ea981ea6dab797c338faeec0fd8b0991b41814f558b435f38054508fd3d26141dde878f3d4c10abbfe06a661c39245d9c39e
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
<img src="https://raw.githubusercontent.com/janbiedermann/up/master/up_logo.svg" alt="UP Logo">
|
2
2
|
<small>Original Image by <a href="https://www.freepik.com/free-vector/colorful-arrows_715199.htm#query=up&position=3&from_view=search&track=sph&uuid=63f9eddf-02a6-4e5c-8178-8cfa507ee33d">Freepik</a>, modified though</small>
|
3
3
|
|
4
|
-
# UP
|
4
|
+
# UP!
|
5
5
|
|
6
6
|
A high performance Rack server for Opal Ruby, Tech Demo
|
7
7
|
|
@@ -9,28 +9,32 @@ A high performance Rack server for Opal Ruby, Tech Demo
|
|
9
9
|
|
10
10
|
```
|
11
11
|
Requests per second:
|
12
|
-
Puma:
|
13
|
-
Iodine:
|
14
|
-
Up
|
12
|
+
Puma: 6391.01 req/s
|
13
|
+
Iodine: 18645.58 req/s
|
14
|
+
Up! node: 3801.21 req/s
|
15
|
+
Up! uWS: 20258.46 req/s <<< fastest
|
15
16
|
|
16
17
|
Time per Request, mean, across all concurrent requests:
|
17
|
-
Puma:
|
18
|
-
Iodine:
|
19
|
-
Up
|
20
|
-
|
21
|
-
|
18
|
+
Puma: 0.156ms
|
19
|
+
Iodine: 0.054ms
|
20
|
+
Up! node: 0.275ms
|
21
|
+
Up! uWS: 0.049ms <<< fastest
|
22
|
+
|
23
|
+
running on Linux with:
|
22
24
|
ruby 3.3.0, YJit enabled
|
23
|
-
Opal 1.8.2 with node
|
25
|
+
Opal 1.8.2 with node v20.11.0
|
24
26
|
Puma 6.4.2, 4 workers, 4 threads
|
25
27
|
Iodine 0.7.57, 4 workers, 4 threads
|
26
|
-
Up! 0.0.1, 1 worker, no threads
|
28
|
+
Up! uWS 0.0.1, 1 worker, no threads
|
29
|
+
Up! Node 0.0.2, 4 workers, no threads
|
27
30
|
|
28
31
|
running the example_rack_app from this repo, benchmarked with:
|
29
|
-
|
32
|
+
ab -n 100000 -c 10 http://localhost:3000/
|
33
|
+
```
|
30
34
|
|
31
35
|
## Introduction
|
32
36
|
|
33
|
-
This is currently mainly a technical demonstration, demonstrating the speed of the Opal Ruby implementation employing Node and UWebSocketJs as runtime. Its not yet a generic, all purpose
|
37
|
+
This is currently mainly a technical demonstration, demonstrating the speed of the Opal Ruby implementation employing Node and UWebSocketJs as runtime. Its not yet a generic, all purpose Rack server, but good for further experimentation, research and open for improvement.
|
34
38
|
|
35
39
|
## Getting started
|
36
40
|
|
@@ -41,7 +45,35 @@ To start experimenting:
|
|
41
45
|
- bundle install
|
42
46
|
- bundle exec up
|
43
47
|
|
44
|
-
You may want to change the `gem 'up'` line in the Gemfile to use up from rubygems, if you want to run your app outside of the cloned repo.
|
48
|
+
You may want to change the `gem 'opal-up'` line in the Gemfile to use up from rubygems, if you want to run your app outside of the cloned repo.
|
49
|
+
|
50
|
+
For a Gemfile available from rubygems:
|
51
|
+
`gem 'opal-up'`
|
52
|
+
|
53
|
+
## Available Commands
|
54
|
+
|
55
|
+
Available with `bundle exec` within the example apps or if this gem is included in your Gemfile:
|
56
|
+
|
57
|
+
- `up` - starts a single worker server using uWebSockets, fastest server
|
58
|
+
- `up_cluster` - starts a cluster of workers using uWebSockets, still fast, depending on workload may be even faster than the single worker or not
|
59
|
+
- `up_node` - starts a single worker server using the standard Node HTTP(S) classes
|
60
|
+
- `up_node_cluster` - starts a cluster of workers using the standard Node HTTP(S) classes, probably faster than `up_node`
|
61
|
+
- `up_bun` - starts single worker server using Bun, requires Opal bun support from [PR#2622](https://github.com/opal/opal/pull/2622)
|
62
|
+
|
63
|
+
```
|
64
|
+
Usage: up [options]
|
65
|
+
|
66
|
+
-h, --help Show this message
|
67
|
+
-p, --port PORT Port number the server will listen to
|
68
|
+
-b, --bind ADDRESS Address the server will listen to
|
69
|
+
-s, --secure Use secure sockets.
|
70
|
+
When using secure sockets, the -a, -c and -k options must be provided
|
71
|
+
-a, --ca-file FILE File with CA certs
|
72
|
+
-c, --cert-file FILE File with the servers certificate
|
73
|
+
-k, --key-file FILE File with the servers certificate
|
74
|
+
-v, --version Show version
|
75
|
+
|
76
|
+
```
|
45
77
|
|
46
78
|
## Roda, Sinatra, others ...
|
47
79
|
|
data/bin/up
CHANGED
@@ -1,29 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
if RUBY_ENGINE == 'opal'
|
4
|
-
require 'up
|
4
|
+
require 'up/u_web_socket/server_cli'
|
5
5
|
Up::CLI.call
|
6
6
|
else
|
7
|
-
|
8
|
-
|
9
|
-
npm_cmd = `which npm`
|
10
|
-
raise "Please install node first!" if !node_cmd || node_cmd.empty?
|
11
|
-
raise "Please install npm first!" if !npm_cmd || npm_cmd.empty?
|
12
|
-
node_cmd.chop! if node_cmd.end_with?("\n")
|
13
|
-
npm_cmd.chop! if npm_cmd.end_with?("\n")
|
14
|
-
got_uws = `npm list|grep uWebSockets.js@20`
|
15
|
-
`npm i uNetworking/uWebSockets.js#v20.41.0` if got_uws.empty?
|
7
|
+
require 'up/cli'
|
8
|
+
Up::CLI.setup_u_web_socket
|
16
9
|
lib_dir = File.expand_path("#{__dir__}/../lib")
|
17
|
-
|
18
|
-
|
19
|
-
# to the Opal load path and require it, works for some gems, fails for others
|
20
|
-
gems = ""
|
21
|
-
if File.exist?("Gemfile")
|
22
|
-
lines = File.readlines('Gemfile')
|
23
|
-
lines.each do |line|
|
24
|
-
m = /gem ['"](\w+)['"]/.match(line)
|
25
|
-
gems << " -g #{m[1]} -r #{m[1]}" if m && m[1] != 'up'
|
26
|
-
end
|
27
|
-
end
|
28
|
-
Kernel.exec("opal -Rnodejs -E -I. -I#{lib_dir} -g rack #{gems} #{__FILE__}")
|
10
|
+
gems = Up::CLI.get_gems_for_cmd
|
11
|
+
Kernel.exec("opal -Rnodejs -E -I. -I#{lib_dir} -g rack #{gems} #{__FILE__} -- #{ARGV.join(' ')}")
|
29
12
|
end
|
data/bin/up_bun
CHANGED
@@ -1,23 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
if RUBY_ENGINE == 'opal'
|
4
|
-
require 'up
|
4
|
+
require 'up/bun/server_cli'
|
5
5
|
Up::CLI.call
|
6
6
|
else
|
7
|
-
|
8
|
-
|
9
|
-
raise "Please install bun first!" if !bun_cmd || bun_cmd.empty?
|
10
|
-
bun_cmd.chop! if bun_cmd.end_with?("\n")
|
7
|
+
require 'up/cli'
|
8
|
+
Up::CLI.setup_bun
|
11
9
|
lib_dir = File.expand_path("#{__dir__}/../lib")
|
12
|
-
|
13
|
-
|
14
|
-
gems = ""
|
15
|
-
if File.exist?("Gemfile")
|
16
|
-
lines = File.readlines('Gemfile')
|
17
|
-
lines.each do |line|
|
18
|
-
m = /gem ['"](\w+)['"]/.match(line)
|
19
|
-
gems << " -g #{m[1]} -r #{m[1]}" if m && m[1] != 'up'
|
20
|
-
end
|
21
|
-
end
|
22
|
-
Kernel.exec("opal -Rbun -E -I. -I#{lib_dir} -g rack #{gems} #{__FILE__}")
|
10
|
+
gems = Up::CLI.get_gems_for_cmd
|
11
|
+
Kernel.exec("opal -Rbun -E -I. -I#{lib_dir} -g rack #{gems} #{__FILE__} -- #{ARGV.join(' ')}")
|
23
12
|
end
|
data/bin/up_cluster
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if RUBY_ENGINE == 'opal'
|
4
|
+
require 'up/u_web_socket/cluster_cli'
|
5
|
+
Up::CLI.call
|
6
|
+
else
|
7
|
+
require 'up/cli'
|
8
|
+
Up::CLI.setup_u_web_socket
|
9
|
+
lib_dir = File.expand_path("#{__dir__}/../lib")
|
10
|
+
gems = Up::CLI.get_gems_for_cmd
|
11
|
+
Kernel.exec("opal -Rnodejs -E -I. -I#{lib_dir} -g rack #{gems} #{__FILE__} -- #{ARGV.join(' ')}")
|
12
|
+
end
|
data/bin/up_node
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if RUBY_ENGINE == 'opal'
|
4
|
+
require 'up/node/server_cli'
|
5
|
+
Up::CLI.call
|
6
|
+
else
|
7
|
+
require 'up/cli'
|
8
|
+
Up::CLI.setup_node
|
9
|
+
lib_dir = File.expand_path("#{__dir__}/../lib")
|
10
|
+
gems = Up::CLI.get_gems_for_cmd
|
11
|
+
Kernel.exec("opal -Rnodejs -E -I. -I#{lib_dir} -g rack #{gems} #{__FILE__} -- #{ARGV.join(' ')}")
|
12
|
+
end
|
data/bin/up_node_cluster
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
if RUBY_ENGINE == 'opal'
|
4
|
+
require 'up/node/cluster_cli'
|
5
|
+
Up::CLI.call
|
6
|
+
else
|
7
|
+
require 'up/cli'
|
8
|
+
Up::CLI.setup_node
|
9
|
+
lib_dir = File.expand_path("#{__dir__}/../lib")
|
10
|
+
gems = Up::CLI.get_gems_for_cmd
|
11
|
+
Kernel.exec("opal -Rnodejs -E -I. -I#{lib_dir} -g rack #{gems} #{__FILE__} -- #{ARGV.join(' ')}")
|
12
|
+
end
|
data/example_roda_app/Gemfile
CHANGED
data/example_sinatra_app/Gemfile
CHANGED
@@ -0,0 +1,104 @@
|
|
1
|
+
# backtick_javascript: true
|
2
|
+
|
3
|
+
require 'logger'
|
4
|
+
require 'up/version'
|
5
|
+
|
6
|
+
module Up
|
7
|
+
module Bun
|
8
|
+
class RackEnv < ::Hash
|
9
|
+
RACK_VARS = %w[rack.errors rack.hijack rack.hijack? rack.input rack.logger
|
10
|
+
rack.multipart.buffer_size rack.multipart.tempfile_factory
|
11
|
+
rack.response_finished
|
12
|
+
rack.session rack.upgrade rack.upgrade? rack.url_scheme
|
13
|
+
HTTP_ACCEPT HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE
|
14
|
+
HTTP_CONNECTION HTTP_HOST HTTP_USER_AGENT PATH_INFO QUERY_STRING REQUEST_METHOD
|
15
|
+
SCRIPT_NAME SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE]
|
16
|
+
def initialize(req, config)
|
17
|
+
@req = req
|
18
|
+
@config = config
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](key)
|
22
|
+
return super(key) if key?(key)
|
23
|
+
self[key] = case key
|
24
|
+
when 'rack.errors'
|
25
|
+
STDERR
|
26
|
+
when 'rack.hijack'
|
27
|
+
nil
|
28
|
+
when 'rack.hijack?'
|
29
|
+
false
|
30
|
+
when 'rack.input'
|
31
|
+
::IO.new
|
32
|
+
when 'rack.logger'
|
33
|
+
::Logger.new(self['rack.errors'])
|
34
|
+
when 'rack.multipart.buffer_size'
|
35
|
+
4096
|
36
|
+
when 'rack.multipart.tempfile_factory'
|
37
|
+
proc { |_filename, _content_type| File.new }
|
38
|
+
when 'rack.response_finished'
|
39
|
+
[]
|
40
|
+
when 'rack.session'
|
41
|
+
{}
|
42
|
+
when 'rack.upgrade'
|
43
|
+
nil
|
44
|
+
when 'rack.upgrade?'
|
45
|
+
nil
|
46
|
+
when 'rack.url_scheme'
|
47
|
+
@config[:scheme]
|
48
|
+
when 'PATH_INFO'
|
49
|
+
`#@req.url`
|
50
|
+
when 'QUERY_STRING'
|
51
|
+
""
|
52
|
+
when 'RACK_ERRORS'
|
53
|
+
self['rack.errors']
|
54
|
+
when 'RACK_LOGGER'
|
55
|
+
self['rack.logger']
|
56
|
+
when 'REQUEST_METHOD'
|
57
|
+
`#@req.method`
|
58
|
+
when 'SCRIPT_NAME'
|
59
|
+
""
|
60
|
+
when 'SERVER_NAME'
|
61
|
+
@config[:host]
|
62
|
+
when 'SERVER_PORT'
|
63
|
+
@config[:port].to_s
|
64
|
+
when 'SERVER_PROTOCOL'
|
65
|
+
""
|
66
|
+
when 'SERVER_SOFTWARE'
|
67
|
+
"#{@config[:handler]}/#{Up::VERSION} #{@config[:engine]}"
|
68
|
+
else
|
69
|
+
if key.start_with?('HTTP_')
|
70
|
+
key = key[5..].gsub(/_/, '-')
|
71
|
+
`#@req.headers.get(key.toLowerCase())`
|
72
|
+
else
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def req_headers
|
79
|
+
h = {}
|
80
|
+
%x{
|
81
|
+
var hdr, hds = #@req.headers;
|
82
|
+
for (hdr of hds) { h.set(hdr[0], hdr[1]); }
|
83
|
+
}
|
84
|
+
h
|
85
|
+
end
|
86
|
+
|
87
|
+
def each
|
88
|
+
unless @got_them_all
|
89
|
+
RACK_VARS.each { |k| self[k] unless self.key?(k) }
|
90
|
+
@got_them_all = true
|
91
|
+
end
|
92
|
+
super
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_s
|
96
|
+
unless @got_them_all
|
97
|
+
RACK_VARS.each { |k| self[k] unless self.key?(k) }
|
98
|
+
@got_them_all = true
|
99
|
+
end
|
100
|
+
super
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'up/bun/server'
|
2
|
+
|
3
|
+
module Up
|
4
|
+
module Bun
|
5
|
+
module RackServer
|
6
|
+
def self.run(app, options = {})
|
7
|
+
raise "already running" if @server
|
8
|
+
@server = Up::Bun::Server.new(app: app, **options).listen
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.shutdown
|
13
|
+
@server&.stop
|
14
|
+
@server = nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
ENV['RACK_HANDLER'] ||= 'up'
|
21
|
+
|
22
|
+
begin
|
23
|
+
::Rackup::Handler.register('up', Up::Bun::RackServer) if defined?(::Rackup::Handler)
|
24
|
+
rescue StandardError
|
25
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# backtick_javascript: true
|
2
|
+
require 'up/cli'
|
3
|
+
require 'up/bun/rack_env'
|
4
|
+
|
5
|
+
module Up
|
6
|
+
module Bun
|
7
|
+
class Server
|
8
|
+
def initialize(app:, host: 'localhost', port: 3000, scheme: 'http', ca_file: nil, cert_file: nil, key_file: nil)
|
9
|
+
@app = app
|
10
|
+
@scheme = scheme || 'http'
|
11
|
+
raise "unsupported scheme #{@scheme}" unless %w[http https].include?(@scheme)
|
12
|
+
@host = host || 'localhost'
|
13
|
+
@port = port&.to_i || 3000
|
14
|
+
@config = { handler: self.class.name, engine: "bun/#{`process.version`}", port: port, scheme: scheme, host: host }.freeze
|
15
|
+
@ca_file = ca_file
|
16
|
+
@cert_file = cert_file
|
17
|
+
@key_file = key_file
|
18
|
+
@server = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
%x{
|
22
|
+
self.handle_headers = function(rack_headers, bun_hdr) {
|
23
|
+
if (rack_headers.$$is_hash) {
|
24
|
+
var header, k, v;
|
25
|
+
for(header of rack_headers) {
|
26
|
+
k = header[0];
|
27
|
+
if (!k.startsWith('rack.')) {
|
28
|
+
v = header[1];
|
29
|
+
if (v.$$is_array) {
|
30
|
+
v = v.join("\n");
|
31
|
+
}
|
32
|
+
bun_hdr.set(k, v);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
self.handle_response = function(parts, body) {
|
39
|
+
if (parts["$respond_to?"]('each')) {
|
40
|
+
// this is not technically correct, just to make things work
|
41
|
+
#{`parts`.each { |part| `body = body + part` }}
|
42
|
+
} else if (parts["$respond_to?"]('call')) {
|
43
|
+
body = parts.$call();
|
44
|
+
}
|
45
|
+
#{`parts`.close if `parts`.respond_to?(:close)}
|
46
|
+
return body;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
def listen
|
50
|
+
raise "already running" if @server
|
51
|
+
%x{
|
52
|
+
const oubr = Opal.Up.Bun.RackEnv;
|
53
|
+
const oubs = Opal.Up.Bun.Server;
|
54
|
+
|
55
|
+
var server_options = {
|
56
|
+
port: #@port,
|
57
|
+
hostname: #@host,
|
58
|
+
development: false,
|
59
|
+
fetch(req) {
|
60
|
+
const rack_res = #@app.$call(oubr.$new(req, #@config));
|
61
|
+
const hdr = new Headers();
|
62
|
+
oubs.handle_headers(rack_res[1]);
|
63
|
+
var body = '';
|
64
|
+
body = oubs.handle_response(rack_res[2], body);
|
65
|
+
return new Response(body, {status: rack_res[0], statusText: 'OK', headers: hdr});
|
66
|
+
}
|
67
|
+
};
|
68
|
+
if (#@scheme === 'https') {
|
69
|
+
server_options.tls = {
|
70
|
+
key: Bun.file(#@key_file),
|
71
|
+
cert: Bun.file(#@cert_file),
|
72
|
+
ca: Bun.file(#@ca_file)
|
73
|
+
};
|
74
|
+
}
|
75
|
+
|
76
|
+
#@server = Bun.serve(server_options);
|
77
|
+
console.log(`Server is running on ${#@scheme}://${#@host}:${#@port}`);
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def stop
|
82
|
+
if Up::CLI::stoppable?
|
83
|
+
`#@server.stop()`
|
84
|
+
@server = nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'opal/platform'
|
2
|
+
require 'bun/file'
|
3
|
+
require 'nodejs/require'
|
4
|
+
require 'opal-parser'
|
5
|
+
require 'rack/builder'
|
6
|
+
require 'up/rack_builder_patch'
|
7
|
+
require 'up/bun/rack_server'
|
8
|
+
|
9
|
+
module Up
|
10
|
+
module CLI
|
11
|
+
def self.call
|
12
|
+
Up::Bun::RackServer.run(get_app, get_options)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/up/cli.rb
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'up/version'
|
3
|
+
|
4
|
+
module Up
|
5
|
+
module CLI
|
6
|
+
class Options < OptionParser
|
7
|
+
attr_reader :options
|
8
|
+
def initialize
|
9
|
+
super
|
10
|
+
@options = {}
|
11
|
+
self.banner = 'Usage: up [options]'
|
12
|
+
separator ''
|
13
|
+
on('-h', '--help', 'Show this message') do
|
14
|
+
puts self
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
on('-p', '--port PORT', String, 'Port number the server will listen to') do |port|
|
18
|
+
options[:port] = port.to_i
|
19
|
+
end
|
20
|
+
on('-b', '--bind ADDRESS', String, 'Address the server will listen to') do |host|
|
21
|
+
options[:host] = host
|
22
|
+
end
|
23
|
+
on('-s', '--secure', "Use secure sockets.\nWhen using secure sockets, the -a, -c and -k options must be provided") do
|
24
|
+
options[:scheme] = 'https'
|
25
|
+
end
|
26
|
+
on('-a', '--ca-file FILE', String, 'File with CA certs') do |ca_file|
|
27
|
+
options[:ca_file] = ca_file
|
28
|
+
end
|
29
|
+
on('-c', '--cert-file FILE', String, 'File with the servers certificate') do |cert_file|
|
30
|
+
options[:cert_file] = cert_file
|
31
|
+
end
|
32
|
+
on('-k', '--key-file FILE', String, 'File with the servers certificate') do |key_file|
|
33
|
+
options[:key_file] = key_file
|
34
|
+
end
|
35
|
+
on('-v', '--version', 'Show version') do
|
36
|
+
puts "Up! v#{Up::VERSION}"
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def parse!
|
42
|
+
super
|
43
|
+
if options[:scheme] == 'https'
|
44
|
+
if !options[:ca_file] || !options[:cert_file] || !options[:key_file]
|
45
|
+
puts "When using -s or --secure the -a,-c- and -k options must be given too!"
|
46
|
+
exit 2
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class << self
|
53
|
+
if RUBY_ENGINE != 'opal'
|
54
|
+
def setup_bun
|
55
|
+
bun_cmd = `which bun`
|
56
|
+
if !bun_cmd || bun_cmd.empty?
|
57
|
+
puts "Please install bun first!"
|
58
|
+
exit 2
|
59
|
+
end
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def setup_node
|
64
|
+
node_cmd = `which node`
|
65
|
+
if !node_cmd || node_cmd.empty?
|
66
|
+
puts "Please install node first!"
|
67
|
+
exit 2
|
68
|
+
end
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
def setup_u_web_socket
|
73
|
+
setup_node
|
74
|
+
npm_cmd = `which npm`
|
75
|
+
if !npm_cmd || npm_cmd.empty?
|
76
|
+
puts "Please install npm first!"
|
77
|
+
exit 2
|
78
|
+
end
|
79
|
+
npm_cmd.chop! if npm_cmd.end_with?("\n")
|
80
|
+
have_uws = `npm list|grep uWebSockets.js@20`
|
81
|
+
`npm i uNetworking/uWebSockets.js#v20.41.0` if have_uws.empty?
|
82
|
+
true
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_options
|
87
|
+
options = Up::CLI::Options.new
|
88
|
+
options.parse!
|
89
|
+
options.options
|
90
|
+
rescue OptionParser::InvalidOption => e
|
91
|
+
$stderr.puts "#{$0}: #{e.message} (-h will show valid options)"
|
92
|
+
exit 64
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_gems_for_cmd
|
96
|
+
# Opal does not yet support gems, so lets read the Gemfile and simply add each gem
|
97
|
+
# to the Opal load path and require it, works for some gems, fails for others
|
98
|
+
gems = ""
|
99
|
+
if File.exist?("Gemfile")
|
100
|
+
lines = File.readlines('Gemfile')
|
101
|
+
lines.each do |line|
|
102
|
+
m = /gem ['"](\w+)['"]/.match(line)
|
103
|
+
gems << " -g #{m[1]} -r #{m[1]}" if m && m[1] != 'opal-up'
|
104
|
+
end
|
105
|
+
end
|
106
|
+
gems
|
107
|
+
end
|
108
|
+
|
109
|
+
def try_file(filename)
|
110
|
+
return nil unless File.exist? filename
|
111
|
+
::Rack::Builder.parse_file filename
|
112
|
+
end
|
113
|
+
|
114
|
+
def get_app(filename = 'config.ru')
|
115
|
+
app = nil
|
116
|
+
filename ||= 'config.ru'
|
117
|
+
app = try_file(filename)
|
118
|
+
unless app
|
119
|
+
filename = "#{filename}.ru"
|
120
|
+
app = try_file(filename)
|
121
|
+
end
|
122
|
+
raise "Something wrong with #{filename}\n" unless app
|
123
|
+
app
|
124
|
+
end
|
125
|
+
|
126
|
+
def stoppable?
|
127
|
+
sleep 0.1
|
128
|
+
puts 'deflating request chain links'
|
129
|
+
sleep 0.1
|
130
|
+
puts 'optimizing response distortion'
|
131
|
+
sleep 0.1
|
132
|
+
print 'releasing boost pressure: '
|
133
|
+
3.times do
|
134
|
+
sleep 0.2
|
135
|
+
print '.'
|
136
|
+
end
|
137
|
+
3.times do
|
138
|
+
puts "\n\033[5;31;47mRED ALERT: boost pressure to high, cannot open release valve!1!!!\033[0m"
|
139
|
+
sleep 0.1
|
140
|
+
print '.'
|
141
|
+
sleep 0.1
|
142
|
+
end
|
143
|
+
puts 'stopping engines failed, for further help see:'
|
144
|
+
puts 'https://www.youtube.com/watch?v=ecBco63zvas'
|
145
|
+
sleep 0.2
|
146
|
+
puts "Just kidding :)"
|
147
|
+
true
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# backtick_javascript: true
|
2
|
+
require 'up/node/server'
|
3
|
+
|
4
|
+
%x{
|
5
|
+
const cluster = require('node:cluster');
|
6
|
+
const num_workers = require('node:os').availableParallelism();
|
7
|
+
}
|
8
|
+
|
9
|
+
module Up
|
10
|
+
module Node
|
11
|
+
class Cluster < Up::Node::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)
|
14
|
+
@workers = workers || `num_workers`
|
15
|
+
@members = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def listen
|
19
|
+
raise "already running" unless @members.empty?
|
20
|
+
%x{
|
21
|
+
if (cluster.isPrimary) {
|
22
|
+
for (let i = 0; i < #@workers; i++) {
|
23
|
+
#@members.push(cluster.fork());
|
24
|
+
}
|
25
|
+
} else {
|
26
|
+
#{super}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def stop
|
32
|
+
if Up::CLI::stoppable?
|
33
|
+
@members.each { |m| `m.kill()` }
|
34
|
+
@members.clear
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'opal/platform'
|
2
|
+
require 'nodejs/file'
|
3
|
+
require 'nodejs/require'
|
4
|
+
require 'opal-parser'
|
5
|
+
require 'rack/builder'
|
6
|
+
require 'up/rack_builder_patch'
|
7
|
+
require 'up/node/rack_cluster'
|
8
|
+
|
9
|
+
module Up
|
10
|
+
module CLI
|
11
|
+
def self.call
|
12
|
+
Up::Node::RackCluster.run(get_app, get_options)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|