#
# httpresponse.rb -- HTTPResponse Class
#
# Author: IPR -- Internet Programming with Ruby -- writers
# Copyright (C) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
#
# $IPR: httpresponse.rb,v 1.19 2002/02/05 20:20:31 gotoyuzo Exp $

require 'webrick/htmlutils'
require 'webrick/httputils'
require 'webrick/httpdate'
require 'webrick/httpstatus'

module WEBrick
  class HTTPResponse

    attr_reader :status
    attr_accessor :http_version
    attr_accessor :reason_phrase
    attr_reader :header
    attr_accessor :body
    attr_reader :cookies

    attr_reader :config

    def initialize(config)
      @config = config
      @header = Hash.new
      @status = HTTPStatus::RC_OK
      @reason_phrase = nil
      @http_version = @config[:HTTPVersion]
      @body = ''
      @keep_conn = true
      @cookies = []
    end

    def status=(status)
      @status = status
      @reason_phrase = HTTPStatus::reason_phrase(status)
    end

    def [](field)
      @header[field.downcase]
    end

    def []=(field, value)
      @header[field.downcase] = value.to_s
    end

    def send_response(socket)
      send_header(socket) && send_body(socket)
    end

    def send_header(socket)
      @header['server'] ||= @config[:ServerSoftware]
      @header['content-length'] = @body ? @body.size : 0.to_s
      @header['date'] ||= HTTPDate.time2s(Time.now)
      @header['connection'] = "close" unless @keep_conn

      if /^(\d+)/ =~ @header['status']
        self.status = $1.to_i
        @header.delete('status')
      end
      unless @reason_phrase
        @reason_phrase = HTTPStatus::reason_phrase(@status)
      end

      h = "HTTP/#@http_version #@status #@reason_phrase" + CRLF
      @header.each{|key, value|
        ktmp = key.gsub(/^\w|-\w/){ |s| s.upcase }
        h << "#{ktmp}: #{value}" << CRLF
      }
      @cookies.each{|cookie|
        h << "Set-Cookie: " << cookie.to_s << CRLF
      }
      h << CRLF
      _write_data(socket, h)
    end

    def send_body(socket)
      if @body && @body.size > 0
        _write_data(socket, @body)
      end
    end

    def _write_data(socket, data)
      begin
        socket << data
        return true
      rescue Errno::EPIPE
        @status = HTTPStatus::RC_INTERNAL_SERVER_ERROR
        @reason_phrase = "EPIPE occured"
        @keep_conn = false
        return false
      end
    end
    private :_write_data

    def to_s
      ret = ""
      send_response(ret)
      ret
    end

    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

    def set_error(ex, backtrace=false)
      case ex
      when HTTPStatus::Status 
        @keep_conn = false if HTTPStatus::error?(ex.code)
        self.status = ex.code
      else 
        @keep_conn = false
        self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR
      end
      @header['content-type'] = "text/html"

      if respond_to?(:create_error_page)
        create_error_page()
        return
      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.type.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
     #{@config[:ServerName]}:#{@config[:Port]}
    </ADDRESS>
  </BODY>
</HTML>
      _end_of_html_
    end

    def keep_alive?
      @keep_conn
    end

    def keep_alive=(val)
      @keep_conn = val
    end

  end
end
