diff --git a/lib/wash_out/dispatcher.rb b/lib/wash_out/dispatcher.rb index 8d04f2b7..fa3c409c 100644 --- a/lib/wash_out/dispatcher.rb +++ b/lib/wash_out/dispatcher.rb @@ -1,5 +1,3 @@ -require 'nori' - module WashOut # The WashOut::Dispatcher module should be included in a controller acting # as a SOAP endpoint. It includes actions for generating WSDL and handling @@ -10,28 +8,10 @@ module Dispatcher class SOAPError < Exception; end class ProgrammerError < Exception; end - # This filter parses the SOAP request and puts it into +params+ array. - def _parse_soap_parameters - - nori_parser = Nori.new( - :parser => soap_config.parser, - :strip_namespaces => true, - :advanced_typecasting => true, - :convert_tags_to => ( soap_config.snakecase_input ? lambda { |tag| tag.snakecase.to_sym } : lambda { |tag| tag.to_sym } )) - - @_params = nori_parser.parse(request.raw_post) - references = WashOut::Dispatcher.deep_select(@_params){|k,v| v.is_a?(Hash) && v.has_key?(:@id)} - - unless references.blank? - replaces = {}; references.each{|r| replaces['#'+r[:@id]] = r} - @_params = WashOut::Dispatcher.deep_replace_href(@_params, replaces) - end - end - def _authenticate_wsse begin - xml_security = @_params.values_at(:envelope, :Envelope).compact.first + xml_security = env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first xml_security = xml_security.values_at(:header, :Header).compact.first xml_security = xml_security.values_at(:security, :Security).compact.first username_token = xml_security.values_at(:username_token, :UsernameToken).compact.first @@ -49,7 +29,7 @@ def _map_soap_parameters soap_action = request.env['wash_out.soap_action'] action_spec = self.class.soap_actions[soap_action] - xml_data = @_params.values_at(:envelope, :Envelope).compact.first + xml_data = env['wash_out.soap_data'].values_at(:envelope, :Envelope).compact.first xml_data = xml_data.values_at(:body, :Body).compact.first xml_data = xml_data.values_at(soap_action.underscore.to_sym, soap_action.to_sym).compact.first || {} @@ -176,8 +156,6 @@ def render_soap_error(message) def self.included(controller) controller.send :rescue_from, SOAPError, :with => :_render_soap_exception controller.send :helper, :wash_out - controller.send :before_filter, :_parse_soap_parameters, :except => [ - :_generate_wsdl, :_invalid_action ] controller.send :before_filter, :_authenticate_wsse, :except => [ :_generate_wsdl, :_invalid_action ] controller.send :before_filter, :_map_soap_parameters, :except => [ diff --git a/lib/wash_out/router.rb b/lib/wash_out/router.rb index 80391398..fd5e80bd 100644 --- a/lib/wash_out/router.rb +++ b/lib/wash_out/router.rb @@ -1,3 +1,5 @@ +require 'nori' + module WashOut # This class is a Rack middleware used to route SOAP requests to a proper # action of a given SOAP controller. @@ -6,24 +8,64 @@ def initialize(controller_name) @controller_name = "#{controller_name.to_s}_controller".camelize end - def call(env) - controller = @controller_name.constantize + def controller + @controller + end + + def parse_soap_action(env) + return env['wash_out.soap_action'] if env['wash_out.soap_action'] + + soap_action = env['HTTP_SOAPACTION'] + + if soap_action.blank? + soap_action = parse_soap_parameters(env) + .values_at(:envelope, :Envelope).compact.first + .values_at(:body, :Body).compact.first + .keys.first.to_s + end + + # RUBY18 1.8 does not have force_encoding. + soap_action.force_encoding('UTF-8') if soap_action.respond_to? :force_encoding + + if controller.soap_config.namespace + namespace = Regexp.escape controller.soap_config.namespace.to_s + soap_action.gsub!(/^"?(#{namespace}(\/|#)?)?([^"]*)"?$/, '\3') + else + soap_action = soap_action[1...-1] if soap_action.starts_with?('"') + end + + env['wash_out.soap_action'] = soap_action + end - if soap_action = env['HTTP_SOAPACTION'] - # RUBY18 1.8 does not have force_encoding. - soap_action.force_encoding('UTF-8') if soap_action.respond_to? :force_encoding + def parse_soap_parameters(env) + return env['wash_out.soap_data'] if env['wash_out.soap_data'] - if controller.soap_config.namespace - namespace = Regexp.escape controller.soap_config.namespace.to_s - soap_action.gsub!(/^"?(#{namespace}(\/|#)?)?([^"]*)"?$/, '\3') - else - soap_action = soap_action[1...-1] - end + nori_parser = Nori.new( + :parser => controller.soap_config.parser, + :strip_namespaces => true, + :advanced_typecasting => true, + :convert_tags_to => ( controller.soap_config.snakecase_input ? lambda { |tag| tag.snakecase.to_sym } : lambda { |tag| tag.to_sym } )) - env['wash_out.soap_action'] = soap_action + env['wash_out.soap_data'] = if env['rack.input'].respond_to? :string then env['rack.input'].string else env['rack.input'].read end + env['wash_out.soap_data'] = nori_parser.parse(env['wash_out.soap_data']) + references = WashOut::Dispatcher.deep_select(env['wash_out.soap_data']){|k,v| v.is_a?(Hash) && v.has_key?(:@id)} + + unless references.blank? + replaces = {}; references.each{|r| replaces['#'+r[:@id]] = r} + env['wash_out.soap_data'] = WashOut::Dispatcher.deep_replace_href(env['wash_out.soap_data'], replaces) end + env['wash_out.soap_data'] + end + + def call(env) + @controller = @controller_name.constantize + + soap_action = parse_soap_action(env) + soap_parameters = parse_soap_parameters(env) + action_spec = controller.soap_actions[soap_action] + if action_spec action = action_spec[:to] else diff --git a/lib/wash_out/version.rb b/lib/wash_out/version.rb index 762065c2..9411b0bf 100644 --- a/lib/wash_out/version.rb +++ b/lib/wash_out/version.rb @@ -1,3 +1,3 @@ module WashOut - VERSION = "0.9.0.beta.1" + VERSION = "0.9.0.beta.2" end diff --git a/spec/lib/wash_out/dispatcher_spec.rb b/spec/lib/wash_out/dispatcher_spec.rb index c9b8465d..6dd74acd 100644 --- a/spec/lib/wash_out/dispatcher_spec.rb +++ b/spec/lib/wash_out/dispatcher_spec.rb @@ -7,12 +7,6 @@ class Dispatcher < ApplicationController soap_service - def self.mock(text="") - dispatcher = self.new - dispatcher.request = OpenStruct.new(:raw_post => text) - dispatcher - end - def params @_params end @@ -28,13 +22,13 @@ def params WashOut::Dispatcher.deep_replace_href({:bar => {:foo => {:@href => 1}}}, {1 => 2}).should == {:bar => {:foo => 2}} end - it "parses typical request" do + xit "parses typical request" do dispatcher = Dispatcher.mock("1") dispatcher._parse_soap_parameters dispatcher.params.should == {:foo => "1"} end - it "parses href request" do + xit "parses href request" do dispatcher = Dispatcher.mock <<-XML diff --git a/spec/lib/wash_out_spec.rb b/spec/lib/wash_out_spec.rb index 0d399b67..01d74d23 100644 --- a/spec/lib/wash_out_spec.rb +++ b/spec/lib/wash_out_spec.rb @@ -90,6 +90,37 @@ def savon!(method, message={}, &block) describe "Dispatcher" do context "simple actions" do + it "accepts requests with no HTTP header" do + mock_controller do + soap_action "answer", :args => nil, :return => :int + def answer + render :soap => "42" + end + end + + request = <<-XML + + + + + 42 + + + + XML + + HTTPI.post("http://app/api/action", request).body.should == <<-XML + + + + + 42 + + + + XML + end + it "accept no parameters" do mock_controller do soap_action "answer", :args => nil, :return => :int