PageRenderTime 47ms CodeModel.GetById 11ms app.highlight 32ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_Main/External.LCA_RESTRICTED/Languages/Ruby/redist-libs/ruby/1.9.1/webrick/httpresponse.rb

#
Ruby | 326 lines | 268 code | 39 blank | 19 comment | 38 complexity | bb04edbc5566d4b60b2852b7e56685b1 MD5 | raw file
  1#
  2# httpresponse.rb -- HTTPResponse Class
  3#
  4# Author: IPR -- Internet Programming with Ruby -- writers
  5# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
  6# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
  7# reserved.
  8#
  9# $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $
 10
 11require 'time'
 12require 'webrick/httpversion'
 13require 'webrick/htmlutils'
 14require 'webrick/httputils'
 15require 'webrick/httpstatus'
 16
 17module WEBrick
 18  class HTTPResponse
 19    attr_reader :http_version, :status, :header
 20    attr_reader :cookies
 21    attr_accessor :reason_phrase
 22    attr_accessor :body
 23
 24    attr_accessor :request_method, :request_uri, :request_http_version
 25    attr_accessor :filename
 26    attr_accessor :keep_alive
 27    attr_reader :config, :sent_size
 28
 29    def initialize(config)
 30      @config = config
 31      @buffer_size = config[:OutputBufferSize]
 32      @logger = config[:Logger]
 33      @header = Hash.new
 34      @status = HTTPStatus::RC_OK
 35      @reason_phrase = nil
 36      @http_version = HTTPVersion::convert(@config[:HTTPVersion])
 37      @body = ''
 38      @keep_alive = true
 39      @cookies = []
 40      @request_method = nil
 41      @request_uri = nil
 42      @request_http_version = @http_version  # temporary
 43      @chunked = false
 44      @filename = nil
 45      @sent_size = 0
 46    end
 47
 48    def status_line
 49      "HTTP/#@http_version #@status #@reason_phrase #{CRLF}"
 50    end
 51
 52    def status=(status)
 53      @status = status
 54      @reason_phrase = HTTPStatus::reason_phrase(status)
 55    end
 56
 57    def [](field)
 58      @header[field.downcase]
 59    end
 60
 61    def []=(field, value)
 62      @header[field.downcase] = value.to_s
 63    end
 64
 65    def content_length
 66      if len = self['content-length']
 67        return Integer(len)
 68      end
 69    end
 70
 71    def content_length=(len)
 72      self['content-length'] = len.to_s
 73    end
 74
 75    def content_type
 76      self['content-type']
 77    end
 78
 79    def content_type=(type)
 80      self['content-type'] = type
 81    end
 82
 83    def each
 84      @header.each{|k, v|  yield(k, v) }
 85    end
 86
 87    def chunked?
 88      @chunked
 89    end
 90
 91    def chunked=(val)
 92      @chunked = val ? true : false
 93    end
 94
 95    def keep_alive?
 96      @keep_alive
 97    end
 98
 99    def send_response(socket)
100      begin
101        setup_header()
102        send_header(socket)
103        send_body(socket)
104      rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex
105        @logger.debug(ex)
106        @keep_alive = false
107      rescue Exception => ex
108        @logger.error(ex)
109        @keep_alive = false
110      end
111    end
112
113    def setup_header()
114      @reason_phrase    ||= HTTPStatus::reason_phrase(@status)
115      @header['server'] ||= @config[:ServerSoftware]
116      @header['date']   ||= Time.now.httpdate
117
118      # HTTP/0.9 features
119      if @request_http_version < "1.0"
120        @http_version = HTTPVersion.new("0.9")
121        @keep_alive = false
122      end
123
124      # HTTP/1.0 features
125      if @request_http_version < "1.1"
126        if chunked?
127          @chunked = false
128          ver = @request_http_version.to_s
129          msg = "chunked is set for an HTTP/#{ver} request. (ignored)"
130          @logger.warn(msg)
131        end
132      end
133
134      # Determine the message length (RFC2616 -- 4.4 Message Length)
135      if @status == 304 || @status == 204 || HTTPStatus::info?(@status)
136        @header.delete('content-length')
137        @body = ""
138      elsif chunked?
139        @header["transfer-encoding"] = "chunked"
140        @header.delete('content-length')
141      elsif %r{^multipart/byteranges} =~ @header['content-type']
142        @header.delete('content-length')
143      elsif @header['content-length'].nil?
144        unless @body.is_a?(IO)
145          @header['content-length'] = @body ? @body.bytesize : 0
146        end
147      end
148
149      # Keep-Alive connection.
150      if @header['connection'] == "close"
151         @keep_alive = false
152      elsif keep_alive?
153        if chunked? || @header['content-length']
154          @header['connection'] = "Keep-Alive"
155        end
156      else
157        @header['connection'] = "close"
158      end
159
160      # Location is a single absoluteURI.
161      if location = @header['location']
162        if @request_uri
163          @header['location'] = @request_uri.merge(location)
164        end
165      end
166    end
167
168    def send_header(socket)
169      if @http_version.major > 0
170        data = status_line()
171        @header.each{|key, value|
172          tmp = key.gsub(/\bwww|^te$|\b\w/){ $&.upcase }
173          data << "#{tmp}: #{value}" << CRLF
174        }
175        @cookies.each{|cookie|
176          data << "Set-Cookie: " << cookie.to_s << CRLF
177        }
178        data << CRLF
179        _write_data(socket, data)
180      end
181    end
182
183    def send_body(socket)
184      case @body
185      when IO then send_body_io(socket)
186      else send_body_string(socket)
187      end
188    end
189
190    def to_s
191      ret = ""
192      send_response(ret)
193      ret
194    end
195
196    def set_redirect(status, url)
197      @body = "<HTML><A HREF=\"#{url.to_s}\">#{url.to_s}</A>.</HTML>\n"
198      @header['location'] = url.to_s
199      raise status
200    end
201
202    def set_error(ex, backtrace=false)
203      case ex
204      when HTTPStatus::Status
205        @keep_alive = false if HTTPStatus::error?(ex.code)
206        self.status = ex.code
207      else
208        @keep_alive = false
209        self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR
210      end
211      @header['content-type'] = "text/html; charset=ISO-8859-1"
212
213      if respond_to?(:create_error_page)
214        create_error_page()
215        return
216      end
217
218      if @request_uri
219        host, port = @request_uri.host, @request_uri.port
220      else
221        host, port = @config[:ServerName], @config[:Port]
222      end
223
224      @body = ''
225      @body << <<-_end_of_html_
226<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
227<HTML>
228  <HEAD><TITLE>#{HTMLUtils::escape(@reason_phrase)}</TITLE></HEAD>
229  <BODY>
230    <H1>#{HTMLUtils::escape(@reason_phrase)}</H1>
231    #{HTMLUtils::escape(ex.message)}
232    <HR>
233      _end_of_html_
234
235      if backtrace && $DEBUG
236        @body << "backtrace of `#{HTMLUtils::escape(ex.class.to_s)}' "
237        @body << "#{HTMLUtils::escape(ex.message)}"
238        @body << "<PRE>"
239        ex.backtrace.each{|line| @body << "\t#{line}\n"}
240        @body << "</PRE><HR>"
241      end
242
243      @body << <<-_end_of_html_
244    <ADDRESS>
245     #{HTMLUtils::escape(@config[:ServerSoftware])} at
246     #{host}:#{port}
247    </ADDRESS>
248  </BODY>
249</HTML>
250      _end_of_html_
251    end
252
253    private
254
255    def send_body_io(socket)
256      begin
257        if @request_method == "HEAD"
258          # do nothing
259        elsif chunked?
260          while buf = @body.read(@buffer_size)
261            next if buf.empty?
262            data = ""
263            data << format("%x", buf.bytesize) << CRLF
264            data << buf << CRLF
265            _write_data(socket, data)
266            @sent_size += buf.bytesize
267          end
268          _write_data(socket, "0#{CRLF}#{CRLF}")
269        else
270          size = @header['content-length'].to_i
271          _send_file(socket, @body, 0, size)
272          @sent_size = size
273        end
274      ensure
275        @body.close
276      end
277    end
278
279    def send_body_string(socket)
280      if @request_method == "HEAD"
281        # do nothing
282      elsif chunked?
283        remain = body ? @body.bytesize : 0
284        while buf = @body[@sent_size, @buffer_size]
285          break if buf.empty?
286          data = ""
287          data << format("%x", buf.bytesize) << CRLF
288          data << buf << CRLF
289          _write_data(socket, data)
290          @sent_size += buf.bytesize
291        end
292        _write_data(socket, "0#{CRLF}#{CRLF}")
293      else
294        if @body && @body.bytesize > 0
295          _write_data(socket, @body)
296          @sent_size = @body.bytesize
297        end
298      end
299    end
300
301    def _send_file(output, input, offset, size)
302      while offset > 0
303        sz = @buffer_size < size ? @buffer_size : size
304        buf = input.read(sz)
305        offset -= buf.bytesize
306      end
307
308      if size == 0
309        while buf = input.read(@buffer_size)
310          _write_data(output, buf)
311        end
312      else
313        while size > 0
314          sz = @buffer_size < size ? @buffer_size : size
315          buf = input.read(sz)
316          _write_data(output, buf)
317          size -= buf.bytesize
318        end
319      end
320    end
321
322    def _write_data(socket, data)
323      socket << data
324    end
325  end
326end