Skip to content

restructure core extensions and stub to fix issues with live requests #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion spec/webmock_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,6 @@ describe WebMock do
WebMock.stub(:get, "http://www.example.com").to_return(body_io: IO::Memory.new("Hello!"))

body = HTTP::Client.get("http://www.example.com").body
body.should eq("Hello!")
end
end

Expand Down
76 changes: 54 additions & 22 deletions src/webmock/core_ext.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,69 @@ end

class HTTP::Client
private def exec_internal(request : HTTP::Request)
response = exec_internal(request) { |res| res }
response.tap do |response|
response.consume_body_io
response.headers.delete("Transfer-encoding")
response.headers["Content-length"] = response.body.bytesize.to_s
end
before_request(request)
WebMock.find_stub(request).try { |stub| return stub.exec(request) }

perform_live_request(request)
end

private def exec_internal(request : HTTP::Request, &block : Response -> T) : T forall T
request.scheme = "https" if tls?
request.headers["Host"] = host_header unless request.headers.has_key?("Host")
run_before_request_callbacks(request)
private def exec_internal(request : HTTP::Request, &block : Response -> T) forall T
before_request(request)
WebMock.find_stub(request).try do |stub|
stub.as_block
return yield(stub.exec(request))
end

stub = WebMock.find_stub(request)
return yield(stub.exec(request)) if stub
raise WebMock::NetConnectNotAllowedError.new(request) unless WebMock.allows_net_connect?
perform_live_request(request) do |response|
yield response
end
end

request.headers["User-agent"] ||= "Crystal"
request.to_io(socket)
socket.flush
private def perform_live_request(request)
send_live_request(request)
receive_live_response(request)
end

private def perform_live_request(request, &block)
send_live_request(request)
receive_live_response(request) do |response|
yield response
end
end

result = nil
private def receive_live_response(request)
HTTP::Client::Response.from_io(socket, request.ignore_body?).tap do |response|
after_live_request(request, response)
end
end

private def receive_live_response(request, &block)
HTTP::Client::Response.from_io(socket, request.ignore_body?) do |response|
result = yield(response)
close unless response.keep_alive?
WebMock.callbacks.call(:after_live_request, request, response)
yield(response).tap do
after_live_request(request, response)
end
end
end

private def after_live_request(request, response)
close unless response.keep_alive?
WebMock.callbacks.call(:after_live_request, request, response)
end


raise "Unexpected end of response" unless result.is_a?(T)
private def send_live_request(request)
unless WebMock.allows_net_connect?
raise WebMock::NetConnectNotAllowedError.new(request)
end

result
request.headers["User-agent"] ||= "Crystal"
request.to_io(socket)
socket.flush
end

private def before_request(request)
request.scheme = "https" if tls?
request.headers["Host"] = host_header unless request.headers.has_key?("Host")
run_before_request_callbacks(request)
end
end
35 changes: 29 additions & 6 deletions src/webmock/stub.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ class WebMock::Stub
@uri : URI
@expected_headers : HTTP::Headers?
@calls = 0
@body : String
@body_io : IO?
@requested_as_block = false

def initialize(method : Symbol | String, uri)
@method = method.to_s.upcase
Expand All @@ -15,6 +17,8 @@ class WebMock::Stub
@headers = HTTP::Headers{"Content-length" => "0", "Connection" => "close"}

@block = Proc(HTTP::Request, HTTP::Client::Response).new do |request|
copy_body unless @requested_as_block
set_content_headers
HTTP::Client::Response.new(@status, body: @body, headers: @headers, body_io: @body_io)
end
end
Expand All @@ -30,24 +34,25 @@ class WebMock::Stub
@body = body
@body_io = nil
@status = status
@headers.delete("Transfer-encoding")
@headers["Content-length"] = body.size.to_s
@headers.merge!(headers) if headers
self
end

def to_return(body_io : IO, status = 200, headers = nil)
@body = nil
@body = ""
@body_io = body_io
@status = status
@headers.delete("Content-length")
@headers["Transfer-encoding"] = "chunked"
@headers.merge!(headers) if headers
self
end

def to_return(&block : HTTP::Request -> HTTP::Client::Response)
@block = block
@block = Proc(HTTP::Request, HTTP::Client::Response).new do |request|
copy_body unless @requested_as_block
set_content_headers
block.call(request)
end

self
end

Expand Down Expand Up @@ -121,6 +126,24 @@ class WebMock::Stub
@calls
end

def as_block
@requested_as_block = true
end

private def set_content_headers
if @requested_as_block
@headers.delete("Content-Length")
@headers["Transfer-Encoding"] = "chunked"
else
@headers["Content-Length"] = @body.bytesize.to_s
@headers.delete("Transfer-Encoding")
end
end

private def copy_body
@body_io.try { |io| @body = io.gets_to_end || "" }
end

private def parse_uri(uri_string)
uri = URI.parse(uri_string)
uri = URI.parse("http://#{uri_string}") unless uri.host
Expand Down