falcon 0.36.4

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.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +5 -0
  3. data/.github/FUNDING.yml +3 -0
  4. data/.github/workflows/development.yml +60 -0
  5. data/.gitignore +14 -0
  6. data/.rspec +3 -0
  7. data/Gemfile +17 -0
  8. data/README.md +316 -0
  9. data/bake/falcon/supervisor.rb +8 -0
  10. data/bin/falcon +32 -0
  11. data/bin/falcon-host +28 -0
  12. data/examples/beer/config.ru +57 -0
  13. data/examples/beer/falcon.rb +8 -0
  14. data/examples/benchmark/config.ru +39 -0
  15. data/examples/benchmark/falcon.rb +6 -0
  16. data/examples/csv/config.ru +31 -0
  17. data/examples/google/falcon.rb +14 -0
  18. data/examples/hello/config.ru +22 -0
  19. data/examples/hello/falcon.rb +24 -0
  20. data/examples/hello/preload.rb +7 -0
  21. data/examples/internet/config.ru +54 -0
  22. data/examples/memory/allocations.rb +39 -0
  23. data/examples/memory/config.ru +14 -0
  24. data/examples/push/client.rb +29 -0
  25. data/examples/push/config.ru +28 -0
  26. data/examples/push/index.html +14 -0
  27. data/examples/push/script.js +2 -0
  28. data/examples/push/style.css +4 -0
  29. data/examples/redis/Gemfile +9 -0
  30. data/examples/redis/config.ru +28 -0
  31. data/examples/sequel/Gemfile +4 -0
  32. data/examples/sequel/config.ru +8 -0
  33. data/examples/sequel/data.sqlite3 +0 -0
  34. data/examples/server/standalone.rb +27 -0
  35. data/examples/sinatra/Gemfile +7 -0
  36. data/examples/sinatra/Gemfile.lock +53 -0
  37. data/examples/sinatra/config.ru +16 -0
  38. data/examples/trailers/config.ru +34 -0
  39. data/examples/trailers/falcon.rb +8 -0
  40. data/falcon.gemspec +45 -0
  41. data/gems/rack1.gemfile +4 -0
  42. data/gems/rack3.gemfile +4 -0
  43. data/lib/falcon.rb +23 -0
  44. data/lib/falcon/adapters/early_hints.rb +49 -0
  45. data/lib/falcon/adapters/input.rb +131 -0
  46. data/lib/falcon/adapters/output.rb +101 -0
  47. data/lib/falcon/adapters/rack.rb +202 -0
  48. data/lib/falcon/adapters/response.rb +91 -0
  49. data/lib/falcon/adapters/rewindable.rb +67 -0
  50. data/lib/falcon/command.rb +31 -0
  51. data/lib/falcon/command/host.rb +69 -0
  52. data/lib/falcon/command/paths.rb +47 -0
  53. data/lib/falcon/command/proxy.rb +71 -0
  54. data/lib/falcon/command/redirect.rb +76 -0
  55. data/lib/falcon/command/serve.rb +151 -0
  56. data/lib/falcon/command/supervisor.rb +78 -0
  57. data/lib/falcon/command/top.rb +94 -0
  58. data/lib/falcon/command/virtual.rb +89 -0
  59. data/lib/falcon/configuration.rb +147 -0
  60. data/lib/falcon/configuration/application.rb +46 -0
  61. data/lib/falcon/configuration/lets_encrypt_tls.rb +30 -0
  62. data/lib/falcon/configuration/proxy.rb +27 -0
  63. data/lib/falcon/configuration/rack.rb +38 -0
  64. data/lib/falcon/configuration/self_signed_tls.rb +47 -0
  65. data/lib/falcon/configuration/supervisor.rb +37 -0
  66. data/lib/falcon/configuration/tls.rb +70 -0
  67. data/lib/falcon/controller/host.rb +60 -0
  68. data/lib/falcon/controller/proxy.rb +109 -0
  69. data/lib/falcon/controller/redirect.rb +69 -0
  70. data/lib/falcon/controller/serve.rb +111 -0
  71. data/lib/falcon/controller/virtual.rb +100 -0
  72. data/lib/falcon/endpoint.rb +52 -0
  73. data/lib/falcon/extensions/openssl.rb +33 -0
  74. data/lib/falcon/middleware/proxy.rb +145 -0
  75. data/lib/falcon/middleware/redirect.rb +66 -0
  76. data/lib/falcon/proxy_endpoint.rb +71 -0
  77. data/lib/falcon/server.rb +54 -0
  78. data/lib/falcon/service/application.rb +93 -0
  79. data/lib/falcon/service/generic.rb +60 -0
  80. data/lib/falcon/service/proxy.rb +60 -0
  81. data/lib/falcon/service/supervisor.rb +104 -0
  82. data/lib/falcon/services.rb +78 -0
  83. data/lib/falcon/tls.rb +46 -0
  84. data/lib/falcon/verbose.rb +59 -0
  85. data/lib/falcon/version.rb +25 -0
  86. data/lib/rack/handler/falcon.rb +40 -0
  87. data/logo-square.afdesign +0 -0
  88. data/logo.afdesign +0 -0
  89. data/logo.svg +107 -0
  90. data/server.rb +21 -0
  91. data/tasks/benchmark.rake +103 -0
  92. metadata +386 -0
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'async/io/unix_endpoint'
24
+
25
+ module Falcon
26
+ class ProxyEndpoint < Async::IO::Endpoint
27
+ def initialize(endpoint, **options)
28
+ super(**options)
29
+
30
+ @endpoint = endpoint
31
+ end
32
+
33
+ def to_s
34
+ "\#<#{self.class} endpoint=#{@endpoint}>"
35
+ end
36
+
37
+ attr :endpoint
38
+
39
+ def protocol
40
+ @options[:protocol]
41
+ end
42
+
43
+ def scheme
44
+ @options[:scheme]
45
+ end
46
+
47
+ def authority
48
+ @options[:authority]
49
+ end
50
+
51
+ def connect(&block)
52
+ @endpoint.connect(&block)
53
+ end
54
+
55
+ def bind(&block)
56
+ @endpoint.bind(&block)
57
+ end
58
+
59
+ def each
60
+ return to_enum unless block_given?
61
+
62
+ @endpoint.each do |endpoint|
63
+ yield self.class.new(endpoint, **@options)
64
+ end
65
+ end
66
+
67
+ def self.unix(path, **options)
68
+ self.new(::Async::IO::Endpoint.unix(path), **options)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2017, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'async/http/server'
24
+
25
+ require 'protocol/http/middleware/builder'
26
+ require 'protocol/http/content_encoding'
27
+
28
+ require 'async/http/cache'
29
+
30
+ require_relative 'verbose'
31
+ require_relative 'adapters/rewindable'
32
+ require_relative 'adapters/rack'
33
+
34
+ module Falcon
35
+ class Server < Async::HTTP::Server
36
+ def self.middleware(rack_app, verbose: false, cache: true)
37
+ ::Protocol::HTTP::Middleware.build do
38
+ if verbose
39
+ use Verbose
40
+ end
41
+
42
+ if cache
43
+ use Async::HTTP::Cache::General
44
+ end
45
+
46
+ use ::Protocol::HTTP::ContentEncoding
47
+ use Adapters::Rewindable
48
+ use Adapters::Rack
49
+
50
+ run rack_app
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative 'proxy'
24
+
25
+ require 'async/http/endpoint'
26
+ require 'async/io/shared_endpoint'
27
+
28
+ module Falcon
29
+ module Service
30
+ class Application < Proxy
31
+ def initialize(environment)
32
+ super
33
+
34
+ @bound_endpoint = nil
35
+ end
36
+
37
+ def middleware
38
+ # In a multi-threaded container, we don't want to modify the shared evaluator's cache, so we create a new evaluator:
39
+ @environment.evaluator.middleware
40
+ end
41
+
42
+ def preload!
43
+ if scripts = @evaluator.preload
44
+ scripts.each do |path|
45
+ Async.logger.info(self) {"Preloading #{path}..."}
46
+ full_path = File.expand_path(path, self.root)
47
+ load(full_path)
48
+ end
49
+ end
50
+ end
51
+
52
+ def start
53
+ Async.logger.info(self) {"Binding to #{self.endpoint}..."}
54
+
55
+ @bound_endpoint = Async::Reactor.run do
56
+ Async::IO::SharedEndpoint.bound(self.endpoint)
57
+ end.wait
58
+
59
+ preload!
60
+
61
+ super
62
+ end
63
+
64
+ def setup(container)
65
+ protocol = self.protocol
66
+ scheme = self.scheme
67
+
68
+ container.run(name: self.name, restart: true) do |instance|
69
+ Async(logger: logger) do |task|
70
+ Async.logger.info(self) {"Starting application server for #{self.root}..."}
71
+
72
+ server = Server.new(self.middleware, @bound_endpoint, protocol, scheme)
73
+
74
+ server.run
75
+
76
+ instance.ready!
77
+
78
+ task.children.each(&:wait)
79
+ end
80
+ end
81
+
82
+ super
83
+ end
84
+
85
+ def stop
86
+ @bound_endpoint&.close
87
+ @bound_endpoint = nil
88
+
89
+ super
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 201, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ module Falcon
24
+ module Service
25
+ class Generic
26
+ def self.wrap(environment)
27
+ evaluator = environment.evaluator
28
+ service = evaluator.service || self
29
+
30
+ return service.new(environment)
31
+ end
32
+
33
+ def initialize(environment)
34
+ @environment = environment
35
+ @evaluator = @environment.evaluator
36
+ end
37
+
38
+ def include?(keys)
39
+ keys.all?{|key| @environment.include?(key)}
40
+ end
41
+
42
+ def name
43
+ @evaluator.name
44
+ end
45
+
46
+ def logger
47
+ return Async.logger # .with(name: name)
48
+ end
49
+
50
+ def start
51
+ end
52
+
53
+ def setup(container)
54
+ end
55
+
56
+ def stop
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require_relative 'generic'
24
+
25
+ require 'async/http/endpoint'
26
+ require 'async/io/shared_endpoint'
27
+
28
+ module Falcon
29
+ module Service
30
+ class Proxy < Generic
31
+ def name
32
+ "#{self.class} for #{self.authority}"
33
+ end
34
+
35
+ def authority
36
+ @evaluator.authority
37
+ end
38
+
39
+ def endpoint
40
+ @evaluator.endpoint
41
+ end
42
+
43
+ def ssl_context
44
+ @evaluator.ssl_context
45
+ end
46
+
47
+ def root
48
+ @evaluator.root
49
+ end
50
+
51
+ def protocol
52
+ endpoint.protocol
53
+ end
54
+
55
+ def scheme
56
+ endpoint.scheme
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
+ #
5
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ # of this software and associated documentation files (the "Software"), to deal
7
+ # in the Software without restriction, including without limitation the rights
8
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ # copies of the Software, and to permit persons to whom the Software is
10
+ # furnished to do so, subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be included in
13
+ # all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ # THE SOFTWARE.
22
+
23
+ require 'process/metrics'
24
+ require 'json'
25
+
26
+ require 'async/io/endpoint'
27
+ require 'async/io/shared_endpoint'
28
+
29
+ module Falcon
30
+ module Service
31
+ class Supervisor < Generic
32
+ def initialize(environment)
33
+ super
34
+
35
+ @bound_endpoint = nil
36
+ end
37
+
38
+ def endpoint
39
+ @evaluator.endpoint
40
+ end
41
+
42
+ def do_restart(message)
43
+ # Tell the parent of this process group to spin up a new process group/container.
44
+ # Wait for that to start accepting new connections.
45
+ # Stop accepting connections.
46
+ # Wait for existing connnections to drain.
47
+ # Terminate this process group.
48
+
49
+ signal = message[:signal] || :INT
50
+
51
+ Process.kill(signal, Process.ppid)
52
+ end
53
+
54
+ def do_metrics(message)
55
+ Process::Metrics::General.capture(pid: Process.ppid, ppid: Process.ppid)
56
+ end
57
+
58
+ def handle(message)
59
+ case message[:please]
60
+ when 'restart'
61
+ self.do_restart(message)
62
+ when 'metrics'
63
+ self.do_metrics(message)
64
+ end
65
+ end
66
+
67
+ def start
68
+ Async.logger.info(self) {"Binding to #{self.endpoint}..."}
69
+
70
+ @bound_endpoint = Async::Reactor.run do
71
+ Async::IO::SharedEndpoint.bound(self.endpoint)
72
+ end.wait
73
+
74
+ super
75
+ end
76
+
77
+ def setup(container)
78
+ container.run(name: self.name, restart: true, count: 1) do |instance|
79
+ Async do
80
+ @bound_endpoint.accept do |peer|
81
+ stream = Async::IO::Stream.new(peer)
82
+
83
+ while message = stream.gets("\0")
84
+ response = handle(JSON.parse(message, symbolize_names: true))
85
+ stream.puts(response.to_json, separator: "\0")
86
+ end
87
+ end
88
+
89
+ instance.ready!
90
+ end
91
+ end
92
+
93
+ super
94
+ end
95
+
96
+ def stop
97
+ @bound_endpoint&.close
98
+ @bound_endpoint = nil
99
+
100
+ super
101
+ end
102
+ end
103
+ end
104
+ end