曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.かつては雑草やヨモギと共に雨や露を分かち合っていたが、今では松やヒノキと共に霜や雪に耐えている。曾与蒿藜同雨露,Once sharing rain and dew with weeds and wormwood, now enduring frost and snow with pines and cypresses.终随松柏到冰霜.曾与蒿藜同雨露한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.,终随松柏到冰霜.譖セ荳手珍阯懷酔髮ィ髴イ�檎サ磯囂譚セ譟丞芦蜀ー髴�曾与蒿藜同雨露,鏇句笌钂胯棞鍚岄洦闇诧紝缁堥殢鏉炬煆鍒板啺闇�终随松柏到冰霜.曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.曾与蒿藜同雨露,终随松柏到冰霜.
#
# httpresponse.rb -- HTTPResponse Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
# reserved.
#
# $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $
require 'time'
require 'webrick/httpversion'
require 'webrick/htmlutils'
require 'webrick/httputils'
require 'webrick/httpstatus'
module WEBrick
##
# An HTTP response. This is filled in by the service or do_* methods of a
# WEBrick HTTP Servlet.
class HTTPResponse
##
# HTTP Response version
attr_reader :http_version
##
# Response status code (200)
attr_reader :status
##
# Response header
attr_reader :header
##
# Response cookies
attr_reader :cookies
##
# Response reason phrase ("OK")
attr_accessor :reason_phrase
##
# Body may be a String or IO-like object that responds to #read and
# #readpartial.
attr_accessor :body
##
# Request method for this response
attr_accessor :request_method
##
# Request URI for this response
attr_accessor :request_uri
##
# Request HTTP version for this response
attr_accessor :request_http_version
##
# Filename of the static file in this response. Only used by the
# FileHandler servlet.
attr_accessor :filename
##
# Is this a keep-alive response?
attr_accessor :keep_alive
##
# Configuration for this response
attr_reader :config
##
# Bytes sent in this response
attr_reader :sent_size
##
# Creates a new HTTP response object. WEBrick::Config::HTTP is the
# default configuration.
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
end
##
# The response's HTTP status line
def status_line
"HTTP/#@http_version #@status #@reason_phrase #{CRLF}"
end
##
# Sets the response's status to the +status+ code
def status=(status)
@status = status
@reason_phrase = HTTPStatus::reason_phrase(status)
end
##
# Retrieves the response header +field+
def [](field)
@header[field.downcase]
end
##
# Sets the response header +field+ to +value+
def []=(field, value)
@header[field.downcase] = value.to_s
end
##
# The content-length header
def content_length
if len = self['content-length']
return Integer(len)
end
end
##
# Sets the content-length header to +len+
def content_length=(len)
self['content-length'] = len.to_s
end
##
# The content-type header
def content_type
self['content-type']
end
##
# Sets the content-type header to +type+
def content_type=(type)
self['content-type'] = type
end
##
# Iterates over each header in the response
def each
@header.each{|field, value| yield(field, value) }
end
##
# Will this response body be returned using chunked transfer-encoding?
def chunked?
@chunked
end
##
# Enables chunked transfer encoding.
def chunked=(val)
@chunked = val ? true : false
end
##
# Will this response's connection be kept alive?
def keep_alive?
@keep_alive
end
##
# Sends the response on +socket+
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
##
# Sets up the headers for sending
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?
unless @body.is_a?(IO)
@header['content-length'] = @body ? @body.bytesize : 0
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)
end
end
end
##
# Sends the headers on +socket+
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}: #{value}" << CRLF
}
@cookies.each{|cookie|
data << "Set-Cookie: " << cookie.to_s << CRLF
}
data << CRLF
_write_data(socket, data)
end
end
##
# Sends the body on +socket+
def send_body(socket) # :nodoc:
if @body.respond_to? :readpartial then
send_body_io(socket)
else
send_body_string(socket)
end
end
def to_s # :nodoc:
ret = ""
send_response(ret)
ret
end
##
# Redirects to +url+ with a WEBrick::HTTPStatus::Redirect +status+.
#
# Example:
#
# res.set_redirect WEBrick::HTTPStatus::TemporaryRedirect
def set_redirect(status, url)
@body = "<HTML><A HREF=\"#{url.to_s}\">#{url.to_s}</A>.</HTML>\n"
@header['location'] = url.to_s
raise status
end
##
# Creates an error page for exception +ex+ with an optional +backtrace+
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
@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.message)}
<HR>
_end_of_html_
if backtrace && $DEBUG
@body << "backtrace of `#{HTMLUtils::escape(ex.class.to_s)}' "
@body << "#{HTMLUtils::escape(ex.message)}"
@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
private
# :stopdoc:
def send_body_io(socket)
begin
if @request_method == "HEAD"
# do nothing
elsif chunked?
begin
buf = ''
data = ''
while true
@body.readpartial( @buffer_size, buf ) # there is no need to clear buf?
data << format("%x", buf.bytesize) << CRLF
data << buf << CRLF
_write_data(socket, data)
data.clear
@sent_size += buf.bytesize
end
rescue EOFError # do nothing
end
_write_data(socket, "0#{CRLF}#{CRLF}")
else
size = @header['content-length'].to_i
_send_file(socket, @body, 0, size)
@sent_size = size
end
ensure
@body.close
end
end
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?
data = ""
data << format("%x", buf.bytesize) << CRLF
data << buf << CRLF
_write_data(socket, data)
@sent_size += buf.bytesize
end
_write_data(socket, "0#{CRLF}#{CRLF}")
else
if @body && @body.bytesize > 0
_write_data(socket, @body)
@sent_size = @body.bytesize
end
end
end
def _send_file(output, input, offset, size)
while offset > 0
sz = @buffer_size < size ? @buffer_size : size
buf = input.read(sz)
offset -= buf.bytesize
end
if size == 0
while buf = input.read(@buffer_size)
_write_data(output, buf)
end
else
while size > 0
sz = @buffer_size < size ? @buffer_size : size
buf = input.read(sz)
_write_data(output, buf)
size -= buf.bytesize
end
end
end
def _write_data(socket, data)
socket << data
end
# :startdoc:
end
end