-
-
Notifications
You must be signed in to change notification settings - Fork 188
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HeadRequestHandler: run GET handler and don't return the body (#655)
- Loading branch information
Showing
7 changed files
with
115 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
require "./spec_helper" | ||
|
||
describe "Kemal::HeadRequestHandler" do | ||
it "implicitly handles GET endpoints, with Content-Length header" do | ||
get "/" do | ||
"hello" | ||
end | ||
request = HTTP::Request.new("HEAD", "/") | ||
client_response = call_request_on_app(request) | ||
client_response.body.should eq("") | ||
client_response.headers["Content-Length"].should eq("5") | ||
end | ||
|
||
it "prefers explicit HEAD endpoint if specified" do | ||
Kemal::RouteHandler::INSTANCE.add_route("HEAD", "/") { "hello" } | ||
get "/" do | ||
raise "shouldn't be called!" | ||
end | ||
request = HTTP::Request.new("HEAD", "/") | ||
client_response = call_request_on_app(request) | ||
client_response.body.should eq("") | ||
client_response.headers["Content-Length"].should eq("5") | ||
end | ||
|
||
it "gives compressed Content-Length when gzip enabled" do | ||
gzip true | ||
get "/" do | ||
"hello" | ||
end | ||
headers = HTTP::Headers{"Accept-Encoding" => "gzip"} | ||
request = HTTP::Request.new("HEAD", "/", headers) | ||
client_response = call_request_on_app(request) | ||
client_response.body.should eq("") | ||
client_response.headers["Content-Encoding"].should eq("gzip") | ||
client_response.headers["Content-Length"].should eq("25") | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
require "http/server/handler" | ||
|
||
module Kemal | ||
class HeadRequestHandler | ||
include HTTP::Handler | ||
|
||
INSTANCE = new | ||
|
||
private class NullIO < IO | ||
@original_output : IO | ||
@out_count : Int32 | ||
@response : HTTP::Server::Response | ||
|
||
def initialize(@response) | ||
@closed = false | ||
@original_output = @response.output | ||
@out_count = 0 | ||
end | ||
|
||
def read(slice : Bytes) | ||
raise NotImplementedError.new("read") | ||
end | ||
|
||
def write(slice : Bytes) : Nil | ||
@out_count += slice.bytesize | ||
end | ||
|
||
def close : Nil | ||
return if @closed | ||
@closed = true | ||
|
||
# Matching HTTP::Server::Response#close behavior: | ||
# Conditionally determine based on status if the `content-length` header should be added automatically. | ||
# See https://tools.ietf.org/html/rfc7230#section-3.3.2. | ||
status = @response.status | ||
set_content_length = !(status.not_modified? || status.no_content? || status.informational?) | ||
|
||
if !@response.headers.has_key?("Content-Length") && set_content_length | ||
@response.content_length = @out_count | ||
end | ||
|
||
@original_output.close | ||
end | ||
|
||
def closed? : Bool | ||
@closed | ||
end | ||
end | ||
|
||
def call(context) : Nil | ||
if context.request.method == "HEAD" | ||
# Capture and count bytes of response body generated on HEAD requests without actually sending the body back. | ||
capture_io = NullIO.new(context.response) | ||
context.response.output = capture_io | ||
end | ||
|
||
call_next(context) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters