173 lines
4.0 KiB
Ruby
Executable File
173 lines
4.0 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
# Phusion Passenger - https://www.phusionpassenger.com/
|
|
# Copyright (c) 2010-2025 Asynchronous B.V.
|
|
#
|
|
# "Passenger", "Phusion Passenger" and "Union Station" are registered
|
|
# trademarks of Asynchronous B.V.
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
|
|
STDOUT.sync = true
|
|
STDERR.sync = true
|
|
|
|
require 'uri'
|
|
require 'socket'
|
|
require 'timeout'
|
|
require 'base64'
|
|
|
|
class PrespawnLocation
|
|
class InvalidURLError < RuntimeError
|
|
def initialize(url)
|
|
@url = url
|
|
end
|
|
|
|
def to_s
|
|
message
|
|
end
|
|
|
|
def message
|
|
"'#{@url}' is not a valid URL."
|
|
end
|
|
end
|
|
|
|
def self.parse(url)
|
|
uri = uri_for(url)
|
|
|
|
if uri.scheme == 'unix' || uri.host == 'unix'
|
|
location = UNIXPrespawnLocation.new(uri)
|
|
else
|
|
case uri.scheme
|
|
when 'http'
|
|
location = TCPPrespawnLocation.new(uri)
|
|
when 'https'
|
|
location = SSLPrespawnLocation.new(uri)
|
|
end
|
|
end
|
|
|
|
unless location
|
|
raise InvalidURLError, url
|
|
end
|
|
|
|
location
|
|
end
|
|
|
|
def self.uri_for(url)
|
|
URI.parse(url)
|
|
rescue URI::InvalidURIError
|
|
raise InvalidURLError, url
|
|
end
|
|
|
|
def initialize(uri)
|
|
@uri = uri
|
|
end
|
|
|
|
def request_path
|
|
@uri.path.empty? ? '/' : @uri.path
|
|
end
|
|
|
|
def request_host
|
|
@uri.host
|
|
end
|
|
|
|
def socket
|
|
@socket ||= connect
|
|
end
|
|
|
|
if Base64.respond_to?(:strict_encode64)
|
|
def base64(data)
|
|
Base64.strict_encode64(data)
|
|
end
|
|
else
|
|
# Base64-encodes the given data. Newlines are removed.
|
|
# This is like `Base64.strict_encode64`, but also works
|
|
# on Ruby 1.8 which doesn't have that method.
|
|
def base64(data)
|
|
result = Base64.encode64(data)
|
|
result.delete!("\n")
|
|
result
|
|
end
|
|
end
|
|
|
|
def head_request
|
|
socket.write("HEAD #{request_path} HTTP/1.1\r\n")
|
|
socket.write("Host: #{request_host}\r\n")
|
|
socket.write("User-Agent: Passenger Prespawn Script\r\n")
|
|
socket.write("Authorization: Basic " + base64(@uri.userinfo) + "\r\n") if @uri.userinfo
|
|
socket.write("Connection: close\r\n")
|
|
socket.write("\r\n")
|
|
|
|
begin
|
|
Timeout.timeout(10) do
|
|
socket.read
|
|
end
|
|
rescue Timeout::Error
|
|
end
|
|
end
|
|
end
|
|
|
|
class TCPPrespawnLocation < PrespawnLocation
|
|
def request_port
|
|
@uri.port
|
|
end
|
|
|
|
def connect
|
|
TCPSocket.new('127.0.0.1', request_port)
|
|
rescue Errno::ECONNREFUSED
|
|
TCPSocket.new('::1', request_port)
|
|
end
|
|
end
|
|
|
|
class SSLPrespawnLocation < TCPPrespawnLocation
|
|
def connect
|
|
require 'openssl'
|
|
socket = OpenSSL::SSL::SSLSocket.new(super)
|
|
socket.sync_close = true
|
|
socket.connect
|
|
socket
|
|
end
|
|
end
|
|
|
|
class UNIXPrespawnLocation < PrespawnLocation
|
|
def request_path
|
|
super.split(':', 2).last
|
|
end
|
|
|
|
def request_host
|
|
'_'
|
|
end
|
|
|
|
def socket_path
|
|
@uri.path.split(':', 2).first
|
|
end
|
|
|
|
def connect
|
|
UNIXSocket.new(socket_path)
|
|
end
|
|
end
|
|
|
|
url = ARGV[0]
|
|
begin
|
|
prespawn_location = PrespawnLocation.parse(url)
|
|
rescue PrespawnLocation::InvalidURLError => e
|
|
STDERR.puts "*** ERROR: #{e}"
|
|
exit 1
|
|
end
|
|
|
|
prespawn_location.head_request
|