Module: Puma::Response
Overview
The methods here are included in Server, but are separated into this file. All the methods here pertain to passing the request to the app, then writing the response back to the client.
None of the methods here are called externally, with the exception of #handle_request, which is called in Server#process_client.
Constant Summary collapse
- BODY_LEN_MAX =
Single element array body: smaller bodies are written to io_buffer first, then a single write from io_buffer. Larger sizes are written separately. Also fixes max size of chunked file body read.
1_024 * 256
- IO_BODY_MAX =
File body: smaller bodies are combined with io_buffer, then written to socket. Larger bodies are written separately using ‘copy_stream`
1_024 * 64
- IO_BUFFER_LEN_MAX =
Array body: elements are collected in io_buffer. When io_buffer’s size exceeds value, they are written to the socket.
1_024 * 512
- SOCKET_WRITE_ERR_MSG =
"Socket timeout writing data"- CUSTOM_STAT =
'CUSTOM'
Constants included from Const
Const::BANNED_HEADER_KEY, Const::CGI_VER, Const::CHUNKED, Const::CHUNK_SIZE, Const::CLOSE, Const::CLOSE_CHUNKED, Const::CODE_NAME, Const::COLON, Const::CONNECTION_CLOSE, Const::CONNECTION_KEEP_ALIVE, Const::CONTENT_LENGTH, Const::CONTENT_LENGTH2, Const::CONTENT_LENGTH_S, Const::CONTINUE, Const::DQUOTE, Const::EARLY_HINTS, Const::ERROR_RESPONSE, Const::GATEWAY_INTERFACE, Const::HALT_COMMAND, Const::HEAD, Const::HIJACK, Const::HIJACK_IO, Const::HIJACK_P, Const::HTTP, Const::HTTPS, Const::HTTPS_KEY, Const::HTTP_10_200, Const::HTTP_11, Const::HTTP_11_100, Const::HTTP_11_200, Const::HTTP_CONNECTION, Const::HTTP_EXPECT, Const::HTTP_HEADER_DELIMITER, Const::HTTP_HOST, Const::HTTP_VERSION, Const::HTTP_X_FORWARDED_FOR, Const::HTTP_X_FORWARDED_PROTO, Const::HTTP_X_FORWARDED_SCHEME, Const::HTTP_X_FORWARDED_SSL, Const::IANA_HTTP_METHODS, Const::ILLEGAL_HEADER_KEY_REGEX, Const::ILLEGAL_HEADER_VALUE_REGEX, Const::KEEP_ALIVE, Const::LINE_END, Const::LOCALHOST, Const::LOCALHOST_IPV4, Const::LOCALHOST_IPV6, Const::MAX_BODY, Const::MAX_HEADER, Const::NEWLINE, Const::PATH_INFO, Const::PORT_443, Const::PORT_80, Const::PROXY_PROTOCOL_V1_REGEX, Const::PUMA_CONFIG, Const::PUMA_PEERCERT, Const::PUMA_SERVER_STRING, Const::PUMA_SOCKET, Const::PUMA_TMP_BASE, Const::PUMA_VERSION, Const::QUERY_STRING, Const::RACK_AFTER_REPLY, Const::RACK_INPUT, Const::RACK_RESPONSE_FINISHED, Const::RACK_URL_SCHEME, Const::REMOTE_ADDR, Const::REQUEST_METHOD, Const::REQUEST_PATH, Const::REQUEST_URI, Const::RESTART_COMMAND, Const::SERVER_NAME, Const::SERVER_PORT, Const::SERVER_PROTOCOL, Const::SERVER_SOFTWARE, Const::STOP_COMMAND, Const::SUPPORTED_HTTP_METHODS, Const::TRANSFER_ENCODING, Const::TRANSFER_ENCODING2, Const::TRANSFER_ENCODING_CHUNKED, Const::UNMASKABLE_HEADERS, Const::UNSPECIFIED_IPV4, Const::UNSPECIFIED_IPV6, Const::WRITE_TIMEOUT
Instance Method Summary collapse
-
#handle_request(processor, client, requests) ⇒ :close, ...
Takes the request contained in
client, invokes the Rack application to construct the response and writes it back toclient.io. - #illegal_header_key?(header_key) ⇒ Boolean
- #illegal_header_value?(header_value) ⇒ Boolean
-
#prepare_response(status, headers, res_body, requests, client) ⇒ :close, ...
Assembles the headers and prepares the body for actually sending the response via ‘#fast_write_response`.
Instance Method Details
#handle_request(processor, client, requests) ⇒ :close, ...
Takes the request contained in client, invokes the Rack application to construct the response and writes it back to client.io.
It’ll return :close when the connection is closed, this doesn’t mean that the response wasn’t successful.
It’ll return :keep_alive if the connection is a pipeline or keep-alive connection. Which may contain additional requests.
It’ll return :async if the connection remains open but will be handled elsewhere, i.e. the connection has been hijacked by the Rack application.
Finally, it’ll return true on keep-alive connections.
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/puma/response.rb', line 53 def handle_request(processor, client, requests) env = client.env io_buffer = client.io_buffer socket = client.io # io may be a MiniSSL::Socket app_body = nil error = nil return :close if closed_socket?(socket) if @early_hints env[EARLY_HINTS] = lambda { |headers| begin unless (str = str_early_hints headers).empty? fast_write_str socket, "HTTP/1.1 103 Early Hints\r\n#{str}\r\n" end rescue ConnectionError => e @log_writer.debug_error e # noop, if we lost the socket we just won't send the early hints end } end env["puma.mark_as_io_bound"] = -> { processor.mark_as_io_thread! } begin status, headers, app_body = @thread_pool.with_force_shutdown do @app.call(env) end # app_body needs to always be closed, hold value in case lowlevel_error # is called res_body = app_body # full hijack, app called env['rack.hijack'] return :async if client.hijacked status = status.to_i if status == -1 unless headers.empty? and res_body == [] raise "async response must have empty headers and body" end return :async end rescue ThreadPool::ForceShutdown => error @log_writer.unknown_error error, client, "Rack app" @log_writer.log "Detected force shutdown of a thread" status, headers, res_body = lowlevel_error(error, env, 503) rescue Exception => error @log_writer.unknown_error error, client, "Rack app" status, headers, res_body = lowlevel_error(error, env, 500) end prepare_response(status, headers, res_body, requests, client) ensure io_buffer.reset app_body.close if app_body.respond_to? :close client&.tempfile_close if after_reply = env[RACK_AFTER_REPLY] after_reply.each do |o| begin o.call rescue StandardError => e @log_writer.debug_error e end end end if response_finished = env[RACK_RESPONSE_FINISHED] response_finished.reverse_each do |o| begin o.call(env, status, headers, error) rescue StandardError => e @log_writer.debug_error e end end end end |
#illegal_header_key?(header_key) ⇒ Boolean
377 378 379 |
# File 'lib/puma/response.rb', line 377 def illegal_header_key?(header_key) !!(ILLEGAL_HEADER_KEY_REGEX =~ header_key.to_s) end |
#illegal_header_value?(header_value) ⇒ Boolean
384 385 386 |
# File 'lib/puma/response.rb', line 384 def illegal_header_value?(header_value) !!(ILLEGAL_HEADER_VALUE_REGEX =~ header_value.to_s) end |
#prepare_response(status, headers, res_body, requests, client) ⇒ :close, ...
Assembles the headers and prepares the body for actually sending the response via ‘#fast_write_response`.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'lib/puma/response.rb', line 144 def prepare_response(status, headers, res_body, requests, client) env = client.env socket = client.io io_buffer = client.io_buffer return :close if closed_socket?(socket) # Close the connection after a reasonable number of inline requests force_keep_alive = @enable_keep_alives && client.requests_served < @max_keep_alive 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 uncork_socket socket.flush return keep_alive ? :keep_alive : :close 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.flush response_hijack.call socket return :async end fast_write_response socket, body, io_buffer, chunked, content_length.to_i body.close if close_body # if we're shutting down, close keep_alive connections !shutting_down? && keep_alive ? :keep_alive : :close end |