Skip to content

Commit

Permalink
Fix outputting Content-Length and Transfer-Encoding headers on HEAD r…
Browse files Browse the repository at this point in the history
…equests for Ruby apps
  • Loading branch information
FooBarWidget committed Feb 27, 2016
1 parent ee122ef commit 9996880
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 32 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Expand Up @@ -2,6 +2,7 @@ Release 5.0.26
--------------

* `passenger-status --show=server` now reports the speed at which new requests are accepted.
* Fixes outputting Content-Length and Transfer-Encoding headers on HEAD requests for Ruby apps. These headers were omitted in previous versions on HEAD requests.
* [Nginx] Passenger can now be [compiled as an Nginx dynamic module](https://www.phusionpassenger.com/library/install/nginx/install_as_nginx_module.html#dynamic-module). Thanks to Ruslan Ermilov from NGINX Inc for contributing this.
* [Standalone] Prints a warning when an unsupported configuration option in Passengerfile.json is set.
* [Enterprise] The rolling restart feature now waits until the old process is completely gone (drained its request queue, process exited) before proceeding with rolling restarting the next process. This results in friendlier resource usage during rolling restart.
Expand Down
7 changes: 6 additions & 1 deletion src/agent/Core/Controller/ForwardResponse.cpp
@@ -1,6 +1,6 @@
/*
* Phusion Passenger - https://www.phusionpassenger.com/
* Copyright (c) 2011-2015 Phusion Holding B.V.
* Copyright (c) 2011-2016 Phusion Holding B.V.
*
* "Passenger", "Phusion Passenger" and "Union Station" are registered
* trademarks of Phusion Holding B.V.
Expand Down Expand Up @@ -295,6 +295,7 @@ Controller::onAppSourceData(Client *client, Request *req, const MemoryKit::mbuf
// EOF
UPDATE_TRACE_POINT();
SKC_TRACE(client, 2, "Application sent EOF");
SKC_TRACE(client, 2, "Not keep-aliving application session connection");
req->session->close(true, false);
endRequest(&client, &req);
return Channel::Result(0, false);
Expand Down Expand Up @@ -1078,6 +1079,10 @@ Controller::handleAppResponseBodyEnd(Client *client, Request *req) {

OXT_FORCE_INLINE void
Controller::keepAliveAppConnection(Client *client, Request *req) {
SKC_TRACE(client, 2,
((req->appResponse.wantKeepAlive)
? "Keep-aliving application session connection"
: "Not keep-aliving application session connection"));
req->session->close(true, req->appResponse.wantKeepAlive);
}

Expand Down
@@ -1,6 +1,6 @@
# encoding: binary
# Phusion Passenger - https://www.phusionpassenger.com/
# Copyright (c) 2010-2015 Phusion Holding B.V.
# Copyright (c) 2010-2016 Phusion Holding B.V.
#
# "Passenger", "Phusion Passenger" and "Union Station" are registered
# trademarks of Phusion Holding B.V.
Expand Down Expand Up @@ -215,7 +215,7 @@ def process_body(env, connection, socket_wrapper, status, is_head_request, heade
if !body.is_a?(Array) || headers.has_key?(X_SENDFILE_HEADER) ||
headers.has_key?(X_ACCEL_REDIRECT_HEADER)
# If X-Sendfile or X-Accel-Redirect is set, don't check the
# body size. PassengerAgent's RequestHandler will ignore the
# body size. Passenger's Core Controller will ignore the
# body anyway. See
# ServerKit::HttpHeaderParser::processParseResult(const HttpParseResponse &)
@can_keepalive = false
Expand All @@ -241,7 +241,10 @@ def process_body(env, connection, socket_wrapper, status, is_head_request, heade
# Disallowed by the HTTP spec
raise "Response object may not contain both Content-Length and Transfer-Encoding"
end
elsif output_body
elsif status_code_allows_body?(status)
# This is a response for which a body is allowed, although the request
# may be one which does not expect a body (HEAD requests).
#
# The app has set neither the Content-Length nor the Transfer-Encoding
# header. This means we'll have to add one of those headers. We know exactly how
# big our body will be, so we can keep-alive the connection.
Expand Down Expand Up @@ -270,37 +273,33 @@ def process_body(env, connection, socket_wrapper, status, is_head_request, heade
# If this is a request without body, write out headers without body.
if !output_body
connection.writev(headers_output)
signal_keep_alive_allowed!
return
end

# Otherwise, write out headers and body, then verify at the end whether what
# we wrote is matches the message length. Only keep-alive connection if it
# is the case.
case message_length_type
when :content_length
if body.is_a?(Array)
connection.writev2(headers_output, body)
else
else
# Otherwise, write out headers and body.
case message_length_type
when :content_length
if body.is_a?(Array)
connection.writev2(headers_output, body)
else
connection.writev(headers_output)
body.each do |part|
connection.write(part.to_s)
end
end
when :chunked_by_app
connection.writev(headers_output)
body.each do |part|
connection.write(part.to_s)
end
end
when :chunked_by_app
connection.writev(headers_output)
body.each do |part|
connection.write(part.to_s)
end
when :needs_chunking
connection.writev(headers_output)
body.each do |part|
size = bytesize(part.to_s)
if size != 0
connection.writev(chunk_data(part.to_s, size))
when :needs_chunking
connection.writev(headers_output)
body.each do |part|
size = bytesize(part.to_s)
if size != 0
connection.writev(chunk_data(part.to_s, size))
end
end
connection.write(TERMINATION_CHUNK)
end
connection.write(TERMINATION_CHUNK)
end

signal_keep_alive_allowed!
Expand Down Expand Up @@ -360,10 +359,12 @@ def lookup_header(haystack, needles)
nil
end

def status_code_allows_body?(status)
status < 100 || (status >= 200 && status != 204 && status != 304)
end

def should_output_body?(status, is_head_request)
return (status < 100 ||
(status >= 200 && status != 204 && status != 304)) &&
!is_head_request
status_code_allows_body?(status) && !is_head_request
end

def chunk_data(data, size)
Expand Down

0 comments on commit 9996880

Please sign in to comment.