falcon 0.25.0 → 0.26.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/beer/config.ru +5 -1
- data/examples/push/client.rb +28 -0
- data/examples/push/config.ru +9 -1
- data/examples/push/script.js +2 -0
- data/falcon.gemspec +10 -9
- data/lib/falcon/command/virtual.rb +9 -66
- data/lib/falcon/configuration.rb +195 -0
- data/lib/falcon/hosts.rb +78 -140
- data/lib/falcon/proxy.rb +13 -4
- data/lib/falcon/redirection.rb +9 -4
- data/lib/falcon/version.rb +1 -1
- data/server.rb +13 -29
- metadata +34 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fab1eafce5a511075f7339beabec2ef2364693ffeb050b84e90a8223e3faf614
|
4
|
+
data.tar.gz: 6f2512f83e06c16ab5600c66ceefc0d2ea910e6f8a53df3d8d919600c75671af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ce28b54426aa8ccbba9801e18c548a284ffbc8c64b54a9cf03b6d3af75ed11b029b999de1e923509b8e16ff525c465ac82695e6a37c9a207f002447333d5c83
|
7
|
+
data.tar.gz: 916bf5e17a59d440c633471a809fad988693f050d8912dd236150983bbb81a4fd2235406418f5a740072aec8e21ed8844593c123d47533d6b6255ddaa7221d14
|
data/examples/beer/config.ru
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
#!/usr/bin/env falcon --verbose serve -c
|
2
2
|
|
3
3
|
require 'rack'
|
4
|
+
require 'trenni'
|
4
5
|
|
5
6
|
def bottles(n)
|
6
7
|
n == 1 ? "#{n} bottle" : "#{n} bottles"
|
@@ -27,7 +28,7 @@ run lambda {|env|
|
|
27
28
|
count.downto(1) do |i|
|
28
29
|
task.annotate "bottles of beer #{i}"
|
29
30
|
|
30
|
-
|
31
|
+
Async.logger.info(body) {"#{bottles(i)} of beer on the wall..."}
|
31
32
|
body.write("<p>#{bottles(i)} of beer on the wall, ")
|
32
33
|
task.sleep(0.1)
|
33
34
|
body.write("#{bottles(i)} of beer, ")
|
@@ -39,6 +40,9 @@ run lambda {|env|
|
|
39
40
|
body.write("<script>var child; while (child = document.body.firstChild) child.remove();</script>")
|
40
41
|
end
|
41
42
|
|
43
|
+
code = File.read(__FILE__)
|
44
|
+
body.write("<h1>Source Code</h1>")
|
45
|
+
body.write("<pre><code>#{Trenni::Markup.escape_string code}</code></pre>")
|
42
46
|
body.write("</body></html>")
|
43
47
|
rescue
|
44
48
|
puts "Remote end closed connection: #{$!}"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'async'
|
4
|
+
require 'async/http/url_endpoint'
|
5
|
+
require 'async/http/client'
|
6
|
+
|
7
|
+
Async do
|
8
|
+
endpoint = Async::HTTP::URLEndpoint.parse("https://localhost:9292")
|
9
|
+
client = Async::HTTP::Client.new(endpoint, Async::HTTP::Protocol::HTTP2::WithPush)
|
10
|
+
|
11
|
+
response = client.get("/index.html")
|
12
|
+
|
13
|
+
puts response.status
|
14
|
+
puts response.read
|
15
|
+
puts
|
16
|
+
|
17
|
+
while promise = response.promises.dequeue
|
18
|
+
promise.wait
|
19
|
+
|
20
|
+
puts "** Promise: #{promise.request.path} **"
|
21
|
+
puts promise.read
|
22
|
+
puts
|
23
|
+
end
|
24
|
+
ensure
|
25
|
+
client.close
|
26
|
+
end
|
27
|
+
|
28
|
+
puts "Done"
|
data/examples/push/config.ru
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
#!/usr/bin/env falcon --verbose serve --concurrency 1 --config
|
1
2
|
|
2
3
|
class EarlyHints
|
3
4
|
def initialize(app)
|
@@ -5,7 +6,12 @@ class EarlyHints
|
|
5
6
|
end
|
6
7
|
|
7
8
|
def call(env)
|
8
|
-
|
9
|
+
path = env['PATH_INFO']
|
10
|
+
early_hints = early_hints = env['rack.early_hints']
|
11
|
+
|
12
|
+
Async.logger.debug("path: #{path} #{early_hints}")
|
13
|
+
|
14
|
+
if path == "/index.html" and early_hints
|
9
15
|
early_hints.push("/style.css")
|
10
16
|
early_hints.push("/script.js")
|
11
17
|
end
|
@@ -15,5 +21,7 @@ class EarlyHints
|
|
15
21
|
end
|
16
22
|
|
17
23
|
use EarlyHints
|
24
|
+
|
18
25
|
use Rack::Static, :urls => [""], :root => __dir__, :index => 'index.html'
|
26
|
+
|
19
27
|
run lambda{|env| [404, [], ["Not Found"]]}
|
data/examples/push/script.js
CHANGED
data/falcon.gemspec
CHANGED
@@ -16,21 +16,22 @@ Gem::Specification.new do |spec|
|
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
17
|
spec.require_paths = ["lib"]
|
18
18
|
|
19
|
-
spec.add_dependency
|
19
|
+
spec.add_dependency "http-protocol", "~> 0.15"
|
20
20
|
|
21
|
-
spec.add_dependency
|
22
|
-
spec.add_dependency
|
23
|
-
spec.add_dependency
|
24
|
-
spec.add_dependency
|
21
|
+
spec.add_dependency "async", "~> 1.13"
|
22
|
+
spec.add_dependency "async-io", "~> 1.20"
|
23
|
+
spec.add_dependency "async-http", "~> 0.38.0"
|
24
|
+
spec.add_dependency "async-container", "~> 0.10.0"
|
25
25
|
|
26
|
-
spec.add_dependency
|
26
|
+
spec.add_dependency "rack", ">= 1.0"
|
27
27
|
|
28
|
-
spec.add_dependency
|
29
|
-
spec.add_dependency
|
28
|
+
spec.add_dependency 'samovar', "~> 1.3"
|
29
|
+
spec.add_dependency 'localhost', "~> 1.1"
|
30
|
+
spec.add_dependency 'build-environment', '~> 1.6'
|
30
31
|
|
32
|
+
spec.add_development_dependency "trenni"
|
31
33
|
spec.add_development_dependency "async-rspec", "~> 1.7"
|
32
34
|
spec.add_development_dependency "async-websocket", "~> 0.6.0"
|
33
|
-
|
34
35
|
spec.add_development_dependency "async-process", "~> 1.1"
|
35
36
|
|
36
37
|
spec.add_development_dependency "covered", "~> 0.10"
|
@@ -21,6 +21,7 @@
|
|
21
21
|
require_relative '../server'
|
22
22
|
require_relative '../endpoint'
|
23
23
|
require_relative '../hosts'
|
24
|
+
require_relative '../configuration'
|
24
25
|
|
25
26
|
require 'async/container'
|
26
27
|
require 'async/container/controller'
|
@@ -40,80 +41,22 @@ module Falcon
|
|
40
41
|
self.description = "Run an HTTP server with one or more virtual hosts."
|
41
42
|
|
42
43
|
options do
|
43
|
-
option '--bind-insecure <address>', "Bind redirection to the given hostname/address", default: "http://
|
44
|
-
option '--bind-secure <address>', "Bind proxy to the given hostname/address", default: "https://
|
45
|
-
|
46
|
-
option '--self-signed', "Use self-signed SSL", default: false
|
47
|
-
end
|
48
|
-
|
49
|
-
many :sites
|
50
|
-
|
51
|
-
CONFIG_RU = "config.ru"
|
52
|
-
|
53
|
-
def load_app(path, verbose)
|
54
|
-
config = File.join(path, CONFIG_RU)
|
55
|
-
|
56
|
-
rack_app, options = Rack::Builder.parse_file(config)
|
57
|
-
|
58
|
-
return Server.middleware(rack_app, verbose: verbose), options
|
44
|
+
option '--bind-insecure <address>', "Bind redirection to the given hostname/address", default: "http://[::]"
|
45
|
+
option '--bind-secure <address>', "Bind proxy to the given hostname/address", default: "https://[::]"
|
59
46
|
end
|
60
47
|
|
61
|
-
|
62
|
-
Async::HTTP::Client.new(client_endpoint)
|
63
|
-
end
|
48
|
+
many :paths
|
64
49
|
|
65
50
|
def run(verbose = false)
|
66
|
-
|
67
|
-
root = Dir.pwd
|
68
|
-
|
69
|
-
sites.each do |path|
|
70
|
-
name = File.basename(path)
|
71
|
-
|
72
|
-
hosts.add(name) do |host|
|
73
|
-
host.app_root = File.expand_path(path, root)
|
74
|
-
|
75
|
-
if @options[:self_signed]
|
76
|
-
host.self_signed!(name)
|
77
|
-
else
|
78
|
-
host.ssl_certificate_path = File.join(path, "ssl", "fullchain.pem")
|
79
|
-
host.ssl_key_path = File.join(path, "ssl", "privkey.pem")
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
controller = Async::Container::Controller.new
|
85
|
-
|
86
|
-
hosts.each do |name, host|
|
87
|
-
if container = host.start
|
88
|
-
controller << container
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
controller << Async::Container::Forked.new do |task|
|
93
|
-
proxy = hosts.proxy
|
94
|
-
secure_endpoint = Async::HTTP::URLEndpoint.parse(@options[:bind_secure], ssl_context: hosts.ssl_context)
|
95
|
-
|
96
|
-
Process.setproctitle("Falcon Proxy")
|
97
|
-
|
98
|
-
proxy_server = Falcon::Server.new(proxy, secure_endpoint)
|
99
|
-
|
100
|
-
proxy_server.run
|
101
|
-
end
|
51
|
+
configuration = Configuration.new(verbose)
|
102
52
|
|
103
|
-
|
104
|
-
|
105
|
-
insecure_endpoint = Async::HTTP::URLEndpoint.parse(@options[:bind_insecure])
|
106
|
-
|
107
|
-
Process.setproctitle("Falcon Redirector")
|
108
|
-
|
109
|
-
redirection_server = Falcon::Server.new(redirection, insecure_endpoint)
|
110
|
-
|
111
|
-
redirection_server.run
|
53
|
+
@paths.each do |path|
|
54
|
+
configuration.load_file(path)
|
112
55
|
end
|
113
56
|
|
114
|
-
|
57
|
+
hosts = Hosts.new(configuration)
|
115
58
|
|
116
|
-
return
|
59
|
+
return hosts.run(@options)
|
117
60
|
end
|
118
61
|
|
119
62
|
def invoke(parent)
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'build/environment'
|
22
|
+
require 'async/io/unix_endpoint'
|
23
|
+
|
24
|
+
module Falcon
|
25
|
+
class ProxyEndpoint < Async::IO::Endpoint
|
26
|
+
def initialize(endpoint, **options)
|
27
|
+
super(**options)
|
28
|
+
|
29
|
+
@endpoint = endpoint
|
30
|
+
end
|
31
|
+
|
32
|
+
attr :endpoint
|
33
|
+
|
34
|
+
def protocol
|
35
|
+
@options[:protocol]
|
36
|
+
end
|
37
|
+
|
38
|
+
def scheme
|
39
|
+
@options[:scheme]
|
40
|
+
end
|
41
|
+
|
42
|
+
def authority
|
43
|
+
@options[:authority]
|
44
|
+
end
|
45
|
+
|
46
|
+
def connect(&block)
|
47
|
+
@endpoint.connect(&block)
|
48
|
+
end
|
49
|
+
|
50
|
+
def bind(&block)
|
51
|
+
@endpoint.bind(&block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def each
|
55
|
+
return to_enum unless block_given?
|
56
|
+
|
57
|
+
@endpoint.each do |endpoint|
|
58
|
+
yield self.class.new(endpoint, @options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.unix(path, **options)
|
63
|
+
self.new(::Async::IO::Endpoint.unix(path), **options)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Configuration
|
68
|
+
def initialize(verbose = false)
|
69
|
+
@environments = {}
|
70
|
+
@verbose = verbose
|
71
|
+
|
72
|
+
add(:ssl) do
|
73
|
+
ssl_session_id {"falcon"}
|
74
|
+
end
|
75
|
+
|
76
|
+
add(:host, :ssl) do
|
77
|
+
ssl_certificate_path {File.expand_path("ssl/certificate.pem", root)}
|
78
|
+
ssl_certificate {OpenSSL::X509::Certificate.new(File.read(ssl_certificate_path))}
|
79
|
+
|
80
|
+
ssl_private_key_path {File.expand_path("ssl/private.key", root)}
|
81
|
+
ssl_private_key {OpenSSL::PKey::RSA.new(File.read(ssl_private_key_path))}
|
82
|
+
|
83
|
+
ssl_context do
|
84
|
+
OpenSSL::SSL::SSLContext.new.tap do |context|
|
85
|
+
context.cert = ssl_certificate
|
86
|
+
context.key = ssl_private_key
|
87
|
+
|
88
|
+
context.session_id_context = ssl_session_id
|
89
|
+
|
90
|
+
context.set_params
|
91
|
+
|
92
|
+
context.setup
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
add(:self_signed, :ssl) do
|
98
|
+
ssl_context do
|
99
|
+
contexts = Localhost::Authority.fetch(authority)
|
100
|
+
|
101
|
+
contexts.server_context.tap do |context|
|
102
|
+
context.alpn_select_cb = lambda do |protocols|
|
103
|
+
if protocols.include? "h2"
|
104
|
+
return "h2"
|
105
|
+
elsif protocols.include? "http/1.1"
|
106
|
+
return "http/1.1"
|
107
|
+
elsif protocols.include? "http/1.0"
|
108
|
+
return "http/1.0"
|
109
|
+
else
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context.session_id_context = "falcon"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
add(:proxy, :host) do
|
120
|
+
endpoint {::Async::HTTP::URLEndpoint.parse(url)}
|
121
|
+
end
|
122
|
+
|
123
|
+
add(:rack, :host) do
|
124
|
+
config_path {::File.expand_path("config.ru", root)}
|
125
|
+
|
126
|
+
middleware do
|
127
|
+
::Falcon::Server.middleware(
|
128
|
+
::Rack::Builder.parse_file(config_path).first, verbose: verbose
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
authority 'localhost'
|
133
|
+
scheme 'https'
|
134
|
+
protocol {::Async::HTTP::Protocol::HTTP2}
|
135
|
+
ipc_path {::File.expand_path("server.ipc", root)}
|
136
|
+
|
137
|
+
endpoint {ProxyEndpoint.unix(ipc_path, protocol: protocol, scheme: scheme, authority: authority)}
|
138
|
+
|
139
|
+
bound_endpoint do
|
140
|
+
Async::Reactor.run do
|
141
|
+
Async::IO::SharedEndpoint.bound(endpoint)
|
142
|
+
end.wait
|
143
|
+
end
|
144
|
+
|
145
|
+
server do
|
146
|
+
::Falcon::Server.new(middleware, bound_endpoint, protocol, scheme)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
attr :environments
|
152
|
+
|
153
|
+
def add(name, *parents, &block)
|
154
|
+
raise KeyError.new("#{name} is already set", key: name) if @environments.key?(name)
|
155
|
+
|
156
|
+
environments = parents.map{|name| @environments.fetch(name)}
|
157
|
+
|
158
|
+
parent = Build::Environment.combine(*environments)
|
159
|
+
|
160
|
+
@environments[name] = Build::Environment.new(parent, name: name, &block)
|
161
|
+
end
|
162
|
+
|
163
|
+
def each
|
164
|
+
return to_enum unless block_given?
|
165
|
+
|
166
|
+
@environments.each do |name, environment|
|
167
|
+
if environment.include?(:authority)
|
168
|
+
yield environment
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def host(name, *parents, &block)
|
174
|
+
add(name, :host, *parents, &block).tap do |environment|
|
175
|
+
environment[:authority] = name
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def proxy(name, *parents, &block)
|
180
|
+
add(name, :proxy, *parents, &block).tap do |environment|
|
181
|
+
environment[:authority] = name
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def rack(name, *parents, &block)
|
186
|
+
add(name, :rack, *parents, &block).tap do |environment|
|
187
|
+
environment[:authority] = name
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def load_file(path)
|
192
|
+
self.instance_eval(File.read(path), path)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
data/lib/falcon/hosts.rb
CHANGED
@@ -23,107 +23,71 @@ require 'async/io/endpoint'
|
|
23
23
|
require_relative 'proxy'
|
24
24
|
require_relative 'redirection'
|
25
25
|
|
26
|
-
require 'async/container
|
26
|
+
require 'async/container'
|
27
|
+
require 'async/container/controller'
|
28
|
+
require 'async/http/url_endpoint'
|
27
29
|
|
28
30
|
module Falcon
|
29
31
|
class Host
|
30
|
-
def initialize
|
31
|
-
@
|
32
|
-
@
|
33
|
-
@config_path = "config.ru"
|
34
|
-
|
35
|
-
@endpoint = nil
|
36
|
-
|
37
|
-
@ssl_certificate = nil
|
38
|
-
@ssl_key = nil
|
39
|
-
|
40
|
-
@ssl_context = nil
|
32
|
+
def initialize(environment)
|
33
|
+
@environment = environment.flatten
|
34
|
+
@evaluator = @environment.evaluator
|
41
35
|
end
|
42
36
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
attr_accessor :endpoint
|
48
|
-
|
49
|
-
attr_accessor :ssl_certificate
|
50
|
-
attr_accessor :ssl_key
|
51
|
-
|
52
|
-
attr_accessor :ssl_context
|
37
|
+
def name
|
38
|
+
"Falcon Host for #{self.authority}"
|
39
|
+
end
|
53
40
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
ssl_context
|
58
|
-
|
59
|
-
super
|
41
|
+
def authority
|
42
|
+
@evaluator.authority
|
60
43
|
end
|
61
44
|
|
62
|
-
def
|
63
|
-
@
|
45
|
+
def endpoint
|
46
|
+
@evaluator.endpoint
|
64
47
|
end
|
65
48
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
if @config_path
|
70
|
-
rack_app, options = Rack::Builder.parse_file(@config_path)
|
71
|
-
|
72
|
-
return Server.middleware(rack_app, verbose: verbose)
|
73
|
-
end
|
49
|
+
def ssl_context
|
50
|
+
@evaluator.ssl_context
|
74
51
|
end
|
75
52
|
|
76
|
-
def
|
77
|
-
|
78
|
-
|
79
|
-
@ssl_context = authority.server_context.tap do |context|
|
80
|
-
context.alpn_select_cb = lambda do |protocols|
|
81
|
-
if protocols.include? "h2"
|
82
|
-
return "h2"
|
83
|
-
elsif protocols.include? "http/1.1"
|
84
|
-
return "http/1.1"
|
85
|
-
elsif protocols.include? "http/1.0"
|
86
|
-
return "http/1.0"
|
87
|
-
else
|
88
|
-
return nil
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
context.session_id_context = "falcon"
|
93
|
-
end
|
53
|
+
def root
|
54
|
+
@evaluator.root
|
94
55
|
end
|
95
56
|
|
96
|
-
def
|
97
|
-
@
|
57
|
+
def bound_endpoint
|
58
|
+
@evaluator.bound_endpoint
|
98
59
|
end
|
99
60
|
|
100
|
-
def
|
101
|
-
@
|
61
|
+
def to_s
|
62
|
+
"\#<#{self.class} #{@evaluator.authority}>"
|
102
63
|
end
|
103
64
|
|
104
|
-
def
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
context.session_id_context = "falcon"
|
110
|
-
|
111
|
-
context.set_params
|
112
|
-
|
113
|
-
context.setup
|
114
|
-
end
|
65
|
+
def assume_privileges(path)
|
66
|
+
stat = File.stat(path)
|
67
|
+
|
68
|
+
Process::GID.change_privilege(stat.gid)
|
69
|
+
Process::UID.change_privilege(stat.uid)
|
115
70
|
end
|
116
71
|
|
117
|
-
def
|
118
|
-
if
|
119
|
-
|
120
|
-
|
72
|
+
def run(container)
|
73
|
+
if @environment.include?(:server)
|
74
|
+
bound_endpoint = self.bound_endpoint
|
75
|
+
|
76
|
+
container.run(count: 1, name: self.name) do |task, instance|
|
77
|
+
Async.logger.info(self) {"Starting application server..."}
|
121
78
|
|
122
|
-
|
79
|
+
if root = self.root
|
80
|
+
Dir.chdir(root)
|
81
|
+
end
|
82
|
+
|
83
|
+
server = @evaluator.server
|
123
84
|
|
124
|
-
|
85
|
+
# Drop root privileges:
|
86
|
+
assume_privileges(root)
|
125
87
|
|
126
88
|
server.run
|
89
|
+
|
90
|
+
task.children.each(&:wait)
|
127
91
|
end
|
128
92
|
end
|
129
93
|
end
|
@@ -132,10 +96,14 @@ module Falcon
|
|
132
96
|
class Hosts
|
133
97
|
DEFAULT_ALPN_PROTOCOLS = ['h2', 'http/1.1'].freeze
|
134
98
|
|
135
|
-
def initialize
|
99
|
+
def initialize(configuration)
|
136
100
|
@named = {}
|
137
101
|
@server_context = nil
|
138
102
|
@server_endpoint = nil
|
103
|
+
|
104
|
+
configuration.each do |environment|
|
105
|
+
add(Host.new(environment))
|
106
|
+
end
|
139
107
|
end
|
140
108
|
|
141
109
|
def each(&block)
|
@@ -157,9 +125,7 @@ module Falcon
|
|
157
125
|
end
|
158
126
|
|
159
127
|
context.session_id_context = "falcon"
|
160
|
-
|
161
128
|
context.alpn_protocols = DEFAULT_ALPN_PROTOCOLS
|
162
|
-
|
163
129
|
context.set_params
|
164
130
|
|
165
131
|
context.setup
|
@@ -168,83 +134,55 @@ module Falcon
|
|
168
134
|
|
169
135
|
def host_context(socket, hostname)
|
170
136
|
if host = @named[hostname]
|
137
|
+
Async.logger.debug(self) {"Resolving #{hostname} -> #{host}"}
|
138
|
+
|
171
139
|
socket.hostname = hostname
|
172
140
|
|
173
141
|
return host.ssl_context
|
142
|
+
else
|
143
|
+
Async.logger.warn(self) {"Unable to resolve #{hostname}!"}
|
144
|
+
|
145
|
+
return nil
|
174
146
|
end
|
175
147
|
end
|
176
148
|
|
177
|
-
def add(
|
178
|
-
host =
|
179
|
-
|
180
|
-
yield host if block_given?
|
181
|
-
|
182
|
-
@named[name] = host.freeze
|
183
|
-
end
|
184
|
-
|
185
|
-
def client_endpoints
|
186
|
-
Hash[
|
187
|
-
@named.collect{|name, host| [name, host.endpoint]}
|
188
|
-
]
|
149
|
+
def add(host)
|
150
|
+
@named[host.authority] = host
|
189
151
|
end
|
190
152
|
|
191
153
|
def proxy
|
192
|
-
Proxy.new(Falcon::BadRequest,
|
154
|
+
Proxy.new(Falcon::BadRequest, @named)
|
193
155
|
end
|
194
156
|
|
195
|
-
def redirection
|
196
|
-
Redirection.new(Falcon::BadRequest,
|
157
|
+
def redirection(secure_endpoint)
|
158
|
+
Redirection.new(Falcon::BadRequest, @named, secure_endpoint)
|
197
159
|
end
|
198
160
|
|
199
|
-
def
|
200
|
-
|
201
|
-
|
202
|
-
controller << container
|
203
|
-
end
|
161
|
+
def run(container = Async::Container::Forked.new, **options)
|
162
|
+
@named.each do |name, host|
|
163
|
+
host.run(container)
|
204
164
|
end
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
controller << Async::Container::Forked.new do |task|
|
212
|
-
Process.setproctitle("Falcon Proxy")
|
165
|
+
|
166
|
+
secure_endpoint = Async::HTTP::URLEndpoint.parse(options[:bind_secure], ssl_context: self.ssl_context)
|
167
|
+
insecure_endpoint = Async::HTTP::URLEndpoint.parse(options[:bind_insecure])
|
168
|
+
|
169
|
+
container.run(count: 1, name: "Falcon Proxy") do |task, instance|
|
170
|
+
proxy = self.proxy
|
213
171
|
|
214
|
-
|
215
|
-
proxy,
|
216
|
-
Async::HTTP::URLEndpoint.parse(
|
217
|
-
'https://0.0.0.0',
|
218
|
-
reuse_address: true,
|
219
|
-
ssl_context: hosts.ssl_context
|
220
|
-
)
|
221
|
-
)
|
172
|
+
proxy_server = Falcon::Server.new(proxy, secure_endpoint)
|
222
173
|
|
223
|
-
|
224
|
-
task.async do
|
225
|
-
debug_trap.install!
|
226
|
-
$stderr.puts "Send `kill -USR1 #{Process.pid}` for detailed status :)"
|
227
|
-
|
228
|
-
debug_trap.trap do
|
229
|
-
task.reactor.print_hierarchy($stderr)
|
230
|
-
# Async.logger.level = Logger::DEBUG
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
task.async do |task|
|
235
|
-
start_time = Async::Clock.now
|
236
|
-
|
237
|
-
while true
|
238
|
-
task.sleep(600)
|
239
|
-
duration = Async::Clock.now - start_time
|
240
|
-
puts "Handled #{proxy.count} requests; #{(proxy.count.to_f / duration.to_f).round(1)} requests per second."
|
241
|
-
end
|
242
|
-
end
|
243
|
-
|
244
|
-
$stderr.puts "Starting server"
|
245
|
-
server.run
|
246
|
-
end
|
174
|
+
proxy_server.run
|
247
175
|
end
|
176
|
+
|
177
|
+
container.run(count: 1, name: "Falcon Redirector") do |task, instance|
|
178
|
+
redirection = self.redirection(secure_endpoint)
|
179
|
+
|
180
|
+
redirection_server = Falcon::Server.new(redirection, insecure_endpoint)
|
181
|
+
|
182
|
+
redirection_server.run
|
183
|
+
end
|
184
|
+
|
185
|
+
return container
|
248
186
|
end
|
249
187
|
end
|
250
188
|
end
|
data/lib/falcon/proxy.rb
CHANGED
@@ -86,9 +86,18 @@ module Falcon
|
|
86
86
|
headers.slice!(HOP_HEADERS)
|
87
87
|
end
|
88
88
|
|
89
|
-
def prepare_request(request)
|
89
|
+
def prepare_request(request, host)
|
90
90
|
forwarded = []
|
91
91
|
|
92
|
+
# Async.logger.info(self) do |buffer|
|
93
|
+
# buffer.puts "Request authority: #{request.authority}"
|
94
|
+
# buffer.puts "Host authority: #{host.authority}"
|
95
|
+
# buffer.puts "Endpoint authority: #{host.endpoint.authority}"
|
96
|
+
# end
|
97
|
+
|
98
|
+
# The authority of the request must match the authority of the endpoint we are proxying to, otherwise SNI and other things won't work correctly.
|
99
|
+
request.authority = host.endpoint.authority
|
100
|
+
|
92
101
|
if address = request.remote_address
|
93
102
|
request.headers.add(X_FORWARDED_FOR, address.ip_address)
|
94
103
|
forwarded << "for=#{address.ip_address}"
|
@@ -111,12 +120,12 @@ module Falcon
|
|
111
120
|
end
|
112
121
|
|
113
122
|
def call(request)
|
114
|
-
if
|
123
|
+
if host = lookup(request)
|
115
124
|
@count += 1
|
116
125
|
|
117
|
-
request = self.prepare_request(request)
|
126
|
+
request = self.prepare_request(request, host)
|
118
127
|
|
119
|
-
client = connect(endpoint)
|
128
|
+
client = connect(host.endpoint)
|
120
129
|
|
121
130
|
client.call(request)
|
122
131
|
else
|
data/lib/falcon/redirection.rb
CHANGED
@@ -32,10 +32,11 @@ module Falcon
|
|
32
32
|
end
|
33
33
|
|
34
34
|
class Redirection < Async::HTTP::Middleware
|
35
|
-
def initialize(app, hosts)
|
35
|
+
def initialize(app, hosts, endpoint)
|
36
36
|
super(app)
|
37
37
|
|
38
38
|
@hosts = hosts
|
39
|
+
@endpoint = endpoint
|
39
40
|
end
|
40
41
|
|
41
42
|
def lookup(request)
|
@@ -46,10 +47,14 @@ module Falcon
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def call(request)
|
49
|
-
if
|
50
|
-
|
50
|
+
if host = lookup(request)
|
51
|
+
if @endpoint.default_port?
|
52
|
+
location = "#{@endpoint.scheme}://#{host.authority}#{request.path}"
|
53
|
+
else
|
54
|
+
location = "#{@endpoint.scheme}://#{host.authority}:#{@endpoint.port}#{request.path}"
|
55
|
+
end
|
51
56
|
|
52
|
-
return Async::HTTP::Response[301,
|
57
|
+
return Async::HTTP::Response[301, [['location', location]], []]
|
53
58
|
else
|
54
59
|
super
|
55
60
|
end
|
data/lib/falcon/version.rb
CHANGED
data/server.rb
CHANGED
@@ -1,36 +1,20 @@
|
|
1
|
-
#!/usr/bin/env
|
1
|
+
#!/usr/bin/env -S ./bin/falcon virtual --bind-insecure http://[::]:1080 --bind-secure https://[::]:1443
|
2
2
|
|
3
|
-
|
3
|
+
# You will want edit your `/etc/hosts`, adding the following:
|
4
|
+
# 127.0.0.1 benchmark.localhost beer.localhost hello.localhost
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
require 'async/container/controller'
|
8
|
-
require 'async/container/forked'
|
9
|
-
|
10
|
-
require 'async/clock'
|
11
|
-
require 'ruby-prof'
|
12
|
-
|
13
|
-
Async.logger.level = Logger::INFO
|
14
|
-
|
15
|
-
hosts = Falcon::Hosts.new
|
16
|
-
|
17
|
-
hosts.add('mc.oriontransfer.co.nz') do |host|
|
18
|
-
host.endpoint = Async::HTTP::URLEndpoint.parse('http://hana.local:8123')
|
19
|
-
|
20
|
-
host.ssl_certificate_path = '/etc/letsencrypt/live/mc.oriontransfer.co.nz/fullchain.pem'
|
21
|
-
host.ssl_key_path = '/etc/letsencrypt/live/mc.oriontransfer.co.nz/privkey.pem'
|
6
|
+
rack 'benchmark.localhost', :self_signed do
|
7
|
+
root File.expand_path("examples/benchmark/", __dir__)
|
22
8
|
end
|
23
9
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
host.ssl_certificate_path = '/etc/letsencrypt/live/chick.nz/fullchain.pem'
|
28
|
-
host.ssl_key_path = '/etc/letsencrypt/live/chick.nz/privkey.pem'
|
10
|
+
rack 'beer.localhost', :self_signed do
|
11
|
+
root File.expand_path("examples/beer/", __dir__)
|
29
12
|
end
|
30
13
|
|
31
|
-
|
32
|
-
|
33
|
-
|
14
|
+
rack 'hello.localhost', :self_signed do
|
15
|
+
root File.expand_path("examples/hello/", __dir__)
|
16
|
+
end
|
34
17
|
|
35
|
-
|
36
|
-
|
18
|
+
proxy 'codeotaku.localhost', :self_signed do
|
19
|
+
url 'https://www.codeotaku.com'
|
20
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: falcon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.26.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: http-protocol
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
47
|
+
version: '1.20'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
54
|
+
version: '1.20'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: async-http
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +122,34 @@ dependencies:
|
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '1.1'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: build-environment
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '1.6'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '1.6'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: trenni
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
125
153
|
- !ruby/object:Gem::Dependency
|
126
154
|
name: async-rspec
|
127
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -239,6 +267,7 @@ files:
|
|
239
267
|
- examples/beer/config.ru
|
240
268
|
- examples/benchmark/config.ru
|
241
269
|
- examples/hello/config.ru
|
270
|
+
- examples/push/client.rb
|
242
271
|
- examples/push/config.ru
|
243
272
|
- examples/push/index.html
|
244
273
|
- examples/push/script.js
|
@@ -261,6 +290,7 @@ files:
|
|
261
290
|
- lib/falcon/command.rb
|
262
291
|
- lib/falcon/command/serve.rb
|
263
292
|
- lib/falcon/command/virtual.rb
|
293
|
+
- lib/falcon/configuration.rb
|
264
294
|
- lib/falcon/endpoint.rb
|
265
295
|
- lib/falcon/hosts.rb
|
266
296
|
- lib/falcon/proxy.rb
|