module Puma::Request
def prepare_response(status, headers, res_body, requests, client)
-
(Boolean, :async)
- keep-alive status or `:async`
Parameters:
-
client
(Puma::Client
) -- -
requests
(Integer
) -- number of inline requests handled -
res_body
(Array
) -- the body returned by the Rack application or -
headers
(Hash
) -- the headers returned by the Rack application -
status
(Integer
) -- the status returned by the Rack application
def prepare_response(status, headers, res_body, requests, client) env = client.env socket = client.io io_buffer = client.io_buffer return false if closed_socket?(socket) # Close the connection after a reasonable number of inline requests # if the server is at capacity and the listener has a new connection ready. # This allows Puma to service connections fairly when the number # of concurrent connections exceeds the size of the threadpool. force_keep_alive = if @enable_keep_alives requests < @max_fast_inline || @thread_pool.busy_threads < @max_threads || !client.listener.to_io.wait_readable(0) else # Always set force_keep_alive to false if the server has keep-alives not enabled. false end resp_info = str_headers(env, status, headers, res_body, io_buffer, force_keep_alive) close_body = false response_hijack = nil content_length = resp_info[:content_length] keep_alive = resp_info[:keep_alive] if res_body.respond_to?(:each) && !resp_info[:response_hijack] # below converts app_body into body, dependent on app_body's characteristics, and # content_length will be set if it can be determined if !content_length && !resp_info[:transfer_encoding] && status != 204 if res_body.respond_to?(:to_ary) && (array_body = res_body.to_ary) && array_body.is_a?(Array) body = array_body.compact content_length = body.sum(&:bytesize) elsif res_body.is_a?(File) && res_body.respond_to?(:size) body = res_body content_length = body.size elsif res_body.respond_to?(:to_path) && (fn = res_body.to_path) && File.readable?(fn) body = File.open fn, 'rb' content_length = body.size close_body = true else body = res_body end elsif !res_body.is_a?(::File) && res_body.respond_to?(:to_path) && (fn = res_body.to_path) && File.readable?(fn = res_body.to_path) body = File.open fn, 'rb' content_length = body.size close_body = true elsif !res_body.is_a?(::File) && res_body.respond_to?(:filename) && res_body.respond_to?(:bytesize) && File.readable?(fn = res_body.filename) # Sprockets::Asset content_length = res_body.bytesize unless content_length if (body_str = res_body.to_hash[:source]) body = [body_str] else # avoid each and use a File object body = File.open fn, 'rb' close_body = true end else body = res_body end else # partial hijack, from Rack spec: # Servers must ignore the body part of the response tuple when the # rack.hijack response header is present. response_hijack = resp_info[:response_hijack] || res_body end line_ending = LINE_END cork_socket socket if resp_info[:no_body] # 101 (Switching Protocols) doesn't return here or have content_length, # it should be using `response_hijack` unless status == 101 if content_length && status != 204 io_buffer.append CONTENT_LENGTH_S, content_length.to_s, line_ending end io_buffer << LINE_END fast_write_str socket, io_buffer.read_and_reset socket.flush return keep_alive end else if content_length io_buffer.append CONTENT_LENGTH_S, content_length.to_s, line_ending chunked = false elsif !response_hijack && resp_info[:allow_chunked] io_buffer << TRANSFER_ENCODING_CHUNKED chunked = true end end io_buffer << line_ending # partial hijack, we write headers, then hand the socket to the app via # response_hijack.call if response_hijack fast_write_str socket, io_buffer.read_and_reset uncork_socket socket response_hijack.call socket return :async end fast_write_response socket, body, io_buffer, chunked, content_length.to_i body.close if close_body keep_alive end