Skip to content

Commit d6a3fbb

Browse files
authored
feat: Support for Puma 6 and Rack 3 (#152)
1 parent 31d1790 commit d6a3fbb

File tree

6 files changed

+93
-23
lines changed

6 files changed

+93
-23
lines changed

.toys/ci.rb

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
desc "Run CI checks"
1616

17-
TESTS = ["unit", "rubocop", "yardoc", "build", "examples", "conformance"]
17+
TESTS = ["unit", "dependencies", "rubocop", "yardoc", "build", "examples", "conformance"]
1818

1919
flag :only
2020
TESTS.each do |name|
@@ -26,26 +26,68 @@
2626

2727
def handle_result result
2828
if result.success?
29-
puts "** #{result.name} passed\n\n", :green, :bold
29+
puts "** Passed: #{result.name}\n\n", :green, :bold
3030
else
31-
puts "** CI terminated: #{result.name} failed!", :red, :bold
32-
exit 1
31+
puts "** Failed: #{result.name}\n\n", :red, :bold
32+
@errors << result.name
3333
end
3434
end
3535

3636
def run
37+
@errors = []
3738
::Dir.chdir context_directory
3839
TESTS.each do |name|
3940
key = "test_#{name}".to_sym
4041
set key, !only if get(key).nil?
4142
end
42-
exec ["toys", "test"], name: "Unit tests" if test_unit
43-
exec ["toys", "rubocop"], name: "Style checker" if test_rubocop
44-
exec ["toys", "yardoc"], name: "Docs generation" if test_yardoc
45-
exec ["toys", "build"], name: "Gem build" if test_build
43+
exec_separate_tool ["test"], name: "Unit tests" if test_unit
44+
exec_separate_tool ["ci", "deps-matrix"], name: "Dependency matrix tests" if test_dependencies
45+
exec_separate_tool ["rubocop"], name: "Style checker" if test_rubocop
46+
exec_separate_tool ["yardoc"], name: "Docs generation" if test_yardoc
47+
exec_separate_tool ["build"], name: "Gem build" if test_build
4648
::Dir.foreach "examples" do |dir|
4749
next if dir =~ /^\.+$/
48-
exec ["toys", "test"], name: "Tests for #{dir} example", chdir: ::File.join("examples", dir)
50+
exec_separate_tool ["test"], name: "Tests for #{dir} example", chdir: ::File.join("examples", dir)
4951
end if test_examples
50-
exec ["toys", "conformance"], name: "Conformance tests" if test_conformance
52+
exec_separate_tool ["conformance"], name: "Conformance tests" if test_conformance
53+
@errors.each do |err|
54+
puts "Failed: #{err}", :red, :bold
55+
end
56+
exit 1 unless @errors.empty?
57+
end
58+
59+
tool "deps-matrix" do
60+
static :puma_versions, ["4.0", "5.0", "6.0"]
61+
static :rack_versions, ["2.1", "3.0"]
62+
63+
include :exec, result_callback: :handle_result
64+
include :terminal
65+
66+
def handle_result result
67+
if result.success?
68+
puts "** Passed: #{result.name}\n\n", :green, :bold
69+
else
70+
puts "** Failed: #{result.name}\n\n", :red, :bold
71+
@errors << result.name
72+
end
73+
end
74+
75+
def run
76+
@errors = []
77+
::Dir.chdir context_directory
78+
puma_versions.each do |puma_version|
79+
rack_versions.each do |rack_version|
80+
name = "Puma #{puma_version} / Rack #{rack_version}"
81+
env = {
82+
"FF_DEPENDENCY_TEST_PUMA" => "~> #{puma_version}",
83+
"FF_DEPENDENCY_TEST_RACK" => "~> #{rack_version}",
84+
}
85+
exec_separate_tool ["test", "test/test_server.rb"], env: env, name: name
86+
end
87+
end
88+
@errors.each do |err|
89+
puts "Failed: #{err}", :red, :bold
90+
end
91+
exit 1 unless @errors.empty?
92+
end
5193
end

Gemfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ source "https://rubygems.org"
1616

1717
gemspec
1818

19-
gem "google-style", "~> 1.26.1"
19+
gem "google-style", "~> 1.26.3"
2020
gem "minitest", "~> 5.16"
2121
gem "minitest-focus", "~> 1.2"
2222
gem "minitest-rg", "~> 5.2"
23+
gem "puma", ENV["FF_DEPENDENCY_TEST_PUMA"] if ENV["FF_DEPENDENCY_TEST_PUMA"]
24+
gem "rack", ENV["FF_DEPENDENCY_TEST_RACK"] if ENV["FF_DEPENDENCY_TEST_RACK"]
2325
gem "redcarpet", "~> 3.5" unless ::RUBY_PLATFORM == "java"
2426
gem "yard", "~> 0.9.25"

functions_framework.gemspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ version = ::FunctionsFramework::VERSION
4646

4747
spec.required_ruby_version = ">= 2.6.0"
4848
spec.add_dependency "cloud_events", ">= 0.7.0", "< 2.a"
49-
spec.add_dependency "puma", ">= 4.3.0", "< 6.a"
50-
spec.add_dependency "rack", "~> 2.1"
49+
spec.add_dependency "puma", ">= 4.3.0", "< 7.a"
50+
spec.add_dependency "rack", ">= 2.1", "< 4.a"
5151

5252
if spec.respond_to? :metadata
5353
spec.metadata["changelog_uri"] =

lib/functions_framework/server.rb

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,21 @@ def initialize function, globals
8282
def start
8383
synchronize do
8484
unless running?
85-
@server = ::Puma::Server.new @app
86-
@server.min_threads = @config.min_threads
87-
@server.max_threads = @config.max_threads
88-
@server.leak_stack_on_error = @config.show_error_details?
85+
# Puma >= 6.0 interprets these settings from options
86+
options = {
87+
min_threads: @config.min_threads,
88+
max_threads: @config.max_threads,
89+
environment: @config.show_error_details? ? "development" : "production"
90+
}
91+
# Puma::Events.stdio for Puma < 6.0; otherwise nil for Puma >= 6.0
92+
events = ::Puma::Events.stdio if ::Puma::Events.respond_to? :stdio
93+
@server = ::Puma::Server.new @app, events, options
94+
if @server.respond_to? :min_threads=
95+
# Puma < 6.0 sets server attributes for these settings
96+
@server.min_threads = @config.min_threads
97+
@server.max_threads = @config.max_threads
98+
@server.leak_stack_on_error = @config.show_error_details?
99+
end
89100
@server.binder.add_tcp_listener @config.bind_addr, @config.port
90101
@config.logger.info "FunctionsFramework: Serving function #{@function.name.inspect} " \
91102
"on port #{@config.port}..."
@@ -377,8 +388,8 @@ def string_response string, status, content_type: nil
377388
content_type = "#{content_type}; charset=#{string.encoding.name.downcase}"
378389
end
379390
headers = {
380-
"Content-Type" => content_type,
381-
"Content-Length" => string.bytesize
391+
"content-type" => content_type,
392+
"content-length" => string.bytesize
382393
}
383394
[status, headers, [string]]
384395
end

lib/functions_framework/testing.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,8 @@ def build_standard_env url, headers
367367
::Rack::QUERY_STRING => url.query,
368368
::Rack::SERVER_NAME => url.host,
369369
::Rack::SERVER_PORT => url.port,
370+
::Rack::SERVER_PROTOCOL => "HTTP/1.1",
370371
::Rack::RACK_URL_SCHEME => url.scheme,
371-
::Rack::RACK_VERSION => ::Rack::VERSION,
372372
::Rack::RACK_LOGGER => ::FunctionsFramework.logger,
373373
::Rack::RACK_INPUT => ::StringIO.new,
374374
::Rack::RACK_ERRORS => ::StringIO.new

test/test_server.rb

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@
4343
let(:retry_interval) { 0.5 }
4444
let(:app_context) { {} }
4545

46-
def make_basic_server function
46+
def make_basic_server function, show_error_details: true
4747
FunctionsFramework::Server.new function, app_context do |config|
4848
config.min_threads = 1
4949
config.max_threads = 1
5050
config.port = port
5151
config.bind_addr = "127.0.0.1"
5252
config.rack_env = "development"
5353
config.logger = quiet_logger
54-
config.show_error_details = true
54+
config.show_error_details = show_error_details
5555
end
5656
end
5757

@@ -235,7 +235,7 @@ def query_server_with_retry server
235235
assert_match(/あああ/, err)
236236
end
237237

238-
it "interprets exceptions" do
238+
it "interprets exceptions showing error details" do
239239
function = FunctionsFramework::Function.new "my-func", :http do |_request|
240240
raise "Whoops!"
241241
end
@@ -245,6 +245,21 @@ def query_server_with_retry server
245245
end
246246
assert_equal "500", response.code
247247
assert_match(/RuntimeError: Whoops!\n\t#{__FILE__}/, response.body)
248+
assert_match %r{test/test_server\.rb:}, response.body
249+
assert_equal "text/plain; charset=utf-8", response["Content-Type"]
250+
end
251+
252+
it "interprets exceptions hiding error details" do
253+
function = FunctionsFramework::Function.new "my-func", :http do |_request|
254+
raise "Whoops!"
255+
end
256+
server = make_basic_server function, show_error_details: false
257+
response = query_server_with_retry server do
258+
::Net::HTTP.get_response URI("#{server_url}/")
259+
end
260+
assert_equal "500", response.code
261+
refute_match(/Whoops/, response.body)
262+
assert_equal "Unexpected internal error", response.body
248263
assert_equal "text/plain; charset=utf-8", response["Content-Type"]
249264
end
250265

0 commit comments

Comments
 (0)