note
	description : "Objects that ..."
	author      : "$Author$"
	date        : "$Date$"
	revision    : "$Revision$"

class
	WSF_FILE_RESPONSE

inherit
	WSF_RESPONSE_MESSAGE

create
	make,
	make_with_content_type,
	make_html

feature {NONE} -- Initialization

	make (a_file_name: READABLE_STRING_8)
		do
			file_name := a_file_name
			get_content_type
			initialize
		end

	make_with_content_type (a_content_type: READABLE_STRING_8; a_filename: READABLE_STRING_8)
			-- Initialize `Current'.
		do
			file_name := a_filename
			content_type := a_content_type
			initialize
		end

	make_html (a_filename: READABLE_STRING_8)
			-- Initialize `Current'.
		do
			make_with_content_type ({HTTP_MIME_TYPES}.text_html, a_filename)
		end

	initialize
		local
			h: like header
		do
			get_file_exists
			create h.make
			header := h
			h.put_content_type (content_type)

			if file_exists then
				if attached file_last_modified as dt then
					h.put_last_modified (dt)
				end
				get_file_size
				if file_size = 0 then
					set_status_code ({HTTP_STATUS_CODE}.not_found)
				else
					set_status_code ({HTTP_STATUS_CODE}.ok)
				end
			else
				set_status_code ({HTTP_STATUS_CODE}.not_found)
			end
			update_content_length
		end

	update_content_length
		local
			n: INTEGER
		do
			if file_exists then
				n := file_size
				if attached head as h then
					n := n + h.count
				end
				if attached bottom as b then
					n := n + b.count
				end
			else
				n := 0
			end
			content_length := n
			header.put_content_length (n)
		end

feature -- Element change

	set_expires_in_seconds (sec: INTEGER)
		do
			set_expires (sec.out)
		end

	set_expires (s: STRING)
		do
			header.put_expires_string (s)
		end

	set_no_cache
		local
			h: like header
		do
			h := header
			h.put_expires (0)
			h.put_cache_control ("no-cache, must-revalidate")
			h.put_pragma_no_cache
		end

feature -- Access

	status_code: INTEGER assign set_status_code

	header: HTTP_HEADER

	content_length: INTEGER
			-- Content-Length of the response

	content_type: READABLE_STRING_8
			-- Content-Type of the response

	file_name: READABLE_STRING_8

	file_exists: BOOLEAN
			-- File exists?

	file_size: INTEGER
			-- Size of file named `file_name'

	head, bottom: detachable READABLE_STRING_8
			-- Eventual head and bottom part
			-- before and after the file content.

feature -- Settings

	answer_head_request_method: BOOLEAN assign set_answer_head_request_method
			-- For HEAD request method, only http header should be sent

feature -- Element change

	set_status_code (c: like status_code)
			-- Set `status_code' to `c'.
		require
			valid_status_code: c > 0
		do
			status_code := c
		ensure
			status_code_set: status_code = c
		end

	set_answer_head_request_method (b: BOOLEAN)
			-- Set answer_head_request_method' to `b'.
		do
			answer_head_request_method := b
		end

	set_head (s: like head)
			-- Set `head' to `s'
			-- it also change the `content_length' and associated value in `header'
		do
			head := s
			update_content_length
		end

	set_bottom (s: like bottom)
			-- Set `bottom' to `s'	
			-- it also change the `content_length' and associated value in `header'
		do
			bottom := s
			update_content_length
		end

feature {WSF_RESPONSE} -- Output

	send_to (res: WSF_RESPONSE)
		local
			s: detachable READABLE_STRING_8
		do
			res.set_status_code (status_code)
			if status_code = {HTTP_STATUS_CODE}.not_found then
			else
				res.put_header_text (header.string)
				s := head
				if s /= Void then
					res.put_string (s)
				end
				if not answer_head_request_method then
					send_file_content_to (file_name, res)
				end
				s := bottom
				if s /= Void then
					res.put_string (s)
				end
			end
		end

feature {NONE} -- Implementation: file system helper

	get_file_exists
			-- Get `file_exists'
		local
			f: RAW_FILE
		do
			create f.make (file_name)
			file_exists := f.exists
		end

	get_file_size
			-- Get `file_size' from file named `file_name'
		require
			file_exists: file_exists
		local
			f: RAW_FILE
		do
			create f.make (file_name)
			file_size := f.count
		end

	file_last_modified: detachable DATE_TIME
			-- Get `file_size' from file named `file_name'
		require
			file_exists: file_exists
		local
			f: RAW_FILE
		do
			create f.make (file_name)
			create Result.make_from_epoch (f.change_date)
		end

	file_extension (fn: STRING): STRING
			-- Extension of file `fn'.
		local
			p: INTEGER
		do
			p := fn.last_index_of ('.', fn.count)
			if p > 0 then
				Result := fn.substring (p + 1, fn.count)
			else
				create Result.make_empty
			end
		end

feature -- Content-type related

	get_content_type
			-- Content type associated with `file_name'
		local
			m_map: HTTP_FILE_EXTENSION_MIME_MAPPING
			m: detachable READABLE_STRING_8
		do
			create m_map.make_default
			m := m_map.mime_type (file_extension (file_name).as_lower)
			if m = Void then
				m := {HTTP_MIME_TYPES}.application_force_download
			end
			content_type := m
		end

feature {NONE} -- Implementation: output

	send_file_content_to (fn: READABLE_STRING_8; res: WSF_RESPONSE)
			-- Send the content of file `fn'
		require
			string_not_empty: not fn.is_empty
			is_readable: (create {RAW_FILE}.make (fn)).is_readable
			file_exists: file_exists
		local
			f: RAW_FILE
		do
			create f.make (fn)
			check f.is_readable end

			f.open_read
			from
			until
				f.exhausted
			loop
				f.read_stream (4_096)
				res.put_string (f.last_string)
			end
			f.close
		end

note
	copyright: "2011-2012, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Eiffel Software and others"
	license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
	source: "[
			Eiffel Software
			5949 Hollister Ave., Goleta, CA 93117 USA
			Telephone 805-685-1006, Fax 805-685-6869
			Website http://www.eiffel.com
			Customer support http://support.eiffel.com
		]"
end
