lib/rackup/handler/webrick.rb in rackup-2.0.0 vs lib/rackup/handler/webrick.rb in rackup-2.1.0

- old
+ new

@@ -9,32 +9,15 @@ require 'rack/constants' require_relative '../handler' require_relative '../version' -# This monkey patch allows for applications to perform their own chunking -# through WEBrick::HTTPResponse if rack is set to true. -class WEBrick::HTTPResponse - attr_accessor :rack +require_relative '../stream' - alias _rack_setup_header setup_header - def setup_header - app_chunking = rack && @header['transfer-encoding'] == 'chunked' - - @chunked = app_chunking if app_chunking - - _rack_setup_header - - @chunked = false if app_chunking - end -end - module Rackup module Handler class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet - include Rack - def self.run(app, **options) environment = ENV['RACK_ENV'] || 'development' default_host = environment == 'development' ? 'localhost' : nil if !options[:BindAddress] || options[:Host] @@ -71,69 +54,106 @@ def initialize(server, app) super server @app = app end + # This handles mapping the WEBrick request to a Rack input stream. + class Input + include Stream::Reader + + def initialize(request) + @request = request + + @reader = Fiber.new do + @request.body do |chunk| + Fiber.yield(chunk) + end + + Fiber.yield(nil) + + # End of stream: + @reader = nil + end + end + + def close + @request = nil + @reader = nil + end + + private + + # Read one chunk from the request body. + def read_next + @reader&.resume + end + end + def service(req, res) - res.rack = true env = req.meta_vars env.delete_if { |k, v| v.nil? } - rack_input = StringIO.new(req.body.to_s) - rack_input.set_encoding(Encoding::BINARY) + input = Input.new(req) env.update( - RACK_INPUT => rack_input, - RACK_ERRORS => $stderr, - RACK_URL_SCHEME => ["yes", "on", "1"].include?(env[HTTPS]) ? "https" : "http", - RACK_IS_HIJACK => true, + ::Rack::RACK_INPUT => input, + ::Rack::RACK_ERRORS => $stderr, + ::Rack::RACK_URL_SCHEME => ["yes", "on", "1"].include?(env[::Rack::HTTPS]) ? "https" : "http", + ::Rack::RACK_IS_HIJACK => true, ) - env[QUERY_STRING] ||= "" - unless env[PATH_INFO] == "" - path, n = req.request_uri.path, env[SCRIPT_NAME].length - env[PATH_INFO] = path[n, path.length - n] + env[::Rack::QUERY_STRING] ||= "" + unless env[::Rack::PATH_INFO] == "" + path, n = req.request_uri.path, env[::Rack::SCRIPT_NAME].length + env[::Rack::PATH_INFO] = path[n, path.length - n] end - env[REQUEST_PATH] ||= [env[SCRIPT_NAME], env[PATH_INFO]].join + env[::Rack::REQUEST_PATH] ||= [env[::Rack::SCRIPT_NAME], env[::Rack::PATH_INFO]].join status, headers, body = @app.call(env) begin res.status = status - if value = headers[RACK_HIJACK] + if value = headers[::Rack::RACK_HIJACK] io_lambda = value + body = nil elsif !body.respond_to?(:to_path) && !body.respond_to?(:each) io_lambda = body + body = nil end if value = headers.delete('set-cookie') res.cookies.concat(Array(value)) end - headers.each { |key, value| + headers.each do |key, value| # Skip keys starting with rack., per Rack SPEC next if key.start_with?('rack.') # Since WEBrick won't accept repeated headers, # merge the values per RFC 1945 section 4.2. value = value.join(", ") if Array === value res[key] = value - } + end if io_lambda - rd, wr = IO.pipe - res.body = rd - res.chunked = true - io_lambda.call wr + protocol = headers['rack.protocol'] || headers['upgrade'] + + if protocol + # Set all the headers correctly for an upgrade response: + res.upgrade!(protocol) + end + res.body = io_lambda elsif body.respond_to?(:to_path) res.body = ::File.open(body.to_path, 'rb') else - body.each { |part| - res.body << part - } + buffer = String.new + body.each do |part| + buffer << part + end + res.body = buffer end ensure - body.close if body.respond_to? :close + body.close if body.respond_to?(:close) end end end register :webrick, WEBrick