Skip to content

Support Rack 3.x #399

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,21 @@ jobs:
- ubuntu

ruby:
- 2.5
- 2.6
- 2.7
- 3.0
- 3.1
- 3.2
- 3.3
- 3.4

gemfile: ["Gemfile"]
gemfile:
- gems/rack-v2.rb

include:
- experimental: false
os: macos
ruby: 3.3
ruby: 3.4
gemfile: gems/rack-v2.rb
- experimental: true
os: ubuntu
Expand All @@ -53,13 +54,16 @@ jobs:
gemfile: gems/rack-v1.rb
- experimental: true
os: ubuntu
ruby: 3.2
ruby: 3.4
gemfile: gems/rack-v2.rb
# enable when rack v3 is supported
# - experimental: true
# os: ubuntu
# ruby: 3.2
# env: BUNDLE_GEMFILE=gems/rack-head.rb
- experimental: true
os: ubuntu
ruby: 3.4
gemfile: gems/rack-v3.rb
- experimental: true
os: ubuntu
ruby: 3.4
gemfile: gems/rack-head.rb

steps:
- uses: actions/checkout@v3
Expand Down
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ gemspec

group :development do
gem "rake-compiler"
gem "rdoc"
gem "benchmark"
end

group :test do
Expand Down
2 changes: 1 addition & 1 deletion benchmark/benchmarker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def start_server(handler_name)
end

app = proc do |env|
[200, {'Content-Type' => 'text/html', 'Content-Length' => '11'}, ['hello world']]
[200, {'content-type' => 'text/html', 'content-length' => '11'}, ['hello world']]
end

handler = Rack::Handler.const_get(handler_name)
Expand Down
6 changes: 3 additions & 3 deletions example/adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def call(env)
body = ["hello!"]
[
200,
{ 'Content-Type' => 'text/plain' },
{ 'content-type' => 'text/plain' },
body
]
end
Expand All @@ -20,13 +20,13 @@ def call(env)
run SimpleAdapter.new
end
map '/files' do
run Rack::File.new('.')
run Rack::Files.new('.')
end
end

# You could also start the server like this:
#
# app = Rack::URLMap.new('/test' => SimpleAdapter.new,
# '/files' => Rack::File.new('.'))
# '/files' => Rack::Files.new('.'))
# Thin::Server.start('0.0.0.0', 3000, app)
#
2 changes: 1 addition & 1 deletion example/async_app.ru
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ class AsyncApp
body = DeferrableBody.new

# Get the headers out there asap, let the client know we're alive...
EventMachine::next_tick { env['async.callback'].call [200, {'Content-Type' => 'text/plain'}, body] }
EventMachine::next_tick { env['async.callback'].call [200, {'content-type' => 'text/plain'}, body] }

# Semi-emulate a long db request, instead of a timer, in reality we'd be
# waiting for the response data. Whilst this happens, other connections
Expand Down
4 changes: 2 additions & 2 deletions example/async_chat.ru
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class Chat
send_message = function(message_box) {
xhr = XHR();
xhr.open("POST", "/", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("X_REQUESTED_WITH", "XMLHttpRequest");
xhr.send("message="+escape(message_box.value));
scroll();
Expand Down Expand Up @@ -161,7 +161,7 @@ class Chat
body.callback { delete_user user_id }

EventMachine::next_tick do
renderer.call [200, {'Content-Type' => 'text/html'}, body]
renderer.call [200, {'content-type' => 'text/html'}, body]
end
end

Expand Down
2 changes: 1 addition & 1 deletion example/async_tailer.ru
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ class AsyncTailer

EventMachine::next_tick do

env['async.callback'].call [200, {'Content-Type' => 'text/html'}, body]
env['async.callback'].call [200, {'content-type' => 'text/html'}, body]

body.call ["<h1>Async Tailer</h1><pre>"]

Expand Down
2 changes: 1 addition & 1 deletion example/config.ru
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ app = proc do |env|

[
200, # Status code
{ 'Content-Type' => 'text/html' }, # Reponse headers
{ 'content-type' => 'text/html' }, # Reponse headers
body # Body of the response
]
end
Expand Down
2 changes: 1 addition & 1 deletion ext/thin_parser/common.rl
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
Method = ( upper | digit | safe ){1,20} >mark %request_method;

http_number = ( digit+ "." digit+ ) ;
HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ;
HTTP_Version = ( "HTTP/" http_number ) >mark %request_http_version ;
Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ;

field_name = ( token -- ":" )+ >start_field %write_field;
Expand Down
4 changes: 2 additions & 2 deletions ext/thin_parser/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,8 @@ case 13:
tr17:
#line 59 "parser.rl"
{
if (parser->http_version != NULL) {
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, p));
if (parser->request_http_version != NULL) {
parser->request_http_version(parser->data, PTR_TO(mark), LEN(mark, p));
}
}
goto st14;
Expand Down
2 changes: 1 addition & 1 deletion ext/thin_parser/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ typedef struct http_parser {
element_cb fragment;
element_cb request_path;
element_cb query_string;
element_cb http_version;
element_cb request_http_version;
element_cb header_done;

} http_parser;
Expand Down
6 changes: 3 additions & 3 deletions ext/thin_parser/parser.rl
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@
}
}

action http_version {
if (parser->http_version != NULL) {
parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
action request_http_version {
if (parser->request_http_version != NULL) {
parser->request_http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
}
}

Expand Down
12 changes: 6 additions & 6 deletions ext/thin_parser/thin.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ static VALUE global_request_method;
static VALUE global_request_uri;
static VALUE global_fragment;
static VALUE global_query_string;
static VALUE global_http_version;
static VALUE global_request_http_version;
static VALUE global_content_length;
static VALUE global_http_content_length;
static VALUE global_request_path;
Expand Down Expand Up @@ -150,11 +150,12 @@ static void query_string(void *data, const char *at, size_t length)
rb_hash_aset(req, global_query_string, val);
}

static void http_version(void *data, const char *at, size_t length)
static void request_http_version(void *data, const char *at, size_t length)
{
VALUE req = (VALUE)data;
VALUE val = rb_str_new(at, length);
rb_hash_aset(req, global_http_version, val);
rb_hash_aset(req, global_request_http_version, val);
rb_hash_aset(req, global_server_protocol, val);
}

/** Finalizes the request header to have a bunch of stuff that's
Expand Down Expand Up @@ -211,7 +212,6 @@ static void header_done(void *data, const char *at, size_t length)
}

/* set some constants */
rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
rb_hash_aset(req, global_url_scheme, global_url_scheme_value);
rb_hash_aset(req, global_script_name, global_empty);
}
Expand All @@ -237,7 +237,7 @@ VALUE Thin_HttpParser_alloc(VALUE klass)
hp->fragment = fragment;
hp->request_path = request_path;
hp->query_string = query_string;
hp->http_version = http_version;
hp->request_http_version = request_http_version;
hp->header_done = header_done;
thin_http_parser_init(hp);

Expand Down Expand Up @@ -401,7 +401,7 @@ void Init_thin_parser()
DEF_GLOBAL(request_uri, "REQUEST_URI");
DEF_GLOBAL(fragment, "FRAGMENT");
DEF_GLOBAL(query_string, "QUERY_STRING");
DEF_GLOBAL(http_version, "HTTP_VERSION");
DEF_GLOBAL(request_http_version, "thin.request_http_version");
DEF_GLOBAL(request_path, "REQUEST_PATH");
DEF_GLOBAL(content_length, "CONTENT_LENGTH");
DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
Expand Down
13 changes: 1 addition & 12 deletions gems/rack-head.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gemspec path: "../"
eval_gemfile "../Gemfile"

gem 'rack', github: 'rack/rack'

group :development do
gem "rake-compiler"
end

group :test do
gem "rake", ">= 12.3.3"
gem "rspec", "~> 3.5"
end
14 changes: 1 addition & 13 deletions gems/rack-v1.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'

gemspec path: "../"
eval_gemfile "../Gemfile"

gem 'rack', '~> 1.0'

group :development do
gem "rake-compiler"
end

group :test do
gem "rake", ">= 12.3.3"
gem "rspec", "~> 3.5"
end

15 changes: 2 additions & 13 deletions gems/rack-v2.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
# frozen_string_literal: true

source 'https://rubygems.org'
eval_gemfile "../Gemfile"

gemspec path: "../"

gem 'rack', '~> 2.0'

group :development do
gem "rake-compiler"
end

group :test do
gem "rake", ">= 12.3.3"
gem "rspec", "~> 3.5"
end
gem 'rack', "~> 2.0"
5 changes: 5 additions & 0 deletions gems/rack-v3.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true

eval_gemfile "../Gemfile"

gem 'rack', "~> 3.0"
9 changes: 8 additions & 1 deletion lib/rack/adapter/loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ class AdapterNotFound < RuntimeError; end
[:file, nil]
]

# Rack v1 compatibility...
unless const_defined?(:Files)
require 'rack/file'

Files = File
end

module Adapter
# Guess which adapter to use based on the directory structure
# or file content.
Expand Down Expand Up @@ -64,7 +71,7 @@ def self.for(name, options={})
return Merb::Rack::Application.new

when :file
return Rack::File.new(options[:chdir])
return Rack::Files.new(options[:chdir])

else
raise AdapterNotFound, "Adapter not found: #{name}"
Expand Down
2 changes: 1 addition & 1 deletion lib/rack/adapter/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def initialize(options = {})
load_application

@rails_app = self.class.rack_based? ? ActionController::Dispatcher.new : CgiApp.new
@file_app = Rack::File.new(::File.join(RAILS_ROOT, "public"))
@file_app = Rack::Files.new(::File.join(RAILS_ROOT, "public"))
end

def load_application
Expand Down
4 changes: 2 additions & 2 deletions lib/thin/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Request
SERVER_NAME = 'SERVER_NAME'.freeze
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
LOCALHOST = 'localhost'.freeze
HTTP_VERSION = 'HTTP_VERSION'.freeze
REQUEST_HTTP_VERSION = 'thin.request_http_version'.freeze
HTTP_1_0 = 'HTTP/1.0'.freeze
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
Expand Down Expand Up @@ -114,7 +114,7 @@ def persistent?
# Clients and servers SHOULD NOT assume that a persistent connection
# is maintained for HTTP versions less than 1.1 unless it is explicitly
# signaled. (http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html)
if @env[HTTP_VERSION] == HTTP_1_0
if @env[REQUEST_HTTP_VERSION] == HTTP_1_0
@env[CONNECTION] =~ KEEP_ALIVE_REGEXP

# HTTP/1.1 client intends to maintain a persistent connection unless
Expand Down
Loading
Loading