Skip to content

Commit

Permalink
Add support for yielding HTTP::Client method types (#27)
Browse files Browse the repository at this point in the history
  • Loading branch information
Joakim Repomaa authored and Brian J. Cardiff committed Oct 7, 2019
1 parent 393a0fe commit 78bb0e3
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 16 deletions.
57 changes: 57 additions & 0 deletions spec/webmock_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,63 @@ describe WebMock do
end
end

context "with body_io" do
it "allows reading the body" do
WebMock.wrap 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

it "sets content-length header correctly" do
WebMock.wrap do
WebMock.stub(:get, "http://www.example.com").to_return(body_io: IO::Memory.new("Hello!"))

headers = HTTP::Client.get("http://www.example.com").headers
headers["Content-length"].should eq("6")
end
end
end

context "with yielding method variants" do
it "stubs the request" do
WebMock.wrap do
stub = WebMock.stub(:get, "http://www.example.com")

HTTP::Client.get("http://www.example.com") do |response|
end

stub.calls.should eq(1)
end
end

it "allows setting and reading body_io" do
WebMock.wrap do
WebMock.stub(:get, "http://www.example.com").to_return(body_io: IO::Memory.new("Hello!"))

body = HTTP::Client.get("http://www.example.com") do |response|
response.body_io.gets_to_end
end

body.should eq("Hello!")
end
end

it "sets transfer encoding header to chunked" do
WebMock.wrap do
WebMock.stub(:get, "http://www.example.com").to_return(body_io: IO::Memory.new("Hello!"))

headers = HTTP::Client.get("http://www.example.com") do |response|
response.headers
end

headers.not_nil!["Transfer-encoding"].should eq("chunked")
end
end
end

# Commented so that specs run fast, but uncomment to try it (it works)
# it "calls callback after live request" do
# WebMock.wrap do
Expand Down
39 changes: 26 additions & 13 deletions src/webmock/core_ext.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,37 @@ 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
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)

stub = WebMock.find_stub(request)
return stub.exec(request) if stub

if WebMock.allows_net_connect?
request.headers["User-agent"] ||= "Crystal"
request.to_io(socket)
socket.flush
res = HTTP::Client::Response.from_io(socket, request.ignore_body?).tap do |response|
close unless response.keep_alive?
end
WebMock.callbacks.call(:after_live_request, request, res)
res
else
raise WebMock::NetConnectNotAllowedError.new(request)
return yield(stub.exec(request)) if stub
raise WebMock::NetConnectNotAllowedError.new(request) unless WebMock.allows_net_connect?

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

result = nil

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)
end

raise "Unexpected end of response" unless result.is_a?(T)

result
end
end
19 changes: 16 additions & 3 deletions src/webmock/stub.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class WebMock::Stub
@uri : URI
@expected_headers : HTTP::Headers?
@calls = 0
@body_io : IO?

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

@block = Proc(HTTP::Request, HTTP::Client::Response).new do |request|
HTTP::Client::Response.new(@status, body: @body, headers: @headers)
HTTP::Client::Response.new(@status, body: @body, headers: @headers, body_io: @body_io)
end
end

Expand All @@ -25,10 +26,22 @@ class WebMock::Stub
self
end

def to_return(body = "", status = 200, headers = nil)
def to_return(body : String? = "", status = 200, headers = nil)
@body = body
@body_io = nil
@status = status
@headers["Content-length"] = @body.size.to_s
@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_io = body_io
@status = status
@headers.delete("Content-length")
@headers["Transfer-encoding"] = "chunked"
@headers.merge!(headers) if headers
self
end
Expand Down

0 comments on commit 78bb0e3

Please sign in to comment.