falcon 0.42.3 → 0.44.0
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
- checksums.yaml.gz.sig +0 -0
- data/bake/falcon/supervisor.rb +3 -1
- data/changes.md +22 -0
- data/lib/falcon/command/host.rb +7 -50
- data/lib/falcon/command/paths.rb +2 -19
- data/lib/falcon/command/proxy.rb +21 -33
- data/lib/falcon/command/redirect.rb +22 -33
- data/lib/falcon/command/serve.rb +44 -82
- data/lib/falcon/command/supervisor.rb +2 -19
- data/lib/falcon/command/top.rb +2 -19
- data/lib/falcon/command/virtual.rb +16 -41
- data/lib/falcon/command.rb +3 -19
- data/lib/falcon/configuration.rb +28 -142
- data/lib/falcon/endpoint.rb +2 -19
- data/lib/falcon/environment/application.rb +60 -0
- data/lib/falcon/environment/lets_encrypt_tls.rb +34 -0
- data/lib/falcon/environment/proxy.rb +109 -0
- data/lib/falcon/environment/rack.rb +20 -0
- data/lib/falcon/environment/rackup.rb +26 -0
- data/lib/falcon/environment/redirect.rb +50 -0
- data/lib/falcon/environment/self_signed_tls.rb +45 -0
- data/lib/falcon/environment/server.rb +69 -0
- data/lib/falcon/environment/supervisor.rb +40 -0
- data/lib/falcon/environment/tls.rb +97 -0
- data/lib/falcon/environment.rb +13 -0
- data/lib/falcon/middleware/proxy.rb +3 -20
- data/lib/falcon/middleware/redirect.rb +2 -19
- data/lib/falcon/middleware/verbose.rb +2 -19
- data/lib/falcon/proxy_endpoint.rb +2 -19
- data/lib/falcon/railtie.rb +10 -0
- data/lib/falcon/server.rb +2 -19
- data/lib/falcon/service/server.rb +84 -0
- data/lib/falcon/service/supervisor.rb +5 -21
- data/lib/falcon/{controller → service}/virtual.rb +72 -36
- data/lib/falcon/tls.rb +2 -19
- data/lib/falcon/version.rb +3 -20
- data/lib/falcon.rb +5 -19
- data/lib/rack/handler/falcon.rb +4 -0
- data/lib/rackup/handler/falcon.rb +83 -0
- data/license.md +41 -0
- data/readme.md +60 -0
- data.tar.gz.sig +0 -0
- metadata +37 -117
- metadata.gz.sig +0 -0
- data/lib/.DS_Store +0 -0
- data/lib/falcon/controller/host.rb +0 -72
- data/lib/falcon/controller/proxy.rb +0 -126
- data/lib/falcon/controller/redirect.rb +0 -76
- data/lib/falcon/controller/serve.rb +0 -126
- data/lib/falcon/environments/application.rb +0 -72
- data/lib/falcon/environments/lets_encrypt_tls.rb +0 -47
- data/lib/falcon/environments/proxy.rb +0 -37
- data/lib/falcon/environments/rack.rb +0 -50
- data/lib/falcon/environments/self_signed_tls.rb +0 -55
- data/lib/falcon/environments/supervisor.rb +0 -51
- data/lib/falcon/environments/tls.rb +0 -103
- data/lib/falcon/environments.rb +0 -31
- data/lib/falcon/service/application.rb +0 -115
- data/lib/falcon/service/generic.rb +0 -78
- data/lib/falcon/service/proxy.rb +0 -66
- data/lib/falcon/services.rb +0 -99
    
        data/lib/falcon/configuration.rb
    CHANGED
    
    | @@ -1,26 +1,10 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            #  | 
| 4 | 
            -
            # 
         | 
| 5 | 
            -
            #  | 
| 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.
         | 
| 3 | 
            +
            # Released under the MIT License.
         | 
| 4 | 
            +
            # Copyright, 2019-2024, by Samuel Williams.
         | 
| 5 | 
            +
            # Copyright, 2019, by Sho Ito.
         | 
| 22 6 |  | 
| 23 | 
            -
            require ' | 
| 7 | 
            +
            require 'async/service'
         | 
| 24 8 |  | 
| 25 9 | 
             
            module Falcon
         | 
| 26 10 | 
             
            	# Manages environments which describes how to host a specific application.
         | 
| @@ -41,177 +25,79 @@ module Falcon | |
| 41 25 | 
             
            	#	end
         | 
| 42 26 | 
             
            	#	~~~
         | 
| 43 27 | 
             
            	#
         | 
| 44 | 
            -
            	class Configuration
         | 
| 45 | 
            -
            		# Initialize an empty configuration.
         | 
| 46 | 
            -
            		def initialize
         | 
| 47 | 
            -
            			@environments = {}
         | 
| 48 | 
            -
            		end
         | 
| 49 | 
            -
            		
         | 
| 50 | 
            -
            		# The map of named environments.
         | 
| 51 | 
            -
            		# @attribute [Hash(String, Build::Environment)]
         | 
| 52 | 
            -
            		attr :environments
         | 
| 53 | 
            -
            		
         | 
| 54 | 
            -
            		# Enumerate all environments that have the specified key.
         | 
| 55 | 
            -
            		# @parameter key [Symbol] Filter environments that don't have this key.
         | 
| 56 | 
            -
            		def each(key = :authority)
         | 
| 57 | 
            -
            			return to_enum(key) unless block_given?
         | 
| 58 | 
            -
            			
         | 
| 59 | 
            -
            			@environments.each do |name, environment|
         | 
| 60 | 
            -
            				environment = environment.flatten
         | 
| 61 | 
            -
            				
         | 
| 62 | 
            -
            				if environment.include?(key)
         | 
| 63 | 
            -
            					yield environment
         | 
| 64 | 
            -
            				end
         | 
| 65 | 
            -
            			end
         | 
| 66 | 
            -
            		end
         | 
| 67 | 
            -
            		
         | 
| 68 | 
            -
            		# Add the named environment to the configuration.
         | 
| 69 | 
            -
            		def add(environment)
         | 
| 70 | 
            -
            			name = environment.name
         | 
| 71 | 
            -
            			
         | 
| 72 | 
            -
            			unless name
         | 
| 73 | 
            -
            				raise ArgumentError, "Environment name is nil #{environment.inspect}"
         | 
| 74 | 
            -
            			end
         | 
| 75 | 
            -
            			
         | 
| 76 | 
            -
            			environment = environment.flatten
         | 
| 77 | 
            -
            			
         | 
| 78 | 
            -
            			raise KeyError.new("#{name.inspect} is already set", key: name) if @environments.key?(name)
         | 
| 79 | 
            -
            			
         | 
| 80 | 
            -
            			@environments[name] = environment
         | 
| 81 | 
            -
            		end
         | 
| 82 | 
            -
            		
         | 
| 28 | 
            +
            	class Configuration < ::Async::Service::Configuration
         | 
| 83 29 | 
             
            		# Load the specified configuration file. See {Loader#load_file} for more details.
         | 
| 84 30 | 
             
            		def load_file(path)
         | 
| 85 31 | 
             
            			Loader.load_file(self, path)
         | 
| 86 32 | 
             
            		end
         | 
| 87 33 |  | 
| 88 34 | 
             
            		# The domain specific language for loading configuration files.
         | 
| 89 | 
            -
            		class Loader
         | 
| 90 | 
            -
            			# Initialize the loader, attached to a specific configuration instance.
         | 
| 91 | 
            -
            			# Any environments generated by the loader will be added to the configuration.
         | 
| 92 | 
            -
            			# @parameter configuration [Configuration]
         | 
| 93 | 
            -
            			# @parameter root [String] The file-system root path for relative path computations.
         | 
| 94 | 
            -
            			def initialize(configuration, root = nil)
         | 
| 95 | 
            -
            				@loaded = {}
         | 
| 96 | 
            -
            				@configuration = configuration
         | 
| 97 | 
            -
            				@environments = {}
         | 
| 98 | 
            -
            				@root = root
         | 
| 99 | 
            -
            			end
         | 
| 100 | 
            -
            			
         | 
| 101 | 
            -
            			# The file-system root path which is injected into the environments as required.
         | 
| 102 | 
            -
            			# @attribute [String]
         | 
| 103 | 
            -
            			attr :root
         | 
| 104 | 
            -
            			
         | 
| 105 | 
            -
            			# The attached configuration instance.
         | 
| 106 | 
            -
            			# @attribute [Configuration]
         | 
| 107 | 
            -
            			attr :configuration
         | 
| 108 | 
            -
            			
         | 
| 109 | 
            -
            			# Load the specified file into the given configuration.
         | 
| 110 | 
            -
            			# @parameter configuration [Configuration]
         | 
| 111 | 
            -
            			# @oaram path [String] The path to the configuration file, e.g. `falcon.rb`.
         | 
| 112 | 
            -
            			def self.load_file(configuration, path)
         | 
| 113 | 
            -
            				path = File.realpath(path)
         | 
| 114 | 
            -
            				root = File.dirname(path)
         | 
| 115 | 
            -
            				
         | 
| 116 | 
            -
            				loader = self.new(configuration, root)
         | 
| 117 | 
            -
            				
         | 
| 118 | 
            -
            				loader.instance_eval(File.read(path), path)
         | 
| 119 | 
            -
            			end
         | 
| 120 | 
            -
            			
         | 
| 35 | 
            +
            		class Loader < ::Async::Service::Loader
         | 
| 121 36 | 
             
            			# Load specific features into the current configuration.
         | 
| 122 37 | 
             
            			#
         | 
| 123 | 
            -
            			#  | 
| 124 | 
            -
            			#
         | 
| 38 | 
            +
            			# @deprecated Use `require` instead.
         | 
| 125 39 | 
             
            			# @parameter features [Array(Symbol)] The features to load.
         | 
| 126 40 | 
             
            			def load(*features)
         | 
| 127 41 | 
             
            				features.each do |feature|
         | 
| 128 | 
            -
            					next if @loaded.include?(feature)
         | 
| 129 | 
            -
            					
         | 
| 130 42 | 
             
            					case feature
         | 
| 131 43 | 
             
            					when Symbol
         | 
| 132 | 
            -
            						 | 
| 133 | 
            -
            						
         | 
| 134 | 
            -
            						self.instance_eval(File.read(relative_path), relative_path)
         | 
| 135 | 
            -
            						
         | 
| 136 | 
            -
            						@loaded[feature] = relative_path
         | 
| 137 | 
            -
            					when Module
         | 
| 138 | 
            -
            						feature.load(self)
         | 
| 139 | 
            -
            						
         | 
| 140 | 
            -
            						@loaded[feature] = feature
         | 
| 44 | 
            +
            						require File.join(__dir__, "environment", "#{feature}.rb")
         | 
| 141 45 | 
             
            					else
         | 
| 142 46 | 
             
            						raise LoadError, "Unsure about how to load #{feature}!"
         | 
| 143 47 | 
             
            					end
         | 
| 144 48 | 
             
            				end
         | 
| 145 49 | 
             
            			end
         | 
| 146 50 |  | 
| 147 | 
            -
            			# Add the named environment, with zero or more parent environments, defined using the specified `block`.
         | 
| 148 | 
            -
            			# @parameter name [String] The name of the environment.
         | 
| 149 | 
            -
            			# @parameter parents [Array(Symbol)] The names of the parent environments to inherit.
         | 
| 150 | 
            -
            			# @yields {...} The block that will generate the environment.
         | 
| 151 | 
            -
            			def environment(name, *parents, &block)
         | 
| 152 | 
            -
            				raise KeyError.new("#{name} is already set", key: name) if @environments.key?(name)
         | 
| 153 | 
            -
            				@environments[name] = merge(name, *parents, &block)
         | 
| 154 | 
            -
            			end
         | 
| 155 | 
            -
            			
         | 
| 156 51 | 
             
            			# Define a host with the specified name.
         | 
| 157 52 | 
             
            			# Adds `root` and `authority` keys.
         | 
| 53 | 
            +
            			# @deprecated Use `service` and `include Falcon::Environment::Server` instead.
         | 
| 158 54 | 
             
            			# @parameter name [String] The name of the environment, usually a hostname.
         | 
| 159 55 | 
             
            			def host(name, *parents, &block)
         | 
| 160 | 
            -
            				 | 
| 161 | 
            -
             | 
| 162 | 
            -
            				 | 
| 163 | 
            -
            				environment[:authority] = name
         | 
| 164 | 
            -
            				
         | 
| 165 | 
            -
            				@configuration.add(environment.flatten)
         | 
| 56 | 
            +
            				@configuration.add(
         | 
| 57 | 
            +
            					merge(*parents, name: name, root: @root, authority: name, &block)
         | 
| 58 | 
            +
            				)
         | 
| 166 59 | 
             
            			end
         | 
| 167 60 |  | 
| 168 61 | 
             
            			# Define a proxy with the specified name.
         | 
| 169 62 | 
             
            			# Adds `root` and `authority` keys.
         | 
| 63 | 
            +
            			# @deprecated Use `service` and `include Falcon::Environment::Proxy` instead.
         | 
| 170 64 | 
             
            			# @parameter name [String] The name of the environment, usually a hostname.
         | 
| 171 65 | 
             
            			def proxy(name, *parents, &block)
         | 
| 172 | 
            -
            				 | 
| 173 | 
            -
             | 
| 174 | 
            -
            				 | 
| 175 | 
            -
            				environment[:authority] = name
         | 
| 176 | 
            -
            				
         | 
| 177 | 
            -
            				@configuration.add(environment.flatten)
         | 
| 66 | 
            +
            				@configuration.add(
         | 
| 67 | 
            +
            					merge(:proxy, *parents, name: name, root: @root, authority: name, &block)
         | 
| 68 | 
            +
            				)
         | 
| 178 69 | 
             
            			end
         | 
| 179 70 |  | 
| 180 71 | 
             
            			# Define a rack application with the specified name.
         | 
| 181 72 | 
             
            			# Adds `root` and `authority` keys.
         | 
| 73 | 
            +
            			# @deprecated Use `service` and `include Falcon::Environment::Rack` instead.
         | 
| 182 74 | 
             
            			# @parameter name [String] The name of the environment, usually a hostname.
         | 
| 183 75 | 
             
            			def rack(name, *parents, &block)
         | 
| 184 | 
            -
            				 | 
| 185 | 
            -
             | 
| 186 | 
            -
            				 | 
| 187 | 
            -
            				environment[:authority] = name
         | 
| 188 | 
            -
            				
         | 
| 189 | 
            -
            				@configuration.add(environment.flatten)
         | 
| 76 | 
            +
            				@configuration.add(
         | 
| 77 | 
            +
            					merge(:rack, *parents, name: name, root: @root, authority: name, &block)
         | 
| 78 | 
            +
            				)
         | 
| 190 79 | 
             
            			end
         | 
| 191 80 |  | 
| 192 81 | 
             
            			# Define a supervisor instance
         | 
| 193 | 
            -
            			#  | 
| 82 | 
            +
            			# @deprecated Use `service` and `include Falcon::Environment::Supervisor` instead.
         | 
| 194 83 | 
             
            			def supervisor(&block)
         | 
| 195 84 | 
             
            				name = File.join(@root, "supervisor")
         | 
| 196 | 
            -
            				environment = merge(name, :supervisor, &block)
         | 
| 197 85 |  | 
| 198 | 
            -
            				 | 
| 199 | 
            -
             | 
| 200 | 
            -
            				 | 
| 86 | 
            +
            				@configuration.add(
         | 
| 87 | 
            +
            					merge(:supervisor, name: name, root: @root, &block)
         | 
| 88 | 
            +
            				)
         | 
| 201 89 | 
             
            			end
         | 
| 202 90 |  | 
| 203 91 | 
             
            			private
         | 
| 204 92 |  | 
| 205 93 | 
             
            			# Build a new environment with the specified name and the given parents.
         | 
| 206 94 | 
             
            			# @parameter name [String]
         | 
| 207 | 
            -
            			# @parameter parents [Array( | 
| 95 | 
            +
            			# @parameter parents [Array(Symbol)]
         | 
| 208 96 | 
             
            			# @yields {...} The block that will generate the environment.
         | 
| 209 | 
            -
            			def merge( | 
| 210 | 
            -
            				 | 
| 211 | 
            -
            				
         | 
| 212 | 
            -
            				parent = Build::Environment.combine(*environments)
         | 
| 97 | 
            +
            			def merge(*parents, **initial, &block)
         | 
| 98 | 
            +
            				facets = parents.map{|parent| Environment::LEGACY_ENVIRONMENTS.fetch(parent)}
         | 
| 213 99 |  | 
| 214 | 
            -
            				 | 
| 100 | 
            +
            				::Async::Service::Environment.build(*facets, **initial, &block)
         | 
| 215 101 | 
             
            			end
         | 
| 216 102 | 
             
            		end
         | 
| 217 103 | 
             
            	end
         | 
    
        data/lib/falcon/endpoint.rb
    CHANGED
    
    | @@ -1,24 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            #  | 
| 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.
         | 
| 3 | 
            +
            # Released under the MIT License.
         | 
| 4 | 
            +
            # Copyright, 2018-2023, by Samuel Williams.
         | 
| 22 5 |  | 
| 23 6 | 
             
            require 'async/http/endpoint'
         | 
| 24 7 | 
             
            require 'localhost/authority'
         | 
| @@ -0,0 +1,60 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Released under the MIT License.
         | 
| 4 | 
            +
            # Copyright, 2019-2024, by Samuel Williams.
         | 
| 5 | 
            +
            # Copyright, 2020, by Daniel Evans.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            require_relative 'server'
         | 
| 8 | 
            +
            require_relative '../proxy_endpoint'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            module Falcon
         | 
| 11 | 
            +
            	module Environment
         | 
| 12 | 
            +
            		# Provides an environment for hosting a web application that uses TLS.
         | 
| 13 | 
            +
            		module Application
         | 
| 14 | 
            +
            			include Server
         | 
| 15 | 
            +
            			
         | 
| 16 | 
            +
            			# The middleware stack for the application.
         | 
| 17 | 
            +
            			# @returns [Protocol::HTTP::Middleware]
         | 
| 18 | 
            +
            			def middleware
         | 
| 19 | 
            +
            				::Protocol::HTTP::Middleware::HelloWorld
         | 
| 20 | 
            +
            			end
         | 
| 21 | 
            +
            			
         | 
| 22 | 
            +
            			# The scheme to use to communicate with the application.
         | 
| 23 | 
            +
            			# @returns [String]
         | 
| 24 | 
            +
            			def scheme
         | 
| 25 | 
            +
            				'https'
         | 
| 26 | 
            +
            			end
         | 
| 27 | 
            +
            			
         | 
| 28 | 
            +
            			# The protocol to use to communicate with the application.
         | 
| 29 | 
            +
            			#
         | 
| 30 | 
            +
            			# Typically one of {Async::HTTP::Protocol::HTTP1} or {Async::HTTP::Protocl::HTTP2}.
         | 
| 31 | 
            +
            			#
         | 
| 32 | 
            +
            			# @returns [Async::HTTP::Protocol]
         | 
| 33 | 
            +
            			def protocol
         | 
| 34 | 
            +
            				Async::HTTP::Protocol::HTTP2
         | 
| 35 | 
            +
            			end
         | 
| 36 | 
            +
            			
         | 
| 37 | 
            +
            			# The IPC path to use for communication with the application.
         | 
| 38 | 
            +
            			# @returns [String]
         | 
| 39 | 
            +
            			def ipc_path
         | 
| 40 | 
            +
            				::File.expand_path("application.ipc", root)
         | 
| 41 | 
            +
            			end
         | 
| 42 | 
            +
            			
         | 
| 43 | 
            +
            			# The endpoint that will be used for communicating with the application server.
         | 
| 44 | 
            +
            			# @returns [Async::IO::Endpoint]
         | 
| 45 | 
            +
            			def endpoint
         | 
| 46 | 
            +
            				::Falcon::ProxyEndpoint.unix(ipc_path,
         | 
| 47 | 
            +
            					protocol: protocol,
         | 
| 48 | 
            +
            					scheme: scheme,
         | 
| 49 | 
            +
            					authority: authority
         | 
| 50 | 
            +
            				)
         | 
| 51 | 
            +
            			end
         | 
| 52 | 
            +
            			
         | 
| 53 | 
            +
            			# Number of instances to start.
         | 
| 54 | 
            +
            			# @returns [Integer | nil]
         | 
| 55 | 
            +
            			def count
         | 
| 56 | 
            +
            				nil
         | 
| 57 | 
            +
            			end
         | 
| 58 | 
            +
            		end
         | 
| 59 | 
            +
            	end
         | 
| 60 | 
            +
            end
         | 
| @@ -0,0 +1,34 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Released under the MIT License.
         | 
| 4 | 
            +
            # Copyright, 2020-2024, by Samuel Williams.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require_relative 'tls'
         | 
| 7 | 
            +
            require_relative '../environment'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Falcon
         | 
| 10 | 
            +
            	module Environment
         | 
| 11 | 
            +
            		# Provides an environment that uses "Lets Encrypt" for TLS.
         | 
| 12 | 
            +
            		module LetsEncryptTLS
         | 
| 13 | 
            +
            			# The Lets Encrypt certificate store path.
         | 
| 14 | 
            +
            			# @parameter [String]
         | 
| 15 | 
            +
            			def lets_encrypt_root
         | 
| 16 | 
            +
            				'/etc/letsencrypt/live'
         | 
| 17 | 
            +
            			end
         | 
| 18 | 
            +
            			
         | 
| 19 | 
            +
            			# The public certificate path.
         | 
| 20 | 
            +
            			# @attribute [String]
         | 
| 21 | 
            +
            			def ssl_certificate_path
         | 
| 22 | 
            +
            				File.join(lets_encrypt_root, authority, "fullchain.pem")
         | 
| 23 | 
            +
            			end
         | 
| 24 | 
            +
            			
         | 
| 25 | 
            +
            			# The private key path.
         | 
| 26 | 
            +
            			# @attribute [String]
         | 
| 27 | 
            +
            			def ssl_private_key_path
         | 
| 28 | 
            +
            				File.join(lets_encrypt_root, authority, "privkey.pem")
         | 
| 29 | 
            +
            			end
         | 
| 30 | 
            +
            		end
         | 
| 31 | 
            +
            		
         | 
| 32 | 
            +
            		LEGACY_ENVIRONMENTS[:tls] = TLS
         | 
| 33 | 
            +
            	end
         | 
| 34 | 
            +
            end
         | 
| @@ -0,0 +1,109 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Released under the MIT License.
         | 
| 4 | 
            +
            # Copyright, 2019-2024, by Samuel Williams.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require_relative 'server'
         | 
| 7 | 
            +
            require_relative '../tls'
         | 
| 8 | 
            +
            require_relative '../middleware/proxy'
         | 
| 9 | 
            +
            require_relative '../environment'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            module Falcon
         | 
| 12 | 
            +
            	module Environment
         | 
| 13 | 
            +
            		# Provides an environment for hosting a TLS-capable reverse proxy using SNI.
         | 
| 14 | 
            +
            		module Proxy
         | 
| 15 | 
            +
            			include Server
         | 
| 16 | 
            +
            			
         | 
| 17 | 
            +
            			# The host that this proxy will receive connections for.
         | 
| 18 | 
            +
            			def url
         | 
| 19 | 
            +
            				"https://[::]:443"
         | 
| 20 | 
            +
            			end
         | 
| 21 | 
            +
            			
         | 
| 22 | 
            +
            			# The default SSL session identifier.
         | 
| 23 | 
            +
            			def tls_session_id
         | 
| 24 | 
            +
            				"falcon"
         | 
| 25 | 
            +
            			end
         | 
| 26 | 
            +
            			
         | 
| 27 | 
            +
            			# The services we will proxy to.
         | 
| 28 | 
            +
            			# @returns [Array(Async::Service::Environment)]
         | 
| 29 | 
            +
            			def environments
         | 
| 30 | 
            +
            				[]
         | 
| 31 | 
            +
            			end
         | 
| 32 | 
            +
            			
         | 
| 33 | 
            +
            			# The hosts we will proxy to. This is a hash of SNI authority -> evaluator.
         | 
| 34 | 
            +
            			# @returns [Hash(String, Async::Service::Environment::Evaluator)]
         | 
| 35 | 
            +
            			def hosts
         | 
| 36 | 
            +
            				hosts = {}
         | 
| 37 | 
            +
            				
         | 
| 38 | 
            +
            				environments.each do |environment|
         | 
| 39 | 
            +
            					evaluator = environment.evaluator
         | 
| 40 | 
            +
            					
         | 
| 41 | 
            +
            					# next unless environment.implements?(Falcon::Environment::Application)
         | 
| 42 | 
            +
            					if evaluator.key?(:authority) and evaluator.key?(:ssl_context) and evaluator.key?(:endpoint)
         | 
| 43 | 
            +
            						Console.info(self) {"Proxying #{self.url} to #{evaluator.authority} using #{evaluator.endpoint}"}
         | 
| 44 | 
            +
            						hosts[evaluator.authority] = evaluator
         | 
| 45 | 
            +
            						
         | 
| 46 | 
            +
            						if RUBY_VERSION < '3.1'
         | 
| 47 | 
            +
            							# Ensure the SSL context is set up before forking - it's buggy on Ruby < 3.1:
         | 
| 48 | 
            +
            							evaluator.ssl_context
         | 
| 49 | 
            +
            						end
         | 
| 50 | 
            +
            					end
         | 
| 51 | 
            +
            				end
         | 
| 52 | 
            +
            				
         | 
| 53 | 
            +
            				return hosts
         | 
| 54 | 
            +
            			end
         | 
| 55 | 
            +
            			
         | 
| 56 | 
            +
            			# Look up the host context for the given hostname, and update the socket hostname if necessary.
         | 
| 57 | 
            +
            			# @parameter socket [OpenSSL::SSL::SSLSocket] The incoming connection.
         | 
| 58 | 
            +
            			# @parameter hostname [String] The negotiated hostname.
         | 
| 59 | 
            +
            			def host_context(socket, hostname)
         | 
| 60 | 
            +
            				hosts = self.hosts
         | 
| 61 | 
            +
            				
         | 
| 62 | 
            +
            				if host = hosts[hostname]
         | 
| 63 | 
            +
            					Console.logger.debug(self) {"Resolving #{hostname} -> #{host}"}
         | 
| 64 | 
            +
            					
         | 
| 65 | 
            +
            					socket.hostname = hostname
         | 
| 66 | 
            +
            					
         | 
| 67 | 
            +
            					return host.ssl_context
         | 
| 68 | 
            +
            				else
         | 
| 69 | 
            +
            					Console.logger.warn(self, hosts: hosts.keys) {"Unable to resolve #{hostname}!"}
         | 
| 70 | 
            +
            					
         | 
| 71 | 
            +
            					return nil
         | 
| 72 | 
            +
            				end
         | 
| 73 | 
            +
            			end
         | 
| 74 | 
            +
            			
         | 
| 75 | 
            +
            			# Generate an SSL context which delegates to {host_context} to multiplex based on hostname.
         | 
| 76 | 
            +
            			def ssl_context
         | 
| 77 | 
            +
            				@server_context ||= OpenSSL::SSL::SSLContext.new.tap do |context|
         | 
| 78 | 
            +
            					context.servername_cb = Proc.new do |socket, hostname|
         | 
| 79 | 
            +
            						self.host_context(socket, hostname)
         | 
| 80 | 
            +
            					end
         | 
| 81 | 
            +
            					
         | 
| 82 | 
            +
            					context.session_id_context = @session_id
         | 
| 83 | 
            +
            					
         | 
| 84 | 
            +
            					context.ssl_version = :TLSv1_2_server
         | 
| 85 | 
            +
            					
         | 
| 86 | 
            +
            					context.set_params(
         | 
| 87 | 
            +
            						ciphers: ::Falcon::TLS::SERVER_CIPHERS,
         | 
| 88 | 
            +
            						verify_mode: ::OpenSSL::SSL::VERIFY_NONE,
         | 
| 89 | 
            +
            					)
         | 
| 90 | 
            +
            					
         | 
| 91 | 
            +
            					context.setup
         | 
| 92 | 
            +
            				end
         | 
| 93 | 
            +
            			end
         | 
| 94 | 
            +
            			
         | 
| 95 | 
            +
            			# The endpoint the server will bind to.
         | 
| 96 | 
            +
            			def endpoint
         | 
| 97 | 
            +
            				super.with(
         | 
| 98 | 
            +
            					ssl_context: self.ssl_context,
         | 
| 99 | 
            +
            				)
         | 
| 100 | 
            +
            			end
         | 
| 101 | 
            +
            			
         | 
| 102 | 
            +
            			def middleware
         | 
| 103 | 
            +
            				return Middleware::Proxy.new(Middleware::BadRequest, self.hosts)
         | 
| 104 | 
            +
            			end
         | 
| 105 | 
            +
            		end
         | 
| 106 | 
            +
            		
         | 
| 107 | 
            +
            		LEGACY_ENVIRONMENTS[:proxy] = Proxy
         | 
| 108 | 
            +
            	end
         | 
| 109 | 
            +
            end
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Released under the MIT License.
         | 
| 4 | 
            +
            # Copyright, 2019-2024, by Samuel Williams.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require_relative 'application'
         | 
| 7 | 
            +
            require_relative 'rackup'
         | 
| 8 | 
            +
            require_relative '../environment'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            module Falcon
         | 
| 11 | 
            +
            	module Environment
         | 
| 12 | 
            +
            		# Provides an environment for hosting a web application that use a Rackup `config.ru` file.
         | 
| 13 | 
            +
            		module Rack
         | 
| 14 | 
            +
            			include Application
         | 
| 15 | 
            +
            			include Rackup
         | 
| 16 | 
            +
            		end
         | 
| 17 | 
            +
            		
         | 
| 18 | 
            +
            		LEGACY_ENVIRONMENTS[:rack] = Rack
         | 
| 19 | 
            +
            	end
         | 
| 20 | 
            +
            end
         | 
| @@ -0,0 +1,26 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Released under the MIT License.
         | 
| 4 | 
            +
            # Copyright, 2024, by Samuel Williams.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require 'rack/builder'
         | 
| 7 | 
            +
            require_relative '../server'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Falcon
         | 
| 10 | 
            +
            	module Environment
         | 
| 11 | 
            +
            		# Provides an environment for hosting loading a Rackup `config.ru` file.
         | 
| 12 | 
            +
            		module Rackup
         | 
| 13 | 
            +
            			def rackup_path
         | 
| 14 | 
            +
            				'config.ru'
         | 
| 15 | 
            +
            			end
         | 
| 16 | 
            +
            			
         | 
| 17 | 
            +
            			def rack_app
         | 
| 18 | 
            +
            				::Rack::Builder.parse_file(rackup_path)
         | 
| 19 | 
            +
            			end
         | 
| 20 | 
            +
            			
         | 
| 21 | 
            +
            			def middleware
         | 
| 22 | 
            +
            				::Falcon::Server.middleware(rack_app, verbose: verbose, cache: cache)
         | 
| 23 | 
            +
            			end
         | 
| 24 | 
            +
            		end
         | 
| 25 | 
            +
            	end
         | 
| 26 | 
            +
            end
         | 
| @@ -0,0 +1,50 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Released under the MIT License.
         | 
| 4 | 
            +
            # Copyright, 2020-2024, by Samuel Williams.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require_relative 'server'
         | 
| 7 | 
            +
            require_relative '../middleware/redirect'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            module Falcon
         | 
| 10 | 
            +
            	module Environment
         | 
| 11 | 
            +
            		# Provides an environment for redirecting insecure web traffic to a secure endpoint.
         | 
| 12 | 
            +
            		module Redirect
         | 
| 13 | 
            +
            			include Server
         | 
| 14 | 
            +
            			
         | 
| 15 | 
            +
            			def redirect_url
         | 
| 16 | 
            +
            				"https://[::]:443"
         | 
| 17 | 
            +
            			end
         | 
| 18 | 
            +
            			
         | 
| 19 | 
            +
            			def redirect_endpoint
         | 
| 20 | 
            +
            				Async::HTTP::Endpoint.parse(redirect_url)
         | 
| 21 | 
            +
            			end
         | 
| 22 | 
            +
            			
         | 
| 23 | 
            +
            			# The services we will redirect to.
         | 
| 24 | 
            +
            			# @returns [Array(Async::Service::Environment)]
         | 
| 25 | 
            +
            			def environments
         | 
| 26 | 
            +
            				[]
         | 
| 27 | 
            +
            			end
         | 
| 28 | 
            +
            			
         | 
| 29 | 
            +
            			def hosts
         | 
| 30 | 
            +
            				hosts = {}
         | 
| 31 | 
            +
            				
         | 
| 32 | 
            +
            				environments.each do |environment|
         | 
| 33 | 
            +
            					evaluator = environment.evaluator
         | 
| 34 | 
            +
            					
         | 
| 35 | 
            +
            					if environment.implements?(Falcon::Environment::Application)
         | 
| 36 | 
            +
            						Console.info(self) {"Redirecting #{self.url} to #{evaluator.authority}"}
         | 
| 37 | 
            +
            						hosts[evaluator.authority] = evaluator
         | 
| 38 | 
            +
            					end
         | 
| 39 | 
            +
            				end
         | 
| 40 | 
            +
            				
         | 
| 41 | 
            +
            				return hosts
         | 
| 42 | 
            +
            			end
         | 
| 43 | 
            +
            			
         | 
| 44 | 
            +
            			# Load the {Middleware::Redirect} application with the specified hosts.
         | 
| 45 | 
            +
            			def middleware
         | 
| 46 | 
            +
            				Middleware::Redirect.new(Middleware::NotFound, hosts, redirect_endpoint)
         | 
| 47 | 
            +
            			end
         | 
| 48 | 
            +
            		end
         | 
| 49 | 
            +
            	end
         | 
| 50 | 
            +
            end
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Released under the MIT License.
         | 
| 4 | 
            +
            # Copyright, 2019-2024, by Samuel Williams.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require 'localhost/authority'
         | 
| 7 | 
            +
            require_relative 'tls'
         | 
| 8 | 
            +
            require_relative '../environment'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            module Falcon
         | 
| 11 | 
            +
            	module Environment
         | 
| 12 | 
            +
            		# Provides an environment that exposes a self-signed TLS certificate using the `localhost` gem.
         | 
| 13 | 
            +
            		module SelfSignedTLS
         | 
| 14 | 
            +
            			# The default session identifier for the session cache.
         | 
| 15 | 
            +
            			# @returns [String]
         | 
| 16 | 
            +
            			def ssl_session_id
         | 
| 17 | 
            +
            				"falcon"
         | 
| 18 | 
            +
            			end
         | 
| 19 | 
            +
            			
         | 
| 20 | 
            +
            			# The SSL context to use for incoming connections.
         | 
| 21 | 
            +
            			# @returns [OpenSSL::SSL::SSLContext]
         | 
| 22 | 
            +
            			def ssl_context
         | 
| 23 | 
            +
            				contexts = Localhost::Authority.fetch(authority)
         | 
| 24 | 
            +
            				
         | 
| 25 | 
            +
            				contexts.server_context.tap do |context|
         | 
| 26 | 
            +
            					context.alpn_select_cb = lambda do |protocols|
         | 
| 27 | 
            +
            						if protocols.include? "h2"
         | 
| 28 | 
            +
            							return "h2"
         | 
| 29 | 
            +
            						elsif protocols.include? "http/1.1"
         | 
| 30 | 
            +
            							return "http/1.1"
         | 
| 31 | 
            +
            						elsif protocols.include? "http/1.0"
         | 
| 32 | 
            +
            							return "http/1.0"
         | 
| 33 | 
            +
            						else
         | 
| 34 | 
            +
            							return nil
         | 
| 35 | 
            +
            						end
         | 
| 36 | 
            +
            					end
         | 
| 37 | 
            +
            					
         | 
| 38 | 
            +
            					context.session_id_context = ssl_session_id
         | 
| 39 | 
            +
            				end
         | 
| 40 | 
            +
            			end
         | 
| 41 | 
            +
            		end
         | 
| 42 | 
            +
            		
         | 
| 43 | 
            +
            		LEGACY_ENVIRONMENTS[:self_signed_tls] = SelfSignedTLS
         | 
| 44 | 
            +
            	end
         | 
| 45 | 
            +
            end
         | 
| @@ -0,0 +1,69 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            # Released under the MIT License.
         | 
| 4 | 
            +
            # Copyright, 2024, by Samuel Williams.
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require 'async/service/generic'
         | 
| 7 | 
            +
            require 'async/http/endpoint'
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            require_relative '../service/server'
         | 
| 10 | 
            +
            require_relative '../server'
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            module Falcon
         | 
| 13 | 
            +
            	module Environment
         | 
| 14 | 
            +
            		# Provides an environment for hosting a web application that uses a Falcon server.
         | 
| 15 | 
            +
            		module Server
         | 
| 16 | 
            +
            			# The service class to use for the proxy.
         | 
| 17 | 
            +
            			# @returns [Class]
         | 
| 18 | 
            +
            			def service_class
         | 
| 19 | 
            +
            				Service::Server
         | 
| 20 | 
            +
            			end
         | 
| 21 | 
            +
            			
         | 
| 22 | 
            +
            			# The server authority. Defaults to the server name.
         | 
| 23 | 
            +
            			# @returns [String]
         | 
| 24 | 
            +
            			def authority
         | 
| 25 | 
            +
            				self.name
         | 
| 26 | 
            +
            			end
         | 
| 27 | 
            +
            			
         | 
| 28 | 
            +
            			# Options to use when creating the container.
         | 
| 29 | 
            +
            			def container_options
         | 
| 30 | 
            +
            				{restart: true}
         | 
| 31 | 
            +
            			end
         | 
| 32 | 
            +
            			
         | 
| 33 | 
            +
            			# The host that this server will receive connections for.
         | 
| 34 | 
            +
            			def url
         | 
| 35 | 
            +
            				"http://[::]:9292"
         | 
| 36 | 
            +
            			end
         | 
| 37 | 
            +
            			
         | 
| 38 | 
            +
            			def timeout
         | 
| 39 | 
            +
            				nil
         | 
| 40 | 
            +
            			end
         | 
| 41 | 
            +
            			
         | 
| 42 | 
            +
            			# The upstream endpoint that will handle incoming requests.
         | 
| 43 | 
            +
            			# @returns [Async::HTTP::Endpoint]
         | 
| 44 | 
            +
            			def endpoint
         | 
| 45 | 
            +
            				::Async::HTTP::Endpoint.parse(url).with(
         | 
| 46 | 
            +
            					reuse_address: true,
         | 
| 47 | 
            +
            					timeout: timeout,
         | 
| 48 | 
            +
            				)
         | 
| 49 | 
            +
            			end
         | 
| 50 | 
            +
            			
         | 
| 51 | 
            +
            			def verbose
         | 
| 52 | 
            +
            				false
         | 
| 53 | 
            +
            			end
         | 
| 54 | 
            +
            			
         | 
| 55 | 
            +
            			def cache
         | 
| 56 | 
            +
            				false
         | 
| 57 | 
            +
            			end
         | 
| 58 | 
            +
            			
         | 
| 59 | 
            +
            			def client_endpoint
         | 
| 60 | 
            +
            				::Async::HTTP::Endpoint.parse(url)
         | 
| 61 | 
            +
            			end
         | 
| 62 | 
            +
            			
         | 
| 63 | 
            +
            			# Any scripts to preload before starting the server.
         | 
| 64 | 
            +
            			def preload
         | 
| 65 | 
            +
            				[]
         | 
| 66 | 
            +
            			end
         | 
| 67 | 
            +
            		end
         | 
| 68 | 
            +
            	end
         | 
| 69 | 
            +
            end
         |