opal-up 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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