mirror of
https://github.com/ruby/ruby.git
synced 2025-08-25 22:14:37 +02:00
100 lines
3.1 KiB
Ruby
100 lines
3.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "rack/test"
|
|
require_relative "../../vendored_net_http"
|
|
|
|
module Artifice
|
|
module Net
|
|
# This is an internal object that can receive Rack requests
|
|
# to the application using the Rack::Test API
|
|
class RackRequest
|
|
include Rack::Test::Methods
|
|
attr_reader :app
|
|
|
|
def initialize(app)
|
|
@app = app
|
|
end
|
|
end
|
|
|
|
class HTTP < ::Gem::Net::HTTP
|
|
class << self
|
|
attr_accessor :endpoint
|
|
end
|
|
|
|
# Gem::Net::HTTP uses a @newimpl instance variable to decide whether
|
|
# to use a legacy implementation. Since we are subclassing
|
|
# Gem::Net::HTTP, we must set it
|
|
@newimpl = true
|
|
|
|
# We don't need to connect, so blank out this method
|
|
def connect
|
|
end
|
|
|
|
# Replace the Gem::Net::HTTP request method with a method
|
|
# that converts the request into a Rack request and
|
|
# dispatches it to the Rack endpoint.
|
|
#
|
|
# @param [Net::HTTPRequest] req A Gem::Net::HTTPRequest
|
|
# object, or one if its subclasses
|
|
# @param [optional, String, #read] body This should
|
|
# be sent as "rack.input". If it's a String, it will
|
|
# be converted to a StringIO.
|
|
# @return [Net::HTTPResponse]
|
|
#
|
|
# @yield [Net::HTTPResponse] If a block is provided,
|
|
# this method will yield the Gem::Net::HTTPResponse to
|
|
# it after the body is read.
|
|
def request(req, body = nil, &block)
|
|
rack_request = RackRequest.new(self.class.endpoint)
|
|
|
|
req.each_header do |header, value|
|
|
rack_request.header(header, value)
|
|
end
|
|
|
|
scheme = use_ssl? ? "https" : "http"
|
|
prefix = "#{scheme}://#{addr_port}"
|
|
body_stream_contents = req.body_stream.read if req.body_stream
|
|
|
|
response = rack_request.request("#{prefix}#{req.path}",
|
|
{ method: req.method, input: body || req.body || body_stream_contents })
|
|
|
|
make_net_http_response(response, &block)
|
|
end
|
|
|
|
private
|
|
|
|
# This method takes a Rack response and creates a Gem::Net::HTTPResponse
|
|
# Instead of trying to mock HTTPResponse directly, we just convert
|
|
# the Rack response into a String that looks like a normal HTTP
|
|
# response and call Gem::Net::HTTPResponse.read_new
|
|
#
|
|
# @param [Array(#to_i, Hash, #each)] response a Rack response
|
|
# @return [Net::HTTPResponse]
|
|
# @yield [Net::HTTPResponse] If a block is provided, yield the
|
|
# response to it after the body is read
|
|
def make_net_http_response(response)
|
|
status = response.status
|
|
headers = response.headers
|
|
body = response.body
|
|
|
|
response_string = []
|
|
response_string << "HTTP/1.1 #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}"
|
|
|
|
headers.each do |header, value|
|
|
response_string << "#{header}: #{value}"
|
|
end
|
|
|
|
response_string << "" << body
|
|
|
|
response_io = ::Gem::Net::BufferedIO.new(StringIO.new(response_string.join("\n")))
|
|
res = ::Gem::Net::HTTPResponse.read_new(response_io)
|
|
|
|
res.reading_body(response_io, true) do
|
|
yield res if block_given?
|
|
end
|
|
|
|
res
|
|
end
|
|
end
|
|
end
|
|
end
|