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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd1f3da03559ae97160438ac56cab04162d985d069f73fdbb228e18a83f21757
4
- data.tar.gz: 880e5c28a44a8aba0d160c07994d4ca69b5b15051e0fd9948e7aa5b0696a9b82
3
+ metadata.gz: 2d20b2473096aef989d7b0d966ae78805db8798bc3d0957e9785252610cb7717
4
+ data.tar.gz: 26fd5a5a1fd37c574844bfad0abefc4af06a3eae4f2d305f7e3a0fed2bf0bb23
5
5
  SHA512:
6
- metadata.gz: '0491776446d5623ebe8ccacde009d9e448778bff5c5803ebfa4f30ea70b0178454fe5d9b39ba8f9e8d0e54be20a607a81e854f758c64aea5dc9cc4d7ae495821'
7
- data.tar.gz: 25659ca070cc81c078599f2281f2f52c4862f10ebadf284257d9c420380319d98708b77233cd6fb684b064d931c58e50bc893aaedf763f2c5c42d35b81392f51
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: 6391.01 req/s
13
- Iodine: 18645.58 req/s
14
- Up!: 20258.46 req/s <<< fastest
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: 0.156ms
18
- Iodine: 0.054ms
19
- Up!: 0.049ms <<< fastest
20
- ```
21
- running on linux with:
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 v2011.0
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
- `ab -n 100000 -c 10 http://localhost:3000/`
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 Reck server, but good for further experimentation, research and open for improvement.
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-node'
4
+ require 'up/u_web_socket/server_cli'
5
5
  Up::CLI.call
6
6
  else
7
- # check for commands
8
- node_cmd = `which node`
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
- # Opal does not yet support gems, so lets read the Gemfile and simply add each gem
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-bun'
4
+ require 'up/bun/server_cli'
5
5
  Up::CLI.call
6
6
  else
7
- # check for commands
8
- bun_cmd = `which bun`
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
- # Opal does not yet support gems, so lets read the Gemfile and simply add each gem
13
- # to the Opal load path and require it, works for some gems, fails for others
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
@@ -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
@@ -1,6 +1,6 @@
1
1
  source 'https://rubygems.org'
2
2
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
3
 
4
- gem 'opal', github: 'opal/opal'
4
+ gem 'rack', path: '../../rack'
5
5
  gem 'opal-up', path: '../'
6
- gem 'roda'
6
+ gem 'roda', path: '../../roda'
@@ -1,7 +1,9 @@
1
1
  require "roda"
2
2
 
3
3
  class RodaApp < Roda
4
+ STDERR.puts "executing route"
4
5
  route do |r|
6
+ STDERR.puts r
5
7
  # GET / request
6
8
  r.root do
7
9
  r.redirect "/hello"
@@ -3,4 +3,4 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
3
 
4
4
  gem 'opal', github: 'opal/opal'
5
5
  gem 'opal-up', path: '../'
6
- gem 'sinatra', '>= 4.0.0'
6
+ gem 'sinatra'
@@ -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