#-- # Copyright (c) 2004 David Heinemeier Hansson # Copyright (c) 2006 Yugui <yugui@yugui.sakura.ne.jp> # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; version 2.1 # of the License # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # #-- # # AjpRailsRequest is based on lib/action_controller/cgi_process.rb which is # distributed as a port of actionpack 1.1.12. # The original file is avaiable here; # http://rubyforge.org/frs/?group_id=249&release_id=3791 # # The following is the permission notice of the original # lib/action_controller/cgi_process.rb, not for this script. # # -- # Copyright (c) 2004 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #++ require 'forwardable' require 'action_controller/cgi_ext/cgi_methods' class Net::AJP13::Request def cookies unless @cookies @cookies = {} cookie_strings = self.get_fields('cookie') cookie_strings.each do |cookie_string| @cookies.update(CGI::Cookie::parse(cookie_string)) end if cookie_strings @cookies.freeze end @cookies end attr_reader :output_cookies end # Wraps Net::AJP13::Request to adapt it to request object in rails. class AjpRailsRequest < ActionController::AbstractRequest DEFAULT_SESSION_OPTIONS = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.merge('session_key' => 'JSESSIONID') extend Forwardable def initialize(ajp_request, session_options, server_environments) @req = ajp_request @session_options = session_options @server_environments = server_environments @session_options['session_path'] ||= @server_environments['APP_LOCATION'] if server_environments['LOAD_BALANCE_ID'] and @req.cookies['JSESSIONID'] balance_id = server_environments['LOAD_BALANCE_ID'] @req.cookies['JSESSIONID'].each do |value| case value when String value.gsub!(/\.#{Regexp.escape(balance_id)}\Z/, '') when Array value.each do |entry| entry.gsub!(/\.#{Regexp.escape(balance_id)}\Z/, '') end end end end # simulates environment hash. @env = self.class.instance_method(:environment).bind(self) # Object#method is overridden by AbstractRequest#method class << @env def include?(key) !call(key).nil? end alias :key? :include? end end attr_reader :env attr_accessor :session_options def method @req.method.downcase.to_sym end def environment(name) case name when /^\AHTTP_(\w+)\Z/ name = $1.tr('_', '-') @req[name] else @req[name] or @server_environments[name] end end def raw_post if @req.body @req.body elsif @req.body_stream stream = @req.body_stream @req.body_stream = nil @req.body = stream.read else '' end end def path path = @req.path path = path.sub(%r!\A#{Regexp.escape(@server_environments['DISPATCHER_PREFIX'])}!, '') if @server_environments['DISPATCHER_PREFIX'] path = path.sub(%r!#{Regexp.escape(@server_environments['DISPATCHER_SUFFIX'])}\Z!, '') if @server_environments['DISPATCHER_SUFFIX'] path end def request_uri if @request_uri @request_uri else uri = path qs = query_string uri << '?' << qs[0] if qs @request_uri = uri end end def_delegators :@req, :ssl?, :cookies def server_software val = @req.get_attributes('server_software') val and val[0] and /([A-Za-z]+)/ =~ val[0] and $1.downcase end def query_string val = @req.get_attributes('query_string') val and val[0] end def query_parameters (qs = self.query_string).blank? ? {} : CGIMethods.parse_query_parameters(qs) end def post_params @post_params ||= CGI.parse(raw_post) @post_params end def request_parameters if formatted_post? CGIMethods.parse_formatted_request_parameters(post_format, raw_post) else CGIMethods.parse_request_parameters(self.post_params) end end def host @req['x-forwarded-host'] || ($1 if @req.server_name and /\A(.*):\d+\Z/ =~ @req.server_name) || (@req.server_name and @req.server_name.split(':').first) || ($1 if @req['host'] and /\A(.*):\d+\Z/ =~ @req['host']) || (@req['host'] and @req['host'].split(':').first) || '' end def port @req['x-forwarded-host'] ? standard_port : (port_from_http_host || @req.server_port) end def port_from_http_host $1.to_i if @req['host'] && /:(\d+)$/ =~ @req['host'] end def session unless @session if @session_options == false @session = Hash.new else stale_session_check! do if session_options_with_string_keys['new_session'] == true @session = new_session else @session = CGI::Session.new(@req, session_options_with_string_keys) end @session['__valid_session'] end end end @session end def reset_session @session.delete if CGI::Session === @session @session = new_session end private # Delete an old session if it exists then create a new one. def new_session if @session_options == false Hash.new else CGI::Session.new(@req, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil CGI::Session.new(@req, session_options_with_string_keys.merge("new_session" => true)) end end def stale_session_check! yield rescue ArgumentError => argument_error if argument_error.message =~ %r{undefined class/module (\w+)} begin Module.const_missing($1) rescue LoadError, NameError => const_error raise ActionController::SessionRestoreError, <<end_msg Session contains objects whose class definition isn't available. Remember to require the classes for all objects kept in the session. (Original exception: #{const_error.message} [#{const_error.class}]) end_msg end retry else raise end end def session_options_with_string_keys @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).inject({}) { |options, (k,v)| options[k.to_s] = v; options } end end class AjpRailsResponse < ActionController::AbstractResponse def to_ajp_response(extra_cookies = nil) raise "Unrecognized status line #{headers['Status']}" unless md = /\A(\d{3})(?: (.+))?/.match(headers['Status']) res = md[2] ? Net::AJP13::Response.new(md[1].to_i, :reason_phrase => md[2]) : Net::AJP13::Response.new(md[1]) @headers.each do |key, value| case key.downcase when 'status' # do nothing when 'expires' res.add_field(key, CGI::rfc1123_date(options.delete(key))) when 'cookie' case value when String res.add_field('Set-Cookie', value) when CGI::Cookie res.add_field('Set-Cookie', value.to_s) when Array value.each {|cookie| res.add_field('Set-Cookie', cookie) } when Hash value.each_value {|cookie| res.add_field('Set-Cookie', cookie) } end else res.add_field(key, value.to_s) if key != 'Status' end end res['content-type'] ||= 'text/html' if extra_cookies extra_cookies.each do |cookie| res.add_field('Set-Cookie', cookie.to_s) end end if @body.respond_to?(:call) buf = '' @body.call(self, StringIO.new(buf)) res.body = buf else res.body = self.body end return res end end