falcon 0.36.4

Sign up to get free protection for your applications and to get access to all the features.
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,31 @@
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_relative 'command/top'
24
+
25
+ module Falcon
26
+ module Command
27
+ def self.call(*arguments)
28
+ Top.call(*arguments)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,69 @@
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 '../controller/host'
24
+ require_relative '../configuration'
25
+ require_relative '../version'
26
+
27
+ require 'samovar'
28
+
29
+ module Falcon
30
+ module Command
31
+ class Host < Samovar::Command
32
+ self.description = "Host the specified applications."
33
+
34
+ many :paths, "Service configuration paths.", default: ["falcon.rb"]
35
+
36
+ def container_class
37
+ Async::Container.best_container_class
38
+ end
39
+
40
+ def configuration(verbose = false)
41
+ configuration = Configuration.new(verbose)
42
+
43
+ @paths.each do |path|
44
+ path = File.expand_path(path)
45
+ configuration.load_file(path)
46
+ end
47
+
48
+ return configuration
49
+ end
50
+
51
+ def controller
52
+ Controller::Host.new(self)
53
+ end
54
+
55
+ def call
56
+ Async.logger.info(self) do |buffer|
57
+ buffer.puts "Falcon Host v#{VERSION} taking flight!"
58
+ buffer.puts "- Configuration: #{@paths.join(', ')}"
59
+ buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
60
+ buffer.puts "- To reload: kill -HUP #{Process.pid}"
61
+ end
62
+
63
+ Bundler.require(:preload)
64
+
65
+ self.controller.run
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright, 2019, 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 '../configuration'
24
+
25
+ module Falcon
26
+ module Command
27
+ module Paths
28
+ def resolved_paths(&block)
29
+ @paths.collect do |path|
30
+ Dir.glob(path)
31
+ end.flatten.sort.uniq.each(&block)
32
+ end
33
+
34
+ def configuration
35
+ configuration = Configuration.new
36
+
37
+ self.resolved_paths.each do |path|
38
+ path = File.expand_path(path)
39
+
40
+ configuration.load_file(path)
41
+ end
42
+
43
+ return configuration
44
+ end
45
+ end
46
+ end
47
+ end
@@ -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_relative '../controller/proxy'
24
+ require_relative 'paths'
25
+
26
+ require 'samovar'
27
+
28
+ module Falcon
29
+ module Command
30
+ class Proxy < Samovar::Command
31
+ self.description = "Proxy to one or more backend hosts."
32
+
33
+ options do
34
+ option '--bind <address>', "Bind to the given hostname/address", default: "https://[::]:443"
35
+
36
+ option '-t/--timeout <duration>', "Specify the maximum time to wait for non-blocking operations.", type: Float, default: nil
37
+ end
38
+
39
+ many :paths
40
+
41
+ include Paths
42
+
43
+ def controller
44
+ Controller::Proxy.new(self)
45
+ end
46
+
47
+ def container_class
48
+ Async::Container.best_container_class
49
+ end
50
+
51
+ def container_options
52
+ {}
53
+ end
54
+
55
+ def call
56
+ Async.logger.info(self) do |buffer|
57
+ buffer.puts "Falcon Proxy v#{VERSION} taking flight!"
58
+ buffer.puts "- Binding to: #{@options[:bind]}"
59
+ buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
60
+ buffer.puts "- To reload: kill -HUP #{Process.pid}"
61
+ end
62
+
63
+ self.controller.run
64
+ end
65
+
66
+ def endpoint(**options)
67
+ Async::HTTP::Endpoint.parse(@options[:bind], timeout: @options[:timeout], **options)
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,76 @@
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 '../controller/redirect'
24
+ require_relative 'paths'
25
+
26
+ require 'samovar'
27
+
28
+ module Falcon
29
+ module Command
30
+ class Redirect < Samovar::Command
31
+ self.description = "Redirect from insecure HTTP to secure HTTP."
32
+
33
+ options do
34
+ option '--bind <address>', "Bind to the given hostname/address", default: "http://[::]:80"
35
+ option '--redirect <address>', "Redirect using this address as a template.", default: "https://[::]:443"
36
+
37
+ option '-t/--timeout <duration>', "Specify the maximum time to wait for non-blocking operations.", type: Float, default: nil
38
+ end
39
+
40
+ many :paths
41
+
42
+ include Paths
43
+
44
+ def controller
45
+ Controller::Redirect.new(self)
46
+ end
47
+
48
+ def container_class
49
+ Async::Container.best_container_class
50
+ end
51
+
52
+ def container_options
53
+ {}
54
+ end
55
+
56
+ def call
57
+ Async.logger.info(self) do |buffer|
58
+ buffer.puts "Falcon Redirect v#{VERSION} taking flight!"
59
+ buffer.puts "- Binding to: #{@options[:bind]}"
60
+ buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
61
+ buffer.puts "- To reload: kill -HUP #{Process.pid}"
62
+ end
63
+
64
+ self.controller.run
65
+ end
66
+
67
+ def endpoint(**options)
68
+ Async::HTTP::Endpoint.parse(@options[:bind], timeout: @options[:timeout], **options)
69
+ end
70
+
71
+ def redirect_endpoint(**options)
72
+ Async::HTTP::Endpoint.parse(@options[:redirect], **options)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,151 @@
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_relative '../server'
24
+ require_relative '../endpoint'
25
+ require_relative '../controller/serve'
26
+
27
+ require 'async/container'
28
+
29
+ require 'async/io/trap'
30
+ require 'async/io/host_endpoint'
31
+ require 'async/io/shared_endpoint'
32
+ require 'async/io/ssl_endpoint'
33
+
34
+ require 'samovar'
35
+
36
+ require 'rack/builder'
37
+ require 'rack/server'
38
+
39
+ require 'bundler'
40
+
41
+ module Falcon
42
+ module Command
43
+ class Serve < Samovar::Command
44
+ self.description = "Run an HTTP server for development purposes."
45
+
46
+ options do
47
+ option '-b/--bind <address>', "Bind to the given hostname/address.", default: "https://localhost:9292"
48
+
49
+ option '-p/--port <number>', "Override the specified port.", type: Integer
50
+ option '-h/--hostname <hostname>', "Specify the hostname which would be used for certificates, etc."
51
+ option '-t/--timeout <duration>', "Specify the maximum time to wait for non-blocking operations.", type: Float, default: nil
52
+
53
+ option '-c/--config <path>', "Rackup configuration file to load.", default: 'config.ru'
54
+ option '--preload <path>', "Preload the specified path before creating containers."
55
+
56
+ option '--cache', "Enable the response cache."
57
+
58
+ option '--forked | --threaded | --hybrid', "Select a specific parallelism model.", key: :container, default: :forked
59
+
60
+ option '-n/--count <count>', "Number of instances to start.", default: Async::Container.processor_count, type: Integer
61
+
62
+ option '--forks <count>', "Number of forks (hybrid only).", type: Integer
63
+ option '--threads <count>', "Number of threads (hybrid only).", type: Integer
64
+ end
65
+
66
+ def container_class
67
+ case @options[:container]
68
+ when :threaded
69
+ return Async::Container::Threaded
70
+ when :forked
71
+ return Async::Container::Forked
72
+ when :hybrid
73
+ return Async::Container::Hybrid
74
+ end
75
+ end
76
+
77
+ def verbose?
78
+ @parent&.verbose?
79
+ end
80
+
81
+ def cache?
82
+ @options[:cache]
83
+ end
84
+
85
+ def load_app
86
+ rack_app, _ = Rack::Builder.parse_file(@options[:config])
87
+
88
+ return Server.middleware(rack_app, verbose: self.verbose?, cache: self.cache?)
89
+ end
90
+
91
+ def slice_options(*keys)
92
+ # TODO: Ruby 2.5 introduced Hash#slice
93
+ options = {}
94
+
95
+ keys.each do |key|
96
+ if @options.key?(key)
97
+ options[key] = @options[key]
98
+ end
99
+ end
100
+
101
+ return options
102
+ end
103
+
104
+ def container_options
105
+ slice_options(:count, :forks, :threads)
106
+ end
107
+
108
+ def endpoint_options
109
+ slice_options(:hostname, :port, :reuse_port, :timeout)
110
+ end
111
+
112
+ def endpoint
113
+ Endpoint.parse(@options[:bind], **endpoint_options)
114
+ end
115
+
116
+ def client_endpoint
117
+ Async::HTTP::Endpoint.parse(@options[:bind], **endpoint_options)
118
+ end
119
+
120
+ def client
121
+ Async::HTTP::Client.new(client_endpoint)
122
+ end
123
+
124
+ def controller
125
+ Controller::Serve.new(self)
126
+ end
127
+
128
+ def call
129
+ Async.logger.info(self) do |buffer|
130
+ buffer.puts "Falcon v#{VERSION} taking flight! Using #{self.container_class} #{self.container_options}."
131
+ buffer.puts "- Binding to: #{self.endpoint}"
132
+ buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
133
+ buffer.puts "- To reload configuration: kill -HUP #{Process.pid}"
134
+ end
135
+
136
+ if path = @options[:preload]
137
+ full_path = File.expand_path(path)
138
+ load(full_path)
139
+ end
140
+
141
+ Bundler.require(:preload)
142
+
143
+ if GC.respond_to?(:compact)
144
+ GC.compact
145
+ end
146
+
147
+ self.controller.run
148
+ end
149
+ end
150
+ end
151
+ end