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,78 @@
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 'samovar'
24
+ require 'async'
25
+ require 'json'
26
+
27
+ require 'async/io/stream'
28
+ require 'async/io/unix_endpoint'
29
+
30
+ module Falcon
31
+ module Command
32
+ class Supervisor < Samovar::Command
33
+ self.description = "Control and query a specific host."
34
+
35
+ options do
36
+ option "--path <path>", "The control IPC path.", default: "supervisor.ipc"
37
+ end
38
+
39
+ class Restart < Samovar::Command
40
+ self.description = "Restart the process group."
41
+
42
+ def call(stream)
43
+ stream.puts({please: 'restart'}.to_json, separator: "\0")
44
+ end
45
+ end
46
+
47
+ class Metrics < Samovar::Command
48
+ self.description = "Show metrics about the falcon processes."
49
+
50
+ def call(stream)
51
+ stream.puts({please: 'metrics'}.to_json, separator: "\0")
52
+ response = JSON.parse(stream.gets("\0"), symbolize_names: true)
53
+
54
+ pp response
55
+ end
56
+ end
57
+
58
+ nested :command, {
59
+ 'restart' => Restart,
60
+ 'metrics' => Metrics,
61
+ }, default: 'metrics'
62
+
63
+ def endpoint
64
+ Async::IO::Endpoint.unix(@options[:path])
65
+ end
66
+
67
+ def call
68
+ Async do
69
+ endpoint.connect do |socket|
70
+ stream = Async::IO::Stream.new(socket)
71
+
72
+ @command.call(stream)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,94 @@
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 'serve'
24
+ require_relative 'host'
25
+ require_relative 'virtual'
26
+ require_relative 'proxy'
27
+ require_relative 'redirect'
28
+ require_relative 'supervisor'
29
+
30
+ require_relative '../version'
31
+
32
+ require 'samovar'
33
+
34
+ module Falcon
35
+ module Command
36
+ class Top < Samovar::Command
37
+ self.description = "An asynchronous HTTP server."
38
+
39
+ options do
40
+ option '--verbose | --quiet', "Verbosity of output for debugging.", key: :logging
41
+ option '-h/--help', "Print out help information."
42
+ option '-v/--version', "Print out the application version."
43
+ option '-e/--encoding', "Specify the default external encoding of the server.", default: "UTF-8"
44
+ end
45
+
46
+ nested :command, {
47
+ 'serve' => Serve,
48
+ 'host' => Host,
49
+ 'virtual' => Virtual,
50
+ 'proxy' => Proxy,
51
+ 'redirect' => Redirect,
52
+ 'supervisor' => Supervisor,
53
+ }, default: 'serve'
54
+
55
+ def verbose?
56
+ @options[:logging] == :verbose
57
+ end
58
+
59
+ def quiet?
60
+ @options[:logging] == :quiet
61
+ end
62
+
63
+ # If you don't specify these, it's possible to have issues when encodings mismatch on the server.
64
+ def update_external_encoding!(encoding = Encoding::UTF_8)
65
+ if Encoding.default_external != encoding
66
+ Console.logger.warn(self) {"Updating Encoding.default_external from #{Encoding.default_external} to #{encoding}"}
67
+ Encoding.default_external = encoding
68
+ end
69
+ end
70
+
71
+ def encoding
72
+ if name = @options[:encoding]
73
+ Encoding.find(name)
74
+ end
75
+ end
76
+
77
+ def call
78
+ if encoding = self.encoding
79
+ update_external_encoding!(encoding)
80
+ else
81
+ update_external_encoding!
82
+ end
83
+
84
+ if @options[:version]
85
+ puts "#{self.name} v#{Falcon::VERSION}"
86
+ elsif @options[:help]
87
+ self.print_usage
88
+ else
89
+ @command.call
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,89 @@
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/virtual'
24
+ require_relative 'paths'
25
+
26
+ require 'samovar'
27
+
28
+ module Falcon
29
+ module Command
30
+ class Virtual < Samovar::Command
31
+ self.description = "Run one or more virtual hosts with a front-end proxy."
32
+
33
+ options do
34
+ option '--bind-insecure <address>', "Bind redirection to the given hostname/address", default: "http://[::]:80"
35
+ option '--bind-secure <address>', "Bind proxy to the given hostname/address", default: "https://[::]:443"
36
+
37
+ option '-t/--timeout <duration>', "Specify the maximum time to wait for non-blocking operations.", type: Float, default: 30
38
+ end
39
+
40
+ many :paths
41
+
42
+ include Paths
43
+
44
+ def controller
45
+ Controller::Virtual.new(self)
46
+ end
47
+
48
+ def bind_secure
49
+ @options[:bind_secure]
50
+ end
51
+
52
+ def bind_insecure
53
+ @options[:bind_insecure]
54
+ end
55
+
56
+ def timeout
57
+ @options[:timeout]
58
+ end
59
+
60
+ def call
61
+ Async.logger.info(self) do |buffer|
62
+ buffer.puts "Falcon Virtual v#{VERSION} taking flight!"
63
+ buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
64
+ buffer.puts "- To reload all sites: kill -HUP #{Process.pid}"
65
+ end
66
+
67
+ self.controller.run
68
+ end
69
+
70
+ def insecure_endpoint(**options)
71
+ Async::HTTP::Endpoint.parse(@options[:bind_insecure], **options)
72
+ end
73
+
74
+ def secure_endpoint(**options)
75
+ Async::HTTP::Endpoint.parse(@options[:bind_secure], **options)
76
+ end
77
+
78
+ # An endpoint suitable for connecting to the specified hostname.
79
+ def host_endpoint(hostname, **options)
80
+ endpoint = secure_endpoint(**options)
81
+
82
+ url = URI.parse(@options[:bind_secure])
83
+ url.hostname = hostname
84
+
85
+ return Async::HTTP::Endpoint.new(url, hostname: endpoint.hostname)
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,147 @@
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 'build/environment'
24
+
25
+ module Falcon
26
+ class Configuration
27
+ def initialize(verbose = false)
28
+ @environments = {}
29
+ end
30
+
31
+ attr :environments
32
+
33
+ def each(key = :authority)
34
+ return to_enum(key) unless block_given?
35
+
36
+ @environments.each do |name, environment|
37
+ environment = environment.flatten
38
+
39
+ if environment.include?(key)
40
+ yield environment
41
+ end
42
+ end
43
+ end
44
+
45
+ def add(environment)
46
+ name = environment.name
47
+
48
+ unless name
49
+ raise ArgumentError, "Environment name is nil #{environment.inspect}"
50
+ end
51
+
52
+ environment = environment.flatten
53
+
54
+ raise KeyError.new("#{name.inspect} is already set", key: name) if @environments.key?(name)
55
+
56
+ @environments[name] = environment
57
+ end
58
+
59
+ def load_file(path)
60
+ Loader.load_file(self, path)
61
+ end
62
+
63
+ class Loader
64
+ def initialize(configuration, root = nil)
65
+ @loaded = {}
66
+ @configuration = configuration
67
+ @environments = {}
68
+ @root = root
69
+ end
70
+
71
+ attr :path
72
+ attr :configuration
73
+
74
+ def self.load_file(configuration, path)
75
+ path = File.realpath(path)
76
+ root = File.dirname(path)
77
+
78
+ loader = self.new(configuration, root)
79
+
80
+ loader.instance_eval(File.read(path), path)
81
+ end
82
+
83
+ def load(*features)
84
+ features.each do |feature|
85
+ next if @loaded.include?(feature)
86
+
87
+ relative_path = File.join(__dir__, "configuration", "#{feature}.rb")
88
+
89
+ self.instance_eval(File.read(relative_path), relative_path)
90
+
91
+ @loaded[feature] = relative_path
92
+ end
93
+ end
94
+
95
+ def add(name, *parents, &block)
96
+ raise KeyError.new("#{name} is already set", key: name) if @environments.key?(name)
97
+ @environments[name] = merge(name, *parents, &block)
98
+ end
99
+
100
+ def host(name, *parents, &block)
101
+ environment = merge(name, :host, *parents, &block)
102
+
103
+ environment[:root] = @root
104
+ environment[:authority] = name
105
+
106
+ @configuration.add(environment.flatten)
107
+ end
108
+
109
+ def proxy(name, *parents, &block)
110
+ environment = merge(name, :proxy, *parents, &block)
111
+
112
+ environment[:root] = @root
113
+ environment[:authority] = name
114
+
115
+ @configuration.add(environment.flatten)
116
+ end
117
+
118
+ def rack(name, *parents, &block)
119
+ environment = merge(name, :rack, *parents, &block)
120
+
121
+ environment[:root] = @root
122
+ environment[:authority] = name
123
+
124
+ @configuration.add(environment.flatten)
125
+ end
126
+
127
+ def supervisor
128
+ name = File.join(@root, "supervisor")
129
+ environment = merge(name, :supervisor)
130
+
131
+ environment[:root] = @root
132
+
133
+ @configuration.add(environment.flatten)
134
+ end
135
+
136
+ private
137
+
138
+ def merge(name, *parents, &block)
139
+ environments = parents.map{|name| @environments.fetch(name)}
140
+
141
+ parent = Build::Environment.combine(*environments)
142
+
143
+ Build::Environment.new(parent, name: name, &block)
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,46 @@
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 '../proxy_endpoint'
24
+ require_relative '../server'
25
+
26
+ require_relative '../service/application'
27
+
28
+ add(:application) do
29
+ middleware do
30
+ ::Protocol::HTTP::Middleware::HelloWorld
31
+ end
32
+
33
+ scheme 'https'
34
+ protocol {Async::HTTP::Protocol::HTTP2}
35
+ ipc_path {::File.expand_path("application.ipc", root)}
36
+
37
+ endpoint do
38
+ ::Falcon::ProxyEndpoint.unix(ipc_path,
39
+ protocol: protocol,
40
+ scheme: scheme,
41
+ authority: authority
42
+ )
43
+ end
44
+
45
+ service ::Falcon::Service::Application
46
+ end