1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
# frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2022-2023, by Samuel Williams.
# Copyright, 2022, by Jeremy Evans.
require 'webrick'
require 'stringio'
require 'rack/constants'
require_relative '../handler'
require_relative '../version'
require_relative '../stream'
module Rackup
module Handler
class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
def self.run(app, **options)
environment = ENV['RACK_ENV'] || 'development'
default_host = environment == 'development' ? 'localhost' : nil
if !options[:BindAddress] || options[:Host]
options[:BindAddress] = options.delete(:Host) || default_host
end
options[:Port] ||= 8080
if options[:SSLEnable]
require 'webrick/https'
end
@server = ::WEBrick::HTTPServer.new(options)
@server.mount "/", Rackup::Handler::WEBrick, app
yield @server if block_given?
@server.start
end
def self.valid_options
environment = ENV['RACK_ENV'] || 'development'
default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
{
"Host=HOST" => "Hostname to listen on (default: #{default_host})",
"Port=PORT" => "Port to listen on (default: 8080)",
}
end
def self.shutdown
if @server
@server.shutdown
@server = nil
end
end
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)
env = req.meta_vars
env.delete_if { |k, v| v.nil? }
input = Input.new(req)
env.update(
::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[::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[::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::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 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
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
buffer = String.new
body.each do |part|
buffer << part
end
res.body = buffer
end
ensure
body.close if body.respond_to?(:close)
end
end
end
register :webrick, WEBrick
end
end
|