zapp 0.1.0 → 0.2.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/.rubocop.yml +5 -0
- data/Gemfile.lock +5 -3
- data/bin/zapp +1 -2
- data/examples/config/app.rb +5 -0
- data/examples/config/puma.rb +10 -0
- data/examples/config/zapp.rb +13 -0
- data/lib/rack/handler/{zap.rb → zapp.rb} +4 -3
- data/lib/zapp/cli.rb +5 -0
- data/lib/zapp/configuration.rb +10 -2
- data/lib/zapp/http_context/context.rb +10 -11
- data/lib/zapp/http_context/request.rb +10 -6
- data/lib/zapp/http_context/response.rb +10 -5
- data/lib/zapp/logger/base.rb +103 -0
- data/lib/zapp/logger.rb +21 -47
- data/lib/zapp/pipe.rb +14 -0
- data/lib/zapp/server.rb +38 -16
- data/lib/zapp/socket_pipe/receiver.rb +24 -0
- data/lib/zapp/socket_pipe/sender.rb +17 -0
- data/lib/zapp/version.rb +1 -1
- data/lib/zapp/worker/request_processor.rb +109 -0
- data/lib/zapp/worker.rb +18 -69
- data/lib/zapp/worker_pool.rb +17 -18
- data/lib/zapp.rb +18 -11
- data/zapp.gemspec +2 -0
- metadata +26 -100
- data/bin/console +0 -15
- data/bin/setup +0 -8
- data/examples/rails-app/.browserslistrc +0 -1
- data/examples/rails-app/.gitattributes +0 -10
- data/examples/rails-app/.gitignore +0 -40
- data/examples/rails-app/.ruby-version +0 -1
- data/examples/rails-app/Gemfile +0 -58
- data/examples/rails-app/Gemfile.lock +0 -253
- data/examples/rails-app/Rakefile +0 -8
- data/examples/rails-app/app/assets/config/manifest.js +0 -2
- data/examples/rails-app/app/assets/images/.keep +0 -0
- data/examples/rails-app/app/assets/stylesheets/application.css +0 -15
- data/examples/rails-app/app/channels/application_cable/channel.rb +0 -6
- data/examples/rails-app/app/channels/application_cable/connection.rb +0 -6
- data/examples/rails-app/app/controllers/application_controller.rb +0 -4
- data/examples/rails-app/app/controllers/concerns/.keep +0 -0
- data/examples/rails-app/app/helpers/application_helper.rb +0 -4
- data/examples/rails-app/app/javascript/channels/consumer.js +0 -6
- data/examples/rails-app/app/javascript/channels/index.js +0 -5
- data/examples/rails-app/app/javascript/packs/application.js +0 -13
- data/examples/rails-app/app/jobs/application_job.rb +0 -9
- data/examples/rails-app/app/mailers/application_mailer.rb +0 -6
- data/examples/rails-app/app/models/application_record.rb +0 -5
- data/examples/rails-app/app/models/concerns/.keep +0 -0
- data/examples/rails-app/app/views/layouts/application.html.erb +0 -16
- data/examples/rails-app/app/views/layouts/mailer.html.erb +0 -13
- data/examples/rails-app/app/views/layouts/mailer.text.erb +0 -1
- data/examples/rails-app/babel.config.js +0 -82
- data/examples/rails-app/bin/bundle +0 -118
- data/examples/rails-app/bin/rails +0 -7
- data/examples/rails-app/bin/rake +0 -7
- data/examples/rails-app/bin/setup +0 -38
- data/examples/rails-app/bin/spring +0 -16
- data/examples/rails-app/bin/webpack +0 -21
- data/examples/rails-app/bin/webpack-dev-server +0 -21
- data/examples/rails-app/bin/yarn +0 -19
- data/examples/rails-app/bin/zapp +0 -1
- data/examples/rails-app/config/application.rb +0 -24
- data/examples/rails-app/config/boot.rb +0 -6
- data/examples/rails-app/config/cable.yml +0 -10
- data/examples/rails-app/config/credentials.yml.enc +0 -1
- data/examples/rails-app/config/database.yml +0 -25
- data/examples/rails-app/config/environment.rb +0 -7
- data/examples/rails-app/config/environments/development.rb +0 -78
- data/examples/rails-app/config/environments/production.rb +0 -122
- data/examples/rails-app/config/environments/test.rb +0 -62
- data/examples/rails-app/config/initializers/application_controller_renderer.rb +0 -9
- data/examples/rails-app/config/initializers/assets.rb +0 -16
- data/examples/rails-app/config/initializers/backtrace_silencers.rb +0 -10
- data/examples/rails-app/config/initializers/content_security_policy.rb +0 -31
- data/examples/rails-app/config/initializers/cookies_serializer.rb +0 -7
- data/examples/rails-app/config/initializers/filter_parameter_logging.rb +0 -8
- data/examples/rails-app/config/initializers/inflections.rb +0 -17
- data/examples/rails-app/config/initializers/mime_types.rb +0 -5
- data/examples/rails-app/config/initializers/permissions_policy.rb +0 -12
- data/examples/rails-app/config/initializers/wrap_parameters.rb +0 -16
- data/examples/rails-app/config/locales/en.yml +0 -33
- data/examples/rails-app/config/puma.rb +0 -45
- data/examples/rails-app/config/routes.rb +0 -5
- data/examples/rails-app/config/spring.rb +0 -8
- data/examples/rails-app/config/storage.yml +0 -34
- data/examples/rails-app/config/webpack/development.js +0 -5
- data/examples/rails-app/config/webpack/environment.js +0 -3
- data/examples/rails-app/config/webpack/production.js +0 -5
- data/examples/rails-app/config/webpack/test.js +0 -5
- data/examples/rails-app/config/webpacker.yml +0 -92
- data/examples/rails-app/config/zapp.rb +0 -10
- data/examples/rails-app/config.ru +0 -7
- data/examples/rails-app/db/seeds.rb +0 -8
- data/examples/rails-app/lib/assets/.keep +0 -0
- data/examples/rails-app/lib/tasks/.keep +0 -0
- data/examples/rails-app/log/.keep +0 -0
- data/examples/rails-app/package.json +0 -17
- data/examples/rails-app/postcss.config.js +0 -12
- data/examples/rails-app/public/404.html +0 -67
- data/examples/rails-app/public/422.html +0 -67
- data/examples/rails-app/public/500.html +0 -66
- data/examples/rails-app/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/rails-app/public/apple-touch-icon.png +0 -0
- data/examples/rails-app/public/favicon.ico +0 -0
- data/examples/rails-app/public/robots.txt +0 -1
- data/examples/rails-app/storage/.keep +0 -0
- data/examples/rails-app/test/application_system_test_case.rb +0 -7
- data/examples/rails-app/test/channels/application_cable/connection_test.rb +0 -15
- data/examples/rails-app/test/controllers/.keep +0 -0
- data/examples/rails-app/test/fixtures/files/.keep +0 -0
- data/examples/rails-app/test/helpers/.keep +0 -0
- data/examples/rails-app/test/integration/.keep +0 -0
- data/examples/rails-app/test/mailers/.keep +0 -0
- data/examples/rails-app/test/models/.keep +0 -0
- data/examples/rails-app/test/system/.keep +0 -0
- data/examples/rails-app/test/test_helper.rb +0 -17
- data/examples/rails-app/tmp/.keep +0 -0
- data/examples/rails-app/tmp/pids/.keep +0 -0
- data/examples/rails-app/vendor/.keep +0 -0
- data/examples/rails-app/yarn.lock +0 -6973
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6b4a4b6bdf9ac52a419e899e41344faf5ff15fdde704f70e93fe71cee334e70e
|
|
4
|
+
data.tar.gz: 4bd738d7ca1919ceef29612c5bf9a07a26ad8bda944b9adfc52197435dbb1a0a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 26554e64959a88e35bf1b7f9657909da61de5a92c980550df415ae0b5564f899b25922e1fbb69d79a39a72506fe903654a007555c8e85ce3b437eb1cbbf8ccb3
|
|
7
|
+
data.tar.gz: c790f81f5fa7d1867ee6f33c3fff92f3df44ece8051e9dc3f9ed260724dd0236a3d3fd1814ba3dcf54f3743713782445a5001f54fcb275872943665055593b64
|
data/.rubocop.yml
CHANGED
|
@@ -5,12 +5,17 @@ AllCops:
|
|
|
5
5
|
Exclude:
|
|
6
6
|
- examples/**/*
|
|
7
7
|
|
|
8
|
+
Gemspec/RequireMFA:
|
|
9
|
+
Enabled: false
|
|
10
|
+
|
|
8
11
|
Style/DocumentationMethod:
|
|
9
12
|
Enabled: false
|
|
10
13
|
Style/MissingElse:
|
|
11
14
|
Enabled: false
|
|
12
15
|
Style/StringLiterals:
|
|
13
16
|
EnforcedStyle: double_quotes
|
|
17
|
+
Style/InlineComment:
|
|
18
|
+
Enabled: false
|
|
14
19
|
Style/Copyright:
|
|
15
20
|
Enabled: false
|
|
16
21
|
Style/ConstantVisibility:
|
data/Gemfile.lock
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
zapp (0.1
|
|
4
|
+
zapp (0.2.1)
|
|
5
5
|
concurrent-ruby (~> 1.1.9)
|
|
6
6
|
puma (~> 5.5.2)
|
|
7
7
|
rack (~> 2.2.3)
|
|
8
8
|
rake (~> 13.0)
|
|
9
9
|
rspec (~> 3.0)
|
|
10
|
+
webrick
|
|
10
11
|
|
|
11
12
|
GEM
|
|
12
13
|
remote: https://rubygems.org/
|
|
13
14
|
specs:
|
|
14
15
|
ast (2.4.2)
|
|
15
16
|
coderay (1.1.3)
|
|
16
|
-
concurrent-ruby (1.1.
|
|
17
|
+
concurrent-ruby (1.1.10)
|
|
17
18
|
diff-lcs (1.4.4)
|
|
18
19
|
docile (1.4.0)
|
|
19
20
|
ffi (1.15.4)
|
|
@@ -53,7 +54,7 @@ GEM
|
|
|
53
54
|
method_source (~> 1.0)
|
|
54
55
|
puma (5.5.2)
|
|
55
56
|
nio4r (~> 2.0)
|
|
56
|
-
rack (2.2.
|
|
57
|
+
rack (2.2.4)
|
|
57
58
|
rainbow (3.0.0)
|
|
58
59
|
rake (13.0.6)
|
|
59
60
|
rb-fsevent (0.11.0)
|
|
@@ -95,6 +96,7 @@ GEM
|
|
|
95
96
|
simplecov_json_formatter (0.1.3)
|
|
96
97
|
thor (1.1.0)
|
|
97
98
|
unicode-display_width (2.1.0)
|
|
99
|
+
webrick (1.7.0)
|
|
98
100
|
|
|
99
101
|
PLATFORMS
|
|
100
102
|
x86_64-linux
|
data/bin/zapp
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require_relative("app")
|
|
2
|
+
|
|
3
|
+
max_threads_count = ENV.fetch("RAILS_MAX_THREADS", 5)
|
|
4
|
+
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
|
|
5
|
+
threads(min_threads_count, max_threads_count)
|
|
6
|
+
worker_timeout(3600) if ENV.fetch("RAILS_ENV", "development") == "development"
|
|
7
|
+
port(ENV.fetch("PORT", 3000))
|
|
8
|
+
environment(ENV.fetch("RAILS_ENV", "development"))
|
|
9
|
+
workers ENV.fetch("WEB_CONCURRENCY") { 2 }
|
|
10
|
+
app(App)
|
|
@@ -6,11 +6,12 @@ module Rack
|
|
|
6
6
|
module Handler
|
|
7
7
|
# Rack handler for the Zapp web server
|
|
8
8
|
class Zapp
|
|
9
|
+
register(:zapp, Rack::Handler::Zapp)
|
|
10
|
+
|
|
9
11
|
def self.run(app)
|
|
10
|
-
Zapp
|
|
12
|
+
Zapp.config.app = app
|
|
13
|
+
Zapp::Server.new.run
|
|
11
14
|
end
|
|
12
|
-
|
|
13
|
-
register(:zapp, Rack::Handler::Zapp)
|
|
14
15
|
end
|
|
15
16
|
end
|
|
16
17
|
end
|
data/lib/zapp/cli.rb
CHANGED
|
@@ -27,6 +27,11 @@ module Zapp
|
|
|
27
27
|
parse_config_file(location: file)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
opts.on("-v", "--version", "Prints the version of Zapp currently running") do
|
|
31
|
+
puts("Zapp v#{Zapp::VERSION}")
|
|
32
|
+
exit
|
|
33
|
+
end
|
|
34
|
+
|
|
30
35
|
opts.on("-h", "--help", "Prints this help") do
|
|
31
36
|
puts(opts)
|
|
32
37
|
exit
|
data/lib/zapp/configuration.rb
CHANGED
|
@@ -12,6 +12,7 @@ module Zapp
|
|
|
12
12
|
:parallelism,
|
|
13
13
|
:threads_per_worker,
|
|
14
14
|
:logger_class,
|
|
15
|
+
:logger_out_io,
|
|
15
16
|
:log_requests,
|
|
16
17
|
:log_uncaught_errors,
|
|
17
18
|
:host,
|
|
@@ -33,6 +34,7 @@ module Zapp
|
|
|
33
34
|
|
|
34
35
|
# Default logging behavior
|
|
35
36
|
logger_class: Zapp::Logger,
|
|
37
|
+
logger_out_io: $stdout,
|
|
36
38
|
log_requests: true,
|
|
37
39
|
log_uncaught_errors: true,
|
|
38
40
|
|
|
@@ -44,7 +46,7 @@ module Zapp
|
|
|
44
46
|
{
|
|
45
47
|
Rack::RACK_VERSION => Rack::VERSION,
|
|
46
48
|
Rack::RACK_ERRORS => $stderr,
|
|
47
|
-
Rack::RACK_MULTITHREAD =>
|
|
49
|
+
Rack::RACK_MULTITHREAD => true,
|
|
48
50
|
Rack::RACK_MULTIPROCESS => true,
|
|
49
51
|
Rack::RACK_RUNONCE => false,
|
|
50
52
|
Rack::RACK_URL_SCHEME => %w[yes on 1].include?(ENV["HTTPS"]) ? "https" : "http"
|
|
@@ -72,7 +74,7 @@ module Zapp
|
|
|
72
74
|
@app = new unless new.nil?
|
|
73
75
|
|
|
74
76
|
@app ||= begin
|
|
75
|
-
raise(Zapp::
|
|
77
|
+
raise(Zapp::ZappError, "Missing rackup file '#{rackup_file}'") unless File.exist?(rackup_file)
|
|
76
78
|
|
|
77
79
|
rack_app, = rack_builder.parse_file(rackup_file)
|
|
78
80
|
|
|
@@ -98,6 +100,12 @@ module Zapp
|
|
|
98
100
|
@logger_class = new
|
|
99
101
|
end
|
|
100
102
|
|
|
103
|
+
def logger_out_io(new = nil)
|
|
104
|
+
return @logger_out_io if new.nil?
|
|
105
|
+
|
|
106
|
+
@logger_out_io = new
|
|
107
|
+
end
|
|
108
|
+
|
|
101
109
|
def log_requests(new = nil)
|
|
102
110
|
return @log_requests if new.nil?
|
|
103
111
|
|
|
@@ -1,31 +1,30 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
require_relative("request")
|
|
4
|
+
require_relative("response")
|
|
5
5
|
|
|
6
6
|
module Zapp
|
|
7
7
|
module HTTPContext
|
|
8
8
|
# Context containing request and response
|
|
9
9
|
class Context
|
|
10
|
-
attr_reader(:req, :res)
|
|
10
|
+
attr_reader(:req, :res, :socket)
|
|
11
11
|
|
|
12
|
-
def initialize(socket:)
|
|
12
|
+
def initialize(socket:, logger: Zapp::Logger)
|
|
13
13
|
@socket = socket
|
|
14
14
|
@req = Zapp::HTTPContext::Request.new(socket: socket)
|
|
15
15
|
@res = Zapp::HTTPContext::Response.new(socket: socket)
|
|
16
|
+
rescue Puma::HttpParserError => e
|
|
17
|
+
res.write(data: "Invalid HTTP request", status: 400, headers: {})
|
|
18
|
+
logger.warn("Puma parser error: #{e}")
|
|
19
|
+
logger.debug("HTTP request raw: #{context.req.raw}")
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
def close
|
|
19
23
|
@socket.close
|
|
20
24
|
end
|
|
21
25
|
|
|
22
|
-
def
|
|
23
|
-
|
|
24
|
-
clone_context.instance_variable_set(:@req, @req.dup)
|
|
25
|
-
clone_context.instance_variable_set(:@res, @res.dup)
|
|
26
|
-
clone_context.instance_variable_set(:@socket, @socket.dup)
|
|
27
|
-
|
|
28
|
-
clone_context
|
|
26
|
+
def client_closed?
|
|
27
|
+
req.data["HTTP_CONNECTION"] == "close"
|
|
29
28
|
end
|
|
30
29
|
end
|
|
31
30
|
end
|
|
@@ -1,27 +1,31 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require("webrick")
|
|
4
|
+
|
|
3
5
|
module Zapp
|
|
4
6
|
module HTTPContext
|
|
5
7
|
# Represents an HTTP Request to be processed by a worker
|
|
6
8
|
class Request
|
|
7
9
|
attr_reader(:raw, :data, :body)
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
# Request parsing is done threaded, but not in separate Ractors.
|
|
12
|
+
# So we allocate an HTTP parser per thread and assign it to this hash key in Thread.current
|
|
13
|
+
PARSER_THREAD_HASH_KEY = "PUMA_PARSER_INSTANCE"
|
|
11
14
|
|
|
15
|
+
def initialize(socket:)
|
|
12
16
|
# Max Request size of 8KB TODO: Make a config value for this setting
|
|
13
17
|
@raw = socket.readpartial(8192)
|
|
14
18
|
@data = {}
|
|
15
|
-
end
|
|
16
19
|
|
|
17
|
-
def parse!(parser: Puma::HttpParser.new)
|
|
18
20
|
parser.execute(data, raw, 0)
|
|
21
|
+
|
|
19
22
|
@body = Zapp::InputStream.new(string: parser.body)
|
|
23
|
+
|
|
20
24
|
parser.reset
|
|
21
25
|
end
|
|
22
26
|
|
|
23
|
-
def
|
|
24
|
-
|
|
27
|
+
def parser
|
|
28
|
+
Thread.current[PARSER_THREAD_HASH_KEY] ||= Puma::HttpParser.new
|
|
25
29
|
end
|
|
26
30
|
end
|
|
27
31
|
end
|
|
@@ -4,21 +4,26 @@ module Zapp
|
|
|
4
4
|
module HTTPContext
|
|
5
5
|
# Represents an HTTP response being sent back to a client
|
|
6
6
|
class Response
|
|
7
|
+
attr_reader(:status, :data, :headers)
|
|
8
|
+
|
|
7
9
|
def initialize(socket:)
|
|
8
10
|
@socket = socket
|
|
9
11
|
end
|
|
10
12
|
|
|
11
|
-
# TODO: Add headers argument
|
|
12
13
|
def write(data:, status:, headers:)
|
|
13
|
-
|
|
14
|
+
@status = status
|
|
15
|
+
@data = data
|
|
16
|
+
@headers = headers
|
|
17
|
+
|
|
18
|
+
response = +"HTTP/1.1 #{status}\n"
|
|
14
19
|
|
|
15
|
-
response
|
|
20
|
+
response << "Content-Length: #{data.size}\n" unless headers["Content-Length"]
|
|
16
21
|
|
|
17
22
|
headers.each do |k, v|
|
|
18
|
-
response
|
|
23
|
+
response << "#{k}: #{v}\n"
|
|
19
24
|
end
|
|
20
25
|
|
|
21
|
-
response
|
|
26
|
+
response << "\n#{data}\n"
|
|
22
27
|
|
|
23
28
|
@socket.write(response)
|
|
24
29
|
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zapp
|
|
4
|
+
class Logger
|
|
5
|
+
# Base contains all the logging functionality and is included both as class and instance methods of Zap::Logger
|
|
6
|
+
# This allows logging without creating new instances,
|
|
7
|
+
# while allowing Ractors to create their own instances for thread safety
|
|
8
|
+
module Base
|
|
9
|
+
attr_writer(:level, :prefix)
|
|
10
|
+
|
|
11
|
+
LEVELS = { TRACE: 0, DEBUG: 1, INFO: 2, WARN: 3, ERROR: 4 }.freeze
|
|
12
|
+
|
|
13
|
+
FROZEN_ENV = ENV.map { |k, v| [k.freeze, v.freeze] }
|
|
14
|
+
.to_h.freeze
|
|
15
|
+
|
|
16
|
+
# The hash key in Ractor.current that stores the mutex for writing to output
|
|
17
|
+
OUT_IO_MUTEX_KEY = "ZAPP_LOGGER_OUT_IO_MUTEX"
|
|
18
|
+
|
|
19
|
+
def trace(msg) = log("TRACE", msg)
|
|
20
|
+
|
|
21
|
+
def debug(msg) = log("DEBUG", msg)
|
|
22
|
+
|
|
23
|
+
def info(msg) = log("INFO", msg)
|
|
24
|
+
|
|
25
|
+
def warn(msg) = log("WARN", msg)
|
|
26
|
+
|
|
27
|
+
def error(msg) = log("ERROR", msg)
|
|
28
|
+
|
|
29
|
+
def level
|
|
30
|
+
@level ||= begin
|
|
31
|
+
log_level = FROZEN_ENV["LOG_LEVEL"]
|
|
32
|
+
|
|
33
|
+
if log_level == "" || log_level.nil?
|
|
34
|
+
LEVELS[:DEBUG]
|
|
35
|
+
else
|
|
36
|
+
resolved_level = LEVELS[log_level.upcase.to_sym]
|
|
37
|
+
|
|
38
|
+
if resolved_level.nil?
|
|
39
|
+
raise(
|
|
40
|
+
Zapp::ZappError,
|
|
41
|
+
"Invalid log level '#{log_level.upcase}', must be one of [#{LEVELS.keys.join(', ')}]"
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
resolved_level
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def log(current_level, msg, **_tags)
|
|
51
|
+
return unless level <= LEVELS[current_level.to_sym]
|
|
52
|
+
|
|
53
|
+
write("--- #{prefix} [#{current_level}] #{msg}\n")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def flush
|
|
57
|
+
writing_thread_pool.wait_for_termination(0.1)
|
|
58
|
+
|
|
59
|
+
out_io_mutex.synchronize do
|
|
60
|
+
out.flush
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param new_out [IO]
|
|
65
|
+
def out=(new_out)
|
|
66
|
+
@out = new_out
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
protected
|
|
70
|
+
|
|
71
|
+
# @return [IO]
|
|
72
|
+
def out
|
|
73
|
+
@out ||= Zapp.config.logger_out_io
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [String]
|
|
77
|
+
def prefix = @prefix ||= Ractor.current.name
|
|
78
|
+
|
|
79
|
+
def write(msg)
|
|
80
|
+
writing_thread_pool.post do
|
|
81
|
+
out_io_mutex.synchronize do
|
|
82
|
+
out.print(msg)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# We really just use this as a queue
|
|
88
|
+
# TODO: There's probably a smarter way of doing this with less overhead,
|
|
89
|
+
# TODO: or maybe we should just actually write logs multi-threaded
|
|
90
|
+
def writing_thread_pool
|
|
91
|
+
@writing_thread_pool ||= Concurrent::ThreadPoolExecutor.new(
|
|
92
|
+
min_threads: 1,
|
|
93
|
+
max_threads: 1,
|
|
94
|
+
max_queue: 100
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def out_io_mutex
|
|
99
|
+
Ractor.current[OUT_IO_MUTEX_KEY] ||= Thread::Mutex.new
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/zapp/logger.rb
CHANGED
|
@@ -1,63 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative("logger/base")
|
|
4
|
+
|
|
3
5
|
module Zapp
|
|
4
|
-
# The default logger for
|
|
6
|
+
# The default logger for Zapp
|
|
5
7
|
class Logger
|
|
6
|
-
|
|
7
|
-
# This allows logging without creating new instances,
|
|
8
|
-
# while allowing Ractors to create their own instances for thread safety
|
|
9
|
-
module Base
|
|
10
|
-
attr_writer(:level, :prefix)
|
|
11
|
-
|
|
12
|
-
LEVELS = { DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3 }.freeze
|
|
13
|
-
|
|
14
|
-
def debug(msg)
|
|
15
|
-
log("DEBUG", msg)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def info(msg)
|
|
19
|
-
log("INFO", msg)
|
|
20
|
-
end
|
|
8
|
+
include(Zapp::Logger::Base)
|
|
21
9
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
def initialize
|
|
11
|
+
yield(self) if block_given?
|
|
12
|
+
end
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
14
|
+
class << self
|
|
15
|
+
# The hash key in Ractor.current that stores the global Zapp::Logger instance
|
|
16
|
+
GLOBAL_INSTANCE_KEY = "ZAPP_LOGGER_INSTANCE"
|
|
29
17
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
if LEVELS[ENV["ZAPP_LOG_LEVEL"]].nil?
|
|
33
|
-
raise(
|
|
34
|
-
Zapp::ZappError,
|
|
35
|
-
"Invalid log level '#{ENV['ZAP_LOG_LEVEL']}', must be one of [#{LEVELS.keys.join(', ')}]"
|
|
36
|
-
)
|
|
37
|
-
else
|
|
38
|
-
LEVELS[ENV["ZAP_LOG_LEVEL"]]
|
|
39
|
-
end
|
|
40
|
-
else
|
|
41
|
-
LEVELS[:DEBUG]
|
|
42
|
-
end
|
|
18
|
+
def instance
|
|
19
|
+
Ractor.current[GLOBAL_INSTANCE_KEY] ||= new
|
|
43
20
|
end
|
|
44
21
|
|
|
45
22
|
private
|
|
46
23
|
|
|
47
|
-
def
|
|
48
|
-
|
|
24
|
+
def method_missing(symbol, *args)
|
|
25
|
+
if respond_to_missing?(symbol)
|
|
26
|
+
instance.public_send(symbol, *args)
|
|
27
|
+
else
|
|
28
|
+
super
|
|
29
|
+
end
|
|
49
30
|
end
|
|
50
|
-
end
|
|
51
|
-
include(Zapp::Logger::Base)
|
|
52
|
-
|
|
53
|
-
def initialize
|
|
54
|
-
@prefix = "Zap"
|
|
55
|
-
yield(self) if block_given?
|
|
56
|
-
end
|
|
57
31
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
32
|
+
def respond_to_missing?(symbol, include_private = false)
|
|
33
|
+
instance.respond_to?(symbol) || super(symbol, include_private)
|
|
34
|
+
end
|
|
61
35
|
end
|
|
62
36
|
end
|
|
63
37
|
end
|
data/lib/zapp/pipe.rb
ADDED
data/lib/zapp/server.rb
CHANGED
|
@@ -3,53 +3,75 @@
|
|
|
3
3
|
module Zapp
|
|
4
4
|
# The Zap HTTP Server, listens on a TCP connection and processes incoming requests
|
|
5
5
|
class Server
|
|
6
|
-
attr_reader(:
|
|
6
|
+
attr_reader(:worker_pool, :socket_pipe_receiver)
|
|
7
7
|
|
|
8
8
|
def initialize
|
|
9
|
-
@
|
|
10
|
-
@
|
|
9
|
+
@socket_pipe = Zapp::Pipe.new
|
|
10
|
+
@context_pipe = Zapp::Pipe.new
|
|
11
|
+
|
|
12
|
+
@socket_pipe_receiver = Zapp::SocketPipe::Receiver.new(pipe: @socket_pipe)
|
|
13
|
+
|
|
14
|
+
@worker_pool = Zapp::WorkerPool.new(socket_pipe: @socket_pipe, context_pipe: @context_pipe)
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def run
|
|
14
|
-
parser = Puma::HttpParser.new
|
|
15
|
-
|
|
16
18
|
log_start
|
|
17
19
|
|
|
18
20
|
loop do
|
|
19
|
-
socket =
|
|
20
|
-
next if socket.eof?
|
|
21
|
+
socket = socket_pipe_receiver.take
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
context.req.parse!(parser: parser)
|
|
23
|
+
next if socket.eof?
|
|
25
24
|
|
|
26
|
-
|
|
25
|
+
parsing_thread_pool.post do
|
|
26
|
+
ctx = Zapp::HTTPContext::Context.new(socket: socket)
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
worker_pool.process(context: ctx) unless ctx.client_closed? # Parsing failed
|
|
29
|
+
end
|
|
30
|
+
rescue Errno::ECONNRESET
|
|
31
|
+
next
|
|
31
32
|
end
|
|
32
33
|
rescue SignalException, IRB::Abort => e
|
|
33
34
|
shutdown(e)
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def shutdown(err = nil)
|
|
37
|
-
Zapp::Logger.info("Received signal #{err}") unless err.nil?
|
|
38
|
+
Zapp::Logger.info("Received signal #{err.class.name}") unless err.nil?
|
|
38
39
|
Zapp::Logger.info("Gracefully shutting down workers, allowing request processing to finish")
|
|
39
40
|
|
|
40
41
|
worker_pool.drain
|
|
41
42
|
|
|
42
43
|
Zapp::Logger.info("Done. See you next time!")
|
|
44
|
+
Zapp::Logger.flush
|
|
43
45
|
end
|
|
44
46
|
|
|
45
47
|
private
|
|
46
48
|
|
|
47
49
|
def log_start
|
|
48
|
-
Zapp::Logger.info(
|
|
50
|
+
Zapp::Logger.info(
|
|
51
|
+
"
|
|
52
|
+
⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡
|
|
53
|
+
⚡ ███████╗ █████╗ ██████╗ ██████╗ ⚡
|
|
54
|
+
⚡ ╚══███╔╝██╔══██╗██╔══██╗██╔══██╗ ⚡
|
|
55
|
+
⚡ ███╔╝ ███████║██████╔╝██████╔╝ ⚡
|
|
56
|
+
⚡ ███╔╝ ██╔══██║██╔═══╝ ██╔═══╝ ⚡
|
|
57
|
+
⚡ ███████╗██║ ██║██║ ██║ ⚡
|
|
58
|
+
⚡ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ⚡
|
|
59
|
+
⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡
|
|
60
|
+
"
|
|
61
|
+
)
|
|
62
|
+
Zapp::Logger.info("Zapp version: #{Zapp::VERSION}")
|
|
49
63
|
Zapp::Logger.info("Environment: #{Zapp.config.mode}")
|
|
50
64
|
Zapp::Logger.info("Serving: #{Zapp.config.env[Rack::RACK_URL_SCHEME]}://#{Zapp.config.host}:#{Zapp.config.port}")
|
|
51
65
|
Zapp::Logger.info("Parallel workers: #{Zapp.config.parallelism}")
|
|
52
66
|
Zapp::Logger.info("Ready to accept requests")
|
|
53
67
|
end
|
|
68
|
+
|
|
69
|
+
def parsing_thread_pool
|
|
70
|
+
@parsing_thread_pool ||= Concurrent::ThreadPoolExecutor.new(
|
|
71
|
+
min_threads: Zapp.config.parallelism,
|
|
72
|
+
max_threads: Zapp.config.parallelism,
|
|
73
|
+
max_queue: Zapp.config.parallelism * 1_000
|
|
74
|
+
)
|
|
75
|
+
end
|
|
54
76
|
end
|
|
55
77
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zapp
|
|
4
|
+
module SocketPipe
|
|
5
|
+
class Receiver
|
|
6
|
+
attr_reader(:pipe, :raw_tcp_pipe)
|
|
7
|
+
|
|
8
|
+
def initialize(pipe:)
|
|
9
|
+
@pipe = pipe
|
|
10
|
+
@raw_tcp_pipe = Ractor.new(Zapp.config, name: "raw-tcp-pipe") do |config|
|
|
11
|
+
server = TCPServer.new(config.host, config.port)
|
|
12
|
+
|
|
13
|
+
loop do
|
|
14
|
+
Ractor.yield(server.accept)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def take
|
|
20
|
+
Ractor.select(pipe, raw_tcp_pipe)[1]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/zapp/version.rb
CHANGED