note description: "Handler to process CGI script." date: "$Date$" revision: "$Revision$" class WSF_CGI_HANDLER inherit WSF_EXECUTE_HANDLER create make feature {NONE} -- Creation make (a_dir: PATH) do working_direction := a_dir end feature -- Settings buffer_size: INTEGER = 1_024 working_direction: PATH feature -- Status report cgi_file_path (req: WSF_REQUEST): PATH local l_path_info: READABLE_STRING_32 do -- Path to CGI executable. l_path_info := req.path_info if l_path_info.starts_with_general ("/") then l_path_info := l_path_info.substring (2, l_path_info.count) end -- Process Result := working_direction.extended (l_path_info) end exists (req: WSF_REQUEST): BOOLEAN -- CGI file exists? do Result := (create {FILE_UTILITIES}).file_path_exists (cgi_file_path (req)) end feature -- Execution execute (req: WSF_REQUEST; res: WSF_RESPONSE) -- Execute `req' responding in `res'. local fut: FILE_UTILITIES l_exec_path: PATH proc: BASE_PROCESS l_input_env: STRING_TABLE [READABLE_STRING_GENERAL] l_input_header: detachable STRING l_input_buf: STRING l_output: STRING l_output_header_sent: BOOLEAN l_error: STRING s: STRING i, j, n: INTEGER do -- Header if attached req.raw_header_data as l_header then l_input_header := l_header end -- Input data create l_input_buf.make (req.content_length_value.to_integer_32) req.read_input_data_into (l_input_buf) -- Input environment create l_input_env.make (10) across req.meta_variables as ic loop if attached {WSF_STRING} ic.item as var then l_input_env.force (var.value, var.name) else check supported: False end end end -- No need to import `l_input_header` in environment -- As current connector already did the job. -- Process l_exec_path := cgi_file_path (req) if fut.file_path_exists (l_exec_path) then proc := (create {BASE_PROCESS_FACTORY}).process_launcher (l_exec_path.name, Void, working_direction.name) proc.set_hidden (True) proc.set_environment_variable_table (l_input_env) proc.set_separate_console (False) proc.redirect_input_to_stream proc.redirect_output_to_stream proc.redirect_error_to_stream -- Launch CGI execution proc.launch if proc.launched then -- Do not send the header to CGI script -- value are passed via environment variables -- proc.put_string (l_input_header) -- Send payload. proc.put_string (l_input_buf) create l_output.make_empty create l_error.make_empty get_output_and_error_from_process (proc, l_output, l_error) if l_error /= Void and then not l_error.is_whitespace then res.put_error (l_error) end -- Wait for process exit if not proc.has_exited then proc.wait_for_exit end if proc.exit_code /= 0 then res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error) s := "CGI script execution failed [exit code=" + proc.exit_code.out+ "]!" res.header.put_content_type_utf_8_text_plain res.header.put_content_length (s.count) res.put_string (s) else -- Send the response -- error already sent via `res.put_error (l_error)` from i := 1 n := l_output.count until i > n or l_output_header_sent loop j := l_output.index_of ('%N', i) if j > 0 then s := l_output.substring (i, j) s.right_adjust if s.is_empty then -- Reached end of header l_output_header_sent := True else res.add_header_line (s) end else -- ERROR l_output_header_sent := True end i := j + 1 end if l_output_header_sent then if i <= n then res.put_string (l_output.substring (i, n)) end else res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error) s := "Internal server error!" res.header.put_content_type_utf_8_text_plain res.header.put_content_length (s.count) res.put_string (s) end end else res.set_status_code ({HTTP_STATUS_CODE}.internal_server_error) s := "Could not launch CGI script!" res.header.put_content_type_utf_8_text_plain res.header.put_content_length (s.count) res.put_string (s) end else res.set_status_code ({HTTP_STATUS_CODE}.not_found) s := "Not found!" res.header.put_content_type_utf_8_text_plain res.header.put_content_length (s.count) res.put_string (s) end end get_output_and_error_from_process (proc: BASE_PROCESS; a_output: STRING; a_error: STRING) local output_buf, error_buf: SPECIAL [NATURAL_8] do from create output_buf.make_filled (0, buffer_size) create error_buf.make_filled (0, buffer_size) until not attached output_buf and not attached error_buf loop if attached output_buf then if proc.has_output_stream_error or proc.has_output_stream_closed then output_buf := Void end if attached output_buf then -- Try reading from standard output. proc.read_output_to_special (output_buf) across output_buf as ic loop a_output.append_code (ic.item) end end end if attached output_buf implies output_buf.count = 0 and then attached error_buf then -- Nothing is read from standard output, switch to standard error. if proc.has_error_stream_error or proc.has_error_stream_closed then error_buf := Void end if attached error_buf then -- Try reading from standard error. proc.read_error_to_special (error_buf) across error_buf as ic loop a_error.append_code (ic.item) end end end if attached output_buf then output_buf.extend_filled (0) end if attached error_buf then error_buf.extend_filled (0) end end end note copyright: "2011-2017, Jocelyn Fiat, Javier Velilla, Olivier Ligot, Colin Adams, 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