From 3d69a6f064078eeb28c1819725d3715ce6905374 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 27 Jan 2016 00:08:19 +0000 Subject: rack is optional at runtime, required for dev We do not want to pull in a newer or older version of rack depending on an the application running under it requires. Furthermore, it has always been possible to use unicorn without any middleware at all. Without rack, we'll be missing descriptive status text in the first response line, but any valid HTTP/1.x parser should be able to handle it properly. ref: http://bogomips.org/unicorn-public/20160121201255.GA6186@dcvr.yhbt.net/t/#u Thanks-to: Adam Duke Thanks-to: Aaron Patterson --- lib/unicorn.rb | 45 ++++++++++++++++++++++++++------------------ lib/unicorn/http_response.rb | 7 +++++-- unicorn.gemspec | 10 +++++----- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/unicorn.rb b/lib/unicorn.rb index b0e6bd1..bb66b61 100644 --- a/lib/unicorn.rb +++ b/lib/unicorn.rb @@ -1,9 +1,14 @@ # -*- encoding: binary -*- require 'etc' require 'stringio' -require 'rack' require 'kgio' +begin + require 'rack' +rescue LoadError + warn 'rack not available, functionality reduced' +end + # :stopdoc: # Unicorn module containing all of the classes (include C extensions) for # running a Unicorn web server. It contains a minimalist HTTP server with just @@ -32,6 +37,9 @@ module Unicorn def self.builder(ru, op) # allow Configurator to parse cli switches embedded in the ru file op = Unicorn::Configurator::RACKUP.merge!(:file => ru, :optparse => op) + if ru =~ /\.ru$/ && !defined?(Rack::Builder) + abort "rack and Rack::Builder must be available for processing #{ru}" + end # Op is going to get cleared before the returned lambda is called, so # save this value so that it's still there when we need it: @@ -53,32 +61,33 @@ module Unicorn return inner_app if no_default_middleware + middleware = { # order matters + ContentLength: nil, + Chunked: nil, + CommonLogger: [ $stderr ], + ShowExceptions: nil, + Lint: nil, + TempfileReaper: nil, + } + # return value, matches rackup defaults based on env # Unicorn does not support persistent connections, but Rainbows! # and Zbatery both do. Users accustomed to the Rack::Server default # middlewares will need ContentLength/Chunked middlewares. case ENV["RACK_ENV"] when "development" - Rack::Builder.new do - use Rack::ContentLength - use Rack::Chunked - use Rack::CommonLogger, $stderr - use Rack::ShowExceptions - use Rack::Lint - use Rack::TempfileReaper if Rack.const_defined?(:TempfileReaper) - run inner_app - end.to_app when "deployment" - Rack::Builder.new do - use Rack::ContentLength - use Rack::Chunked - use Rack::CommonLogger, $stderr - use Rack::TempfileReaper if Rack.const_defined?(:TempfileReaper) - run inner_app - end.to_app + middleware.delete(:ShowExceptions) + middleware.delete(:Lint) else - inner_app + return inner_app end + Rack::Builder.new do + middleware.each do |m, args| + use(Rack.const_get(m), *args) if Rack.const_defined?(m) + end + run inner_app + end.to_app end end diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb index 7b446c2..ec128e4 100644 --- a/lib/unicorn/http_response.rb +++ b/lib/unicorn/http_response.rb @@ -10,10 +10,13 @@ # is the job of Rack, with the exception of the "Date" and "Status" header. module Unicorn::HttpResponse + STATUS_CODES = defined?(Rack::Utils::HTTP_STATUS_CODES) ? + Rack::Utils::HTTP_STATUS_CODES : {} + # internal API, code will always be common-enough-for-even-old-Rack def err_response(code, response_start_sent) "#{response_start_sent ? '' : 'HTTP/1.1 '}" \ - "#{code} #{Rack::Utils::HTTP_STATUS_CODES[code]}\r\n\r\n" + "#{code} #{STATUS_CODES[code]}\r\n\r\n" end # writes the rack_response to socket as an HTTP response @@ -23,7 +26,7 @@ module Unicorn::HttpResponse if headers code = status.to_i - msg = Rack::Utils::HTTP_STATUS_CODES[code] + msg = STATUS_CODES[code] start = response_start_sent ? ''.freeze : 'HTTP/1.1 '.freeze buf = "#{start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \ "Date: #{httpdate}\r\n" \ diff --git a/unicorn.gemspec b/unicorn.gemspec index 2728373..cf65aef 100644 --- a/unicorn.gemspec +++ b/unicorn.gemspec @@ -31,11 +31,11 @@ Gem::Specification.new do |s| # version requirements here. s.required_ruby_version = '< 3.0' - # for people that are absolutely stuck on Rails 2.3.2 and can't - # up/downgrade to any other version, the Rack dependency may be - # commented out. Nevertheless, upgrading to Rails 2.3.4 or later is - # *strongly* recommended for security reasons. - s.add_dependency(%q) + # We do not have a hard dependency on rack, it's possible to load + # things which respond to #call. HTTP status lines in responses + # won't have descriptive text, only the numeric status. + s.add_development_dependency(%q) + s.add_dependency(%q, '~> 2.6') s.add_dependency(%q, '~> 0.7') -- cgit v1.2.3-24-ge0c7