Skip to content

Commit

Permalink
Routing reimplented with the fallback to payload parsing (if no SOAPA…
Browse files Browse the repository at this point in the history
…CTION header given) - fixes #117
  • Loading branch information
inossidabile committed Sep 29, 2013
1 parent c6d5a8c commit 491f2f4
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 45 deletions.
26 changes: 2 additions & 24 deletions lib/wash_out/dispatcher.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand All @@ -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 || {}
Expand Down Expand Up @@ -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 => [
Expand Down
66 changes: 54 additions & 12 deletions lib/wash_out/router.rb
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/wash_out/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module WashOut
VERSION = "0.9.0.beta.1"
VERSION = "0.9.0.beta.2"
end
10 changes: 2 additions & 8 deletions spec/lib/wash_out/dispatcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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("<foo>1</foo>")
dispatcher._parse_soap_parameters
dispatcher.params.should == {:foo => "1"}
end

it "parses href request" do
xit "parses href request" do
dispatcher = Dispatcher.mock <<-XML
<root>
<request>
Expand Down
31 changes: 31 additions & 0 deletions spec/lib/wash_out_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="false" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Body>
<tns:answer>
<value>42</value>
</tns:answer>
</env:Body>
</env:Envelope>
XML

HTTPI.post("http://app/api/action", request).body.should == <<-XML
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="false">
<soap:Body>
<tns:answerResponse>
<Value xsi:type="xsd:int">42</Value>
</tns:answerResponse>
</soap:Body>
</soap:Envelope>
XML
end

it "accept no parameters" do
mock_controller do
soap_action "answer", :args => nil, :return => :int
Expand Down

0 comments on commit 491f2f4

Please sign in to comment.