falcon 0.36.4 → 0.37.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/falcon/adapters/input.rb +32 -11
- data/lib/falcon/adapters/output.rb +20 -1
- data/lib/falcon/adapters/rack.rb +60 -39
- data/lib/falcon/adapters/response.rb +23 -1
- data/lib/falcon/adapters/rewindable.rb +10 -3
- data/lib/falcon/command.rb +2 -0
- data/lib/falcon/command/host.rb +22 -3
- data/lib/falcon/command/paths.rb +4 -0
- data/lib/falcon/command/proxy.rb +14 -0
- data/lib/falcon/command/redirect.rb +12 -0
- data/lib/falcon/command/serve.rb +27 -16
- data/lib/falcon/command/supervisor.rb +15 -1
- data/lib/falcon/command/top.rb +16 -0
- data/lib/falcon/command/virtual.rb +15 -0
- data/lib/falcon/configuration.rb +69 -7
- data/lib/falcon/controller/host.rb +12 -0
- data/lib/falcon/controller/proxy.rb +13 -0
- data/lib/falcon/controller/redirect.rb +7 -0
- data/lib/falcon/controller/serve.rb +17 -2
- data/lib/falcon/controller/virtual.rb +17 -0
- data/lib/falcon/endpoint.rb +8 -0
- data/lib/falcon/{configuration/proxy.rb → environments.rb} +9 -5
- data/lib/falcon/environments/application.rb +72 -0
- data/lib/falcon/{configuration/application.rb → environments/lets_encrypt_tls.rb} +21 -20
- data/lib/falcon/{configuration/lets_encrypt_tls.rb → environments/proxy.rb} +13 -6
- data/lib/falcon/{configuration → environments}/rack.rb +14 -2
- data/lib/falcon/{configuration → environments}/self_signed_tls.rb +9 -1
- data/lib/falcon/{configuration → environments}/supervisor.rb +19 -5
- data/lib/falcon/{configuration → environments}/tls.rb +39 -5
- data/lib/falcon/extensions/openssl.rb +1 -0
- data/lib/falcon/middleware/proxy.rb +26 -5
- data/lib/falcon/middleware/redirect.rb +11 -0
- data/lib/falcon/{verbose.rb → middleware/verbose.rb} +34 -26
- data/lib/falcon/proxy_endpoint.rb +21 -0
- data/lib/falcon/server.rb +8 -2
- data/lib/falcon/service/application.rb +24 -2
- data/lib/falcon/service/generic.rb +18 -0
- data/lib/falcon/service/proxy.rb +6 -0
- data/lib/falcon/service/supervisor.rb +13 -1
- data/lib/falcon/services.rb +21 -0
- data/lib/falcon/tls.rb +4 -2
- data/lib/falcon/version.rb +1 -1
- data/lib/rack/handler/falcon.rb +8 -2
- metadata +64 -121
- data/.editorconfig +0 -5
- data/.github/FUNDING.yml +0 -3
- data/.github/workflows/development.yml +0 -60
- data/.gitignore +0 -14
- data/.rspec +0 -3
- data/Gemfile +0 -17
- data/README.md +0 -316
- data/examples/beer/config.ru +0 -57
- data/examples/beer/falcon.rb +0 -8
- data/examples/benchmark/config.ru +0 -39
- data/examples/benchmark/falcon.rb +0 -6
- data/examples/csv/config.ru +0 -31
- data/examples/google/falcon.rb +0 -14
- data/examples/hello/config.ru +0 -22
- data/examples/hello/falcon.rb +0 -24
- data/examples/hello/preload.rb +0 -7
- data/examples/internet/config.ru +0 -54
- data/examples/memory/allocations.rb +0 -39
- data/examples/memory/config.ru +0 -14
- data/examples/push/client.rb +0 -29
- data/examples/push/config.ru +0 -28
- data/examples/push/index.html +0 -14
- data/examples/push/script.js +0 -2
- data/examples/push/style.css +0 -4
- data/examples/redis/Gemfile +0 -9
- data/examples/redis/config.ru +0 -28
- data/examples/sequel/Gemfile +0 -4
- data/examples/sequel/config.ru +0 -8
- data/examples/sequel/data.sqlite3 +0 -0
- data/examples/server/standalone.rb +0 -27
- data/examples/sinatra/Gemfile +0 -7
- data/examples/sinatra/Gemfile.lock +0 -53
- data/examples/sinatra/config.ru +0 -16
- data/examples/trailers/config.ru +0 -34
- data/examples/trailers/falcon.rb +0 -8
- data/falcon.gemspec +0 -45
- data/gems/rack1.gemfile +0 -4
- data/gems/rack3.gemfile +0 -4
- data/lib/falcon/adapters/early_hints.rb +0 -49
- data/logo-square.afdesign +0 -0
- data/logo.afdesign +0 -0
- data/logo.svg +0 -107
- data/server.rb +0 -21
- data/tasks/benchmark.rake +0 -103
data/examples/beer/falcon.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
#!/usr/bin/env falcon --verbose serve -c
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
class Benchmark
|
5
|
-
def initialize(app)
|
6
|
-
@app = app
|
7
|
-
end
|
8
|
-
|
9
|
-
PATH_INFO = 'PATH_INFO'.freeze
|
10
|
-
|
11
|
-
SMALL = [200, {}, ["Hello World\n" * 10] * 10].freeze
|
12
|
-
|
13
|
-
def small(env)
|
14
|
-
SMALL
|
15
|
-
end
|
16
|
-
|
17
|
-
BIG = [200, {}, ["Hello World\n" * 1000] * 1000].freeze
|
18
|
-
|
19
|
-
def big(env)
|
20
|
-
BIG
|
21
|
-
end
|
22
|
-
|
23
|
-
def call(env)
|
24
|
-
_, name, *path = env[PATH_INFO].split("/")
|
25
|
-
|
26
|
-
method = name&.to_sym
|
27
|
-
|
28
|
-
if method and self.respond_to?(method)
|
29
|
-
self.send(method, env)
|
30
|
-
else
|
31
|
-
@app.call(env)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
use Benchmark
|
37
|
-
|
38
|
-
run lambda {|env| [200, {}, ["Hello World"]]}
|
39
|
-
|
data/examples/csv/config.ru
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
#!/usr/bin/env falcon --verbose serve -c
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
class MyApp
|
5
|
-
def initialize(app)
|
6
|
-
@app = app
|
7
|
-
|
8
|
-
@words = File.readlines('/usr/share/dict/words', chomp: true).each_slice(3).to_a
|
9
|
-
end
|
10
|
-
|
11
|
-
def call(env)
|
12
|
-
body = Async::HTTP::Body::Writable.new(queue: Async::LimitedQueue.new(8))
|
13
|
-
|
14
|
-
Async do |task|
|
15
|
-
@words.each do |words|
|
16
|
-
Async.logger.debug("Sending #{words.inspect}")
|
17
|
-
body.write(words.join(",") + "\n")
|
18
|
-
task.sleep(1)
|
19
|
-
end
|
20
|
-
ensure
|
21
|
-
body.close($!)
|
22
|
-
end
|
23
|
-
|
24
|
-
return [200, [], body]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# Build the middleware stack:
|
29
|
-
use MyApp
|
30
|
-
|
31
|
-
run lambda {|env| [404, {}, []]}
|
data/examples/google/falcon.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env falcon-host
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
load :proxy, :self_signed_tls, :supervisor
|
5
|
-
|
6
|
-
supervisor
|
7
|
-
|
8
|
-
proxy "google.localhost", :self_signed_tls do
|
9
|
-
url 'https://www.google.com'
|
10
|
-
end
|
11
|
-
|
12
|
-
proxy "codeotaku.localhost", :self_signed_tls do
|
13
|
-
url 'https://www.codeotaku.com'
|
14
|
-
end
|
data/examples/hello/config.ru
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
#!/usr/bin/env falcon --verbose serve -c
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require 'async'
|
5
|
-
|
6
|
-
class RequestLogger
|
7
|
-
def initialize(app)
|
8
|
-
@app = app
|
9
|
-
end
|
10
|
-
|
11
|
-
def call(env)
|
12
|
-
logger = Async.logger.with(level: :debug)
|
13
|
-
|
14
|
-
Async(logger: logger) do
|
15
|
-
@app.call(env)
|
16
|
-
end.wait
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
# use RequestLogger
|
21
|
-
|
22
|
-
run lambda {|env| [200, {'cache-control' => 'max-age=10, public'}, ["Hello World"]]}
|
data/examples/hello/falcon.rb
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
#!/usr/bin/env falcon-host
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
load :rack, :self_signed_tls, :supervisor
|
5
|
-
|
6
|
-
supervisor
|
7
|
-
|
8
|
-
rack 'hello.localhost', :self_signed_tls do
|
9
|
-
# scheme 'http'
|
10
|
-
# protocol {Async::HTTP::Protocol::HTTP1}
|
11
|
-
#
|
12
|
-
# endpoint do
|
13
|
-
# Async::HTTP::Endpoint.for(scheme, "localhost", port: 9292, protocol: protocol)
|
14
|
-
# end
|
15
|
-
|
16
|
-
append preload "preload.rb"
|
17
|
-
|
18
|
-
# Process will connect to supervisor to report statistics periodically, otherwise it would be killed.
|
19
|
-
# report :supervisor
|
20
|
-
end
|
21
|
-
|
22
|
-
# service 'jobs' do
|
23
|
-
# shell ['rake', 'background:jobs:process']
|
24
|
-
# end
|
data/examples/hello/preload.rb
DELETED
data/examples/internet/config.ru
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'async'
|
4
|
-
require 'async/http/internet'
|
5
|
-
|
6
|
-
# Experimental.
|
7
|
-
require 'kernel/sync'
|
8
|
-
|
9
|
-
class Search
|
10
|
-
def initialize(app)
|
11
|
-
@internet = Async::HTTP::Internet.new
|
12
|
-
|
13
|
-
@app = app
|
14
|
-
end
|
15
|
-
|
16
|
-
# This method uses the `Async` method to create a reactor if required, and then executes the contained code without waiting for the result. So even if the search query takes a long time (e.g. 100ms), it won't hold up the request processing.
|
17
|
-
def async(close: !Async::Task.current?)
|
18
|
-
Async do
|
19
|
-
response = @internet.get("https://google.com/search?q=async+ruby")
|
20
|
-
|
21
|
-
puts response.inspect
|
22
|
-
ensure
|
23
|
-
response&.finish
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# This method uses the experimental `Sync` method to create a reactor if required. If the code is already running in a reactor, it runs synchronously, otherwise it's effectively the same as `Async` and a blocking operation. This allow you to write efficient non-blocking code that works in both traditional web application servers, but gains additional scalability in `Async`-aware servers like Falcon.
|
28
|
-
# You can achieve a similar result by calling `Async{}.wait`, but this is less efficient as it will allocate a sub-task even thought it's not needed.
|
29
|
-
def sync(close: !Async::Task.current?)
|
30
|
-
Sync do
|
31
|
-
response = @internet.get("https://google.com/search?q=async+ruby")
|
32
|
-
|
33
|
-
puts response.inspect
|
34
|
-
ensure
|
35
|
-
response&.finish
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
# The only point of this is to invoke one of the above two methods.
|
40
|
-
def call(env)
|
41
|
-
case env['PATH_INFO']
|
42
|
-
when '/sync'
|
43
|
-
self.sync
|
44
|
-
when '/async'
|
45
|
-
self.async
|
46
|
-
end
|
47
|
-
|
48
|
-
return @app.call(env)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
use Search
|
53
|
-
|
54
|
-
run lambda{|env| [404, [], []]}
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class Allocations
|
4
|
-
def initialize(app)
|
5
|
-
@app = app
|
6
|
-
end
|
7
|
-
|
8
|
-
def allocations
|
9
|
-
counts = Hash.new{|h,k| h[k] = 0}
|
10
|
-
|
11
|
-
ObjectSpace.each_object do |object|
|
12
|
-
counts[object.class] += 1
|
13
|
-
end
|
14
|
-
|
15
|
-
return counts
|
16
|
-
end
|
17
|
-
|
18
|
-
def print_allocations(minimum = 100)
|
19
|
-
buffer = StringIO.new
|
20
|
-
|
21
|
-
total = allocations.values.sum
|
22
|
-
|
23
|
-
allocations.select{|k,v| v >= minimum}.sort_by{|k,v| -v}.each do |key, value|
|
24
|
-
buffer.puts "#{key}: #{value} allocations"
|
25
|
-
end
|
26
|
-
|
27
|
-
buffer.puts "** Total: #{total} allocations."
|
28
|
-
|
29
|
-
return buffer.string
|
30
|
-
end
|
31
|
-
|
32
|
-
def call(env)
|
33
|
-
if env["PATH_INFO"] == "/allocations"
|
34
|
-
return [200, [], [print_allocations]]
|
35
|
-
else
|
36
|
-
return @app.call(env)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
data/examples/memory/config.ru
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'allocations'
|
4
|
-
|
5
|
-
use Allocations
|
6
|
-
|
7
|
-
run lambda{|env| [404, [], []]}
|
8
|
-
|
9
|
-
# % curl --insecure https://localhost:9292/allocations
|
10
|
-
# String: 32179 allocations
|
11
|
-
# Array: 10228 allocations
|
12
|
-
# Hash: 1299 allocations
|
13
|
-
# Class: 1118 allocations
|
14
|
-
# ** Total: 50162 allocations.
|
data/examples/push/client.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require 'async'
|
5
|
-
require 'async/http/endpoint'
|
6
|
-
require 'async/http/client'
|
7
|
-
|
8
|
-
Async do
|
9
|
-
endpoint = Async::HTTP::Endpoint.parse("https://localhost:9292")
|
10
|
-
client = Async::HTTP::Client.new(endpoint, Async::HTTP::Protocol::HTTP2::WithPush)
|
11
|
-
|
12
|
-
response = client.get("/index.html")
|
13
|
-
|
14
|
-
puts response.status
|
15
|
-
puts response.read
|
16
|
-
puts
|
17
|
-
|
18
|
-
while promise = response.promises.dequeue
|
19
|
-
promise.wait
|
20
|
-
|
21
|
-
puts "** Promise: #{promise.request.path} **"
|
22
|
-
puts promise.read
|
23
|
-
puts
|
24
|
-
end
|
25
|
-
ensure
|
26
|
-
client.close
|
27
|
-
end
|
28
|
-
|
29
|
-
puts "Done"
|
data/examples/push/config.ru
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
#!/usr/bin/env falcon --verbose serve --concurrency 1 --config
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
class EarlyHints
|
5
|
-
def initialize(app)
|
6
|
-
@app = app
|
7
|
-
end
|
8
|
-
|
9
|
-
def call(env)
|
10
|
-
path = env['PATH_INFO']
|
11
|
-
early_hints = early_hints = env['rack.early_hints']
|
12
|
-
|
13
|
-
Async.logger.debug("path: #{path} #{early_hints}")
|
14
|
-
|
15
|
-
if path == "/index.html" and early_hints
|
16
|
-
early_hints.push("/style.css")
|
17
|
-
early_hints.push("/script.js")
|
18
|
-
end
|
19
|
-
|
20
|
-
@app.call(env)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
use EarlyHints
|
25
|
-
|
26
|
-
use Rack::Static, :urls => [""], :root => __dir__, :index => 'index.html'
|
27
|
-
|
28
|
-
run lambda{|env| [404, [], ["Not Found"]]}
|
data/examples/push/index.html
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
<!doctype HTML>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>Hello World</title>
|
5
|
-
|
6
|
-
<link rel="stylesheet" href="/style.css" />
|
7
|
-
<script type="text/javascript" src="script.js"></script>
|
8
|
-
</head>
|
9
|
-
<body>
|
10
|
-
<h1>Early Hints</h1>
|
11
|
-
|
12
|
-
<p>Check the developer tools to see if early hints worked!</p>
|
13
|
-
</body>
|
14
|
-
</html>
|
data/examples/push/script.js
DELETED
data/examples/push/style.css
DELETED
data/examples/redis/Gemfile
DELETED
data/examples/redis/config.ru
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'sinatra/base'
|
4
|
-
require 'async/redis'
|
5
|
-
require 'async/clock'
|
6
|
-
|
7
|
-
CLIENT = Async::Redis::Client.new(Async::Redis.local_endpoint)
|
8
|
-
|
9
|
-
class MyApp < Sinatra::Base
|
10
|
-
get "/" do
|
11
|
-
puts "Starting BLPOP SLEEP..."
|
12
|
-
duration = Async::Clock.measure do
|
13
|
-
CLIENT.call "BLPOP", "SLEEP", 1
|
14
|
-
end
|
15
|
-
puts "Finished BLPOP SLEEP after #{duration.round(2)}s"
|
16
|
-
|
17
|
-
"ok"
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
use MyApp # Then, it will get to Sinatra.
|
22
|
-
run lambda {|env| [404, {}, []]} # Bottom of the stack, give 404.
|
23
|
-
|
24
|
-
# Start server like this:
|
25
|
-
# falcon --verbose serve --threaded --count 1 --bind http://localhost:9292
|
26
|
-
|
27
|
-
# Test server, e.g.:
|
28
|
-
# time ab -n 64 -c 64 http://localhost:9292/
|
data/examples/sequel/Gemfile
DELETED
data/examples/sequel/config.ru
DELETED
Binary file
|
@@ -1,27 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'async'
|
4
|
-
require 'async/http/endpoint'
|
5
|
-
require 'async/websocket/adapters/rack'
|
6
|
-
|
7
|
-
require 'falcon'
|
8
|
-
|
9
|
-
module WebSocketApp
|
10
|
-
def self.call(env)
|
11
|
-
Async::WebSocket::Adapters::Rack.open(env, protocols: %w[ws]) do |connection|
|
12
|
-
while (message = connection.read)
|
13
|
-
pp message
|
14
|
-
end
|
15
|
-
end or [200, [], ["Websocket only."]]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
Async do
|
20
|
-
websocket_endpoint = Async::HTTP::Endpoint.parse('http://127.0.0.1:3000')
|
21
|
-
|
22
|
-
app = Falcon::Server.middleware(WebSocketApp)
|
23
|
-
|
24
|
-
server = Falcon::Server.new(app, websocket_endpoint)
|
25
|
-
|
26
|
-
server.run.each(&:wait)
|
27
|
-
end
|