Class: WEBrick::HTTPResponse
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Exceptions:
| |
Inherits: | Object |
Defined in: | lib/webrick/httpresponse.rb |
Overview
An HTTP response. This is filled in by the service or do_* methods of a ::WEBrick
HTTP Servlet.
Class Method Summary
-
.new(config) ⇒ HTTPResponse
constructor
Creates a new HTTP response object.
Instance Attribute Summary
-
#body
rw
Body may be: * a String; * an IO-like object that responds to
#read
and#readpartial
; * a Proc-like object that responds to#call
. -
#chunked=(val)
rw
Enables chunked transfer encoding.
-
#chunked? ⇒ Boolean
rw
Will this response body be returned using chunked transfer-encoding?
-
#config
readonly
Configuration for this response.
-
#content_length
rw
The content-length header.
-
#content_length=(len)
rw
Sets the content-length header to
len
-
#content_type
rw
The content-type header.
-
#content_type=(type)
rw
Sets the content-type header to
type
-
#cookies
readonly
Response cookies.
-
#filename
rw
Filename of the static file in this response.
-
#header
readonly
Response header.
-
#http_version
readonly
HTTP Response version.
-
#keep_alive
rw
Is this a keep-alive response?
-
#keep_alive? ⇒ Boolean
rw
Will this response’s connection be kept alive?
-
#reason_phrase
rw
Response reason phrase (“OK”).
-
#request_http_version
rw
Request HTTP version for this response.
-
#request_method
rw
Request method for this response.
-
#request_uri
rw
Request URI for this response.
-
#sent_size
readonly
Bytes sent in this response.
-
#status
rw
Response status code (200).
-
#status=(status)
rw
Sets the response’s status to the #status code.
Instance Method Summary
-
#[](field)
Retrieves the response header
field
-
#[]=(field, value)
Sets the response header
field
tovalue
-
#each
Iterates over each header in the response.
-
#set_error(ex, backtrace = false)
Creates an error page for exception
ex
with an optionalbacktrace
-
#set_redirect(status, url)
Redirects to
url
with aHTTPStatus::Redirect
#status. -
#status_line
The response’s HTTP status line.
- #check_header(header_value) private
-
#_write_data(socket, data)
Internal use only
preserved for compatibility with some 3rd-party handlers.
- #make_body_tempfile Internal use only
- #remove_body_tempfile Internal use only
-
#send_body(socket)
Internal use only
Sends the body on
socket
-
#send_header(socket)
Internal use only
Sends the headers on
socket
-
#send_response(socket)
Internal use only
Sends the response on
socket
-
#setup_header
Internal use only
Sets up the headers for sending.
- #error_body(backtrace, ex, host, port) private Internal use only
- #send_body_io(socket) private Internal use only
- #send_body_proc(socket) private Internal use only
- #send_body_string(socket) private Internal use only
Constructor Details
.new(config) ⇒ HTTPResponse
Creates a new HTTP response object. Config::HTTP is the default configuration.
# File 'lib/webrick/httpresponse.rb', line 112
def initialize(config) @config = config @buffer_size = config[:OutputBufferSize] @logger = config[:Logger] @header = Hash.new @status = HTTPStatus::RC_OK @reason_phrase = nil @http_version = HTTPVersion::convert(@config[:HTTPVersion]) @body = '' @keep_alive = true @cookies = [] @request_method = nil @request_uri = nil @request_http_version = @http_version # temporary @chunked = false @filename = nil @sent_size = 0 @bodytempfile = nil end
Instance Attribute Details
#body (rw)
Body may be:
-
a String;
-
an IO-like object that responds to
#read
and#readpartial
; -
a Proc-like object that responds to
#call
.
In the latter case, either #chunked= should be set to true
, or header['content-length']
explicitly provided. Example:
server.mount_proc '/' do |req, res|
res.chunked = true
# or
# res.header['content-length'] = 10
res.body = proc { |out| out.write(Time.now.to_s) }
end
# File 'lib/webrick/httpresponse.rb', line 70
attr_accessor :body
#chunked=(val) (rw)
Enables chunked transfer encoding.
# File 'lib/webrick/httpresponse.rb', line 209
def chunked=(val) @chunked = val ? true : false end
#chunked? ⇒ Boolean
(rw)
Will this response body be returned using chunked transfer-encoding?
# File 'lib/webrick/httpresponse.rb', line 202
def chunked? @chunked end
#config (readonly)
Configuration for this response
# File 'lib/webrick/httpresponse.rb', line 101
attr_reader :config
#content_length (rw)
The content-length header
# File 'lib/webrick/httpresponse.rb', line 165
def content_length if len = self['content-length'] return Integer(len) end end
#content_length=(len) (rw)
Sets the content-length header to len
# File 'lib/webrick/httpresponse.rb', line 174
def content_length=(len) self['content-length'] = len.to_s end
#content_type (rw)
The content-type header
# File 'lib/webrick/httpresponse.rb', line 181
def content_type self['content-type'] end
#content_type=(type) (rw)
Sets the content-type header to type
# File 'lib/webrick/httpresponse.rb', line 188
def content_type=(type) self['content-type'] = type end
#cookies (readonly)
Response cookies
# File 'lib/webrick/httpresponse.rb', line 46
attr_reader :
#filename (rw)
Filename of the static file in this response. Only used by the FileHandler servlet.
# File 'lib/webrick/httpresponse.rb', line 91
attr_accessor :filename
#header (readonly)
Response header
# File 'lib/webrick/httpresponse.rb', line 41
attr_reader :header
#http_version (readonly)
HTTP Response version
# File 'lib/webrick/httpresponse.rb', line 31
attr_reader :http_version
#keep_alive (rw)
Is this a keep-alive response?
# File 'lib/webrick/httpresponse.rb', line 96
attr_accessor :keep_alive
#keep_alive? ⇒ Boolean
(rw)
Will this response’s connection be kept alive?
# File 'lib/webrick/httpresponse.rb', line 216
def keep_alive? @keep_alive end
#reason_phrase (rw)
Response reason phrase (“OK”)
# File 'lib/webrick/httpresponse.rb', line 51
attr_accessor :reason_phrase
#request_http_version (rw)
Request HTTP version for this response
# File 'lib/webrick/httpresponse.rb', line 85
attr_accessor :request_http_version
#request_method (rw)
Request method for this response
# File 'lib/webrick/httpresponse.rb', line 75
attr_accessor :request_method
#request_uri (rw)
Request URI for this response
# File 'lib/webrick/httpresponse.rb', line 80
attr_accessor :request_uri
#sent_size (readonly)
Bytes sent in this response
# File 'lib/webrick/httpresponse.rb', line 106
attr_reader :sent_size
#status (rw)
Response status code (200)
# File 'lib/webrick/httpresponse.rb', line 36
attr_reader :status
#status=(status) (rw)
Sets the response’s status to the #status code
# File 'lib/webrick/httpresponse.rb', line 142
def status=(status) @status = status @reason_phrase = HTTPStatus::reason_phrase(status) end
Instance Method Details
#[](field)
Retrieves the response header field
# File 'lib/webrick/httpresponse.rb', line 150
def [](field) @header[field.downcase] end
#[]=(field, value)
Sets the response header field
to value
# File 'lib/webrick/httpresponse.rb', line 157
def []=(field, value) @chunked = value.to_s.downcase == 'chunked' if field.downcase == 'transfer-encoding' @header[field.downcase] = value.to_s end
#_write_data(socket, data)
preserved for compatibility with some 3rd-party handlers
# File 'lib/webrick/httpresponse.rb', line 557
def _write_data(socket, data) socket << data end
#check_header(header_value) (private)
[ GitHub ]# File 'lib/webrick/httpresponse.rb', line 410
def check_header(header_value) header_value = header_value.to_s if /[\r\n]/ =~ header_value raise InvalidHeader else header_value end end
#each
Iterates over each header in the response
# File 'lib/webrick/httpresponse.rb', line 195
def each @header.each{|field, value| yield(field, value) } end
#error_body(backtrace, ex, host, port) (private)
# File 'lib/webrick/httpresponse.rb', line 421
def error_body(backtrace, ex, host, port) @body = '' @body << <<-_end_of_html_ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> <HTML> <HEAD><TITLE>#{HTMLUtils::escape(@reason_phrase)}</TITLE></HEAD> <BODY> <H1>#{HTMLUtils::escape(@reason_phrase)}</H1> #{HTMLUtils::escape(ex. )} <HR> _end_of_html_ if backtrace && $DEBUG @body << "backtrace of `#{HTMLUtils::escape(ex.class.to_s)}' " @body << "#{HTMLUtils::escape(ex. )}" @body << "<PRE>" ex.backtrace.each{|line| @body << "\t#{line}\n"} @body << "</PRE><HR>" end @body << <<-_end_of_html_ <ADDRESS> #{HTMLUtils::escape(@config[:ServerSoftware])} at #{host}:#{port} </ADDRESS> </BODY> </HTML> _end_of_html_ end
#make_body_tempfile
# File 'lib/webrick/httpresponse.rb', line 303
def make_body_tempfile # :nodoc: return if @bodytempfile bodytempfile = Tempfile.create("webrick") if @body.nil? # nothing elsif @body.respond_to? :readpartial IO.copy_stream(@body, bodytempfile) @body.close elsif @body.respond_to? :call @body.call(bodytempfile) else bodytempfile.write @body end bodytempfile.rewind @body = @bodytempfile = bodytempfile @header['content-length'] = bodytempfile.stat.size.to_s end
#remove_body_tempfile
# File 'lib/webrick/httpresponse.rb', line 321
def remove_body_tempfile # :nodoc: if @bodytempfile @bodytempfile.close File.unlink @bodytempfile.path @bodytempfile = nil end end
#send_body(socket)
Sends the body on socket
# File 'lib/webrick/httpresponse.rb', line 356
def send_body(socket) # :nodoc: if @body.respond_to? :readpartial then send_body_io(socket) elsif @body.respond_to?(:call) then send_body_proc(socket) else send_body_string(socket) end end
#send_body_io(socket) (private)
# File 'lib/webrick/httpresponse.rb', line 451
def send_body_io(socket) begin if @request_method == "HEAD" # do nothing elsif chunked? buf = '' begin @body.readpartial(@buffer_size, buf) size = buf.bytesize data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}" socket.write(data) data.clear @sent_size += size rescue EOFError break end while true buf.clear socket.write("0#{CRLF}#{CRLF}") else if %r{\Abytes (\d)-(\d)/\d+\z} =~ @header['content-range'] offset = $1.to_i size = $2.to_i - offset + 1 else offset = nil size = @header['content-length'] size = size.to_i if size end begin @sent_size = IO.copy_stream(@body, socket, size, offset) rescue NotImplementedError @body.seek(offset, IO::SEEK_SET) @sent_size = IO.copy_stream(@body, socket, size) end end ensure @body.close end remove_body_tempfile end
#send_body_proc(socket) (private)
# File 'lib/webrick/httpresponse.rb', line 513
def send_body_proc(socket) if @request_method == "HEAD" # do nothing elsif chunked? @body.call(ChunkedWrapper.new(socket, self)) socket.write("0#{CRLF}#{CRLF}") else size = @header['content-length'].to_i if @bodytempfile @bodytempfile.rewind IO.copy_stream(@bodytempfile, socket) else @body.call(socket) end @sent_size = size end end
#send_body_string(socket) (private)
# File 'lib/webrick/httpresponse.rb', line 491
def send_body_string(socket) if @request_method == "HEAD" # do nothing elsif chunked? body ? @body.bytesize : 0 while buf = @body[@sent_size, @buffer_size] break if buf.empty? size = buf.bytesize data = "#{size.to_s(16)}#{CRLF}#{buf}#{CRLF}" buf.clear socket.write(data) @sent_size += size end socket.write("0#{CRLF}#{CRLF}") else if @body && @body.bytesize > 0 socket.write(@body) @sent_size = @body.bytesize end end end
#send_header(socket)
Sends the headers on socket
# File 'lib/webrick/httpresponse.rb', line 333
def send_header(socket) # :nodoc: if @http_version.major > 0 data = status_line() @header.each{|key, value| tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase } data << "#{tmp}: #{check_header(value)}" << CRLF } @cookies.each{|| data << "Set-Cookie: " << check_header( .to_s) << CRLF } data << CRLF socket.write(data) end rescue InvalidHeader => e @header.clear @cookies.clear set_error e retry end
#send_response(socket)
Sends the response on socket
# File 'lib/webrick/httpresponse.rb', line 223
def send_response(socket) # :nodoc: begin setup_header() send_header(socket) send_body(socket) rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex @logger.debug(ex) @keep_alive = false rescue Exception => ex @logger.error(ex) @keep_alive = false end end
#set_error(ex, backtrace = false)
Creates an error page for exception ex
with an optional backtrace
# File 'lib/webrick/httpresponse.rb', line 383
def set_error(ex, backtrace=false) case ex when HTTPStatus::Status @keep_alive = false if HTTPStatus::error?(ex.code) self.status = ex.code else @keep_alive = false self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR end @header['content-type'] = "text/html; charset=ISO-8859-1" if respond_to?(:create_error_page) create_error_page() return end if @request_uri host, port = @request_uri.host, @request_uri.port else host, port = @config[:ServerName], @config[:Port] end error_body(backtrace, ex, host, port) end
#set_redirect(status, url)
Redirects to url
with a HTTPStatus::Redirect
#status.
Example:
res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
#setup_header
Sets up the headers for sending
# File 'lib/webrick/httpresponse.rb', line 240
def setup_header() # :nodoc: @reason_phrase ||= HTTPStatus::reason_phrase(@status) @header['server'] ||= @config[:ServerSoftware] @header['date'] ||= Time.now.httpdate # HTTP/0.9 features if @request_http_version < "1.0" @http_version = HTTPVersion.new("0.9") @keep_alive = false end # HTTP/1.0 features if @request_http_version < "1.1" if chunked? @chunked = false ver = @request_http_version.to_s msg = "chunked is set for an HTTP/#{ver} request. (ignored)" @logger.warn(msg) end end # Determine the message length (RFC2616 -- 4.4 Message Length) if @status == 304 || @status == 204 || HTTPStatus::info?(@status) @header.delete('content-length') @body = "" elsif chunked? @header["transfer-encoding"] = "chunked" @header.delete('content-length') elsif %r{^multipart/byteranges} =~ @header['content-type'] @header.delete('content-length') elsif @header['content-length'].nil? if @body.respond_to? :readpartial elsif @body.respond_to? :call make_body_tempfile else @header['content-length'] = (@body ? @body.bytesize : 0).to_s end end # Keep-Alive connection. if @header['connection'] == "close" @keep_alive = false elsif keep_alive? if chunked? || @header['content-length'] || @status == 304 || @status == 204 || HTTPStatus.info?(@status) @header['connection'] = "Keep-Alive" else msg = "Could not determine content-length of response body. Set content-length of the response or set Response#chunked = true" @logger.warn(msg) @header['connection'] = "close" @keep_alive = false end else @header['connection'] = "close" end # Location is a single absoluteURI. if location = @header['location'] if @request_uri @header['location'] = @request_uri.merge(location).to_s end end end
#status_line
The response’s HTTP status line
# File 'lib/webrick/httpresponse.rb', line 135
def status_line "HTTP/#@http_version #@status #@reason_phrase".rstrip << CRLF end