From aaea48fe9826b9e5d2d5b92795a297b8f238c58d Mon Sep 17 00:00:00 2001 From: Leon Breedt Date: Sat, 2 Apr 2005 21:03:36 +0000 Subject: * collapse 'ws' back into protocols, it just added complexity and indirection, and was hard to extend. * extract casting into seperate support file * ensure casting always does the right thing for return values, should fix interoperability issues with Ecto and possibly other XML-RPC clients * add functional unit tests for scaffolding * represent signature items with classes instead of symbols/Class objects, much more flexible * tweak logging to always show casted versions of parameters and return values, if possible. git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1072 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- .../action_web_service/protocol/soap_protocol.rb | 111 ++++++++++++++++++--- 1 file changed, 98 insertions(+), 13 deletions(-) (limited to 'actionwebservice/lib/action_web_service/protocol/soap_protocol.rb') diff --git a/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb index 5e56748ae3..cc3e90a4cc 100644 --- a/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb +++ b/actionwebservice/lib/action_web_service/protocol/soap_protocol.rb @@ -1,3 +1,5 @@ +require 'action_web_service/protocol/soap_protocol/marshaler' + module ActionWebService # :nodoc: module Protocol # :nodoc: module Soap # :nodoc: @@ -7,28 +9,105 @@ module ActionWebService # :nodoc: end class SoapProtocol < AbstractProtocol # :nodoc: - def initialize - @encoder = WS::Encoding::SoapRpcEncoding.new 'urn:ActionWebService' - @marshaler = WS::Marshaling::SoapMarshaler.new 'urn:ActionWebService' + def marshaler + @marshaler ||= SoapMarshaler.new + end + + def decode_action_pack_request(action_pack_request) + return nil unless has_valid_soap_action?(action_pack_request) + service_name = action_pack_request.parameters['action'] + decode_request(action_pack_request.raw_post, service_name) + end + + def encode_action_pack_request(service_name, public_method_name, raw_body, options={}) + request = super + request.env['HTTP_SOAPACTION'] = '/soap/%s/%s' % [service_name, public_method_name] + request end - def unmarshal_request(ap_request) - return nil unless has_valid_soap_action?(ap_request) - method_name, params = @encoder.decode_rpc_call(ap_request.raw_post) - params = params.map{|x| @marshaler.unmarshal(x)} - service_name = ap_request.parameters['action'] + def decode_request(raw_request, service_name) + envelope = SOAP::Processor.unmarshal(raw_request) + unless envelope + raise ProtocolError, "Failed to parse SOAP request message" + end + request = envelope.body.request + method_name = request.elename.name + params = request.collect{ |k, v| marshaler.soap_to_ruby(request[k]) } Request.new(self, method_name, params, service_name) end + def encode_request(method_name, params, param_types) + param_types.each{ |type| marshaler.register_type(type) } if param_types + qname = XSD::QName.new(marshaler.type_namespace, method_name) + param_def = [] + i = 0 + if param_types + params = params.map do |param| + param_type = param_types[i] + param_def << ['in', param_type.name, marshaler.lookup_type(param_type).mapping] + i += 1 + [param_type.name, marshaler.ruby_to_soap(param)] + end + else + params = [] + end + request = SOAP::RPC::SOAPMethodRequest.new(qname, param_def) + request.set_param(params) + envelope = create_soap_envelope(request) + SOAP::Processor.marshal(envelope) + end + + def decode_response(raw_response) + envelope = SOAP::Processor.unmarshal(raw_response) + unless envelope + raise ProtocolError, "Failed to parse SOAP request message" + end + method_name = envelope.body.request.elename.name + return_value = envelope.body.response + return_value = marshaler.soap_to_ruby(return_value) unless return_value.nil? + [method_name, return_value] + end + + def encode_response(method_name, return_value, return_type) + if return_type + return_binding = marshaler.register_type(return_type) + marshaler.annotate_arrays(return_binding, return_value) + end + qname = XSD::QName.new(marshaler.type_namespace, method_name) + if return_value.nil? + response = SOAP::RPC::SOAPMethodResponse.new(qname, nil) + else + if return_value.is_a?(Exception) + detail = SOAP::Mapping::SOAPException.new(return_value) + response = SOAP::SOAPFault.new( + SOAP::SOAPQName.new('%s:%s' % [SOAP::SOAPNamespaceTag, 'Server']), + SOAP::SOAPString.new(return_value.to_s), + SOAP::SOAPString.new(self.class.name), + marshaler.ruby_to_soap(detail)) + else + if return_type + param_def = [['retval', 'return', marshaler.lookup_type(return_type).mapping]] + response = SOAP::RPC::SOAPMethodResponse.new(qname, param_def) + response.retval = marshaler.ruby_to_soap(return_value) + else + response = SOAP::RPC::SOAPMethodResponse.new(qname, nil) + end + end + end + envelope = create_soap_envelope(response) + Response.new(SOAP::Processor.marshal(envelope), 'text/xml', return_value) + end + def protocol_client(api, protocol_name, endpoint_uri, options={}) return nil unless protocol_name == :soap ActionWebService::Client::Soap.new(api, endpoint_uri, options) end - def create_action_pack_request(service_name, public_method_name, raw_body, options={}) - request = super - request.env['HTTP_SOAPACTION'] = '/soap/%s/%s' % [service_name, public_method_name] - request + def register_api(api) + api.api_methods.each do |name, method| + method.expects.each{ |type| marshaler.register_type(type) } if method.expects + method.returns.each{ |type| marshaler.register_type(type) } if method.returns + end end private @@ -43,7 +122,13 @@ module ActionWebService # :nodoc: return nil if soap_action.empty? soap_action end - end + + def create_soap_envelope(body) + header = SOAP::SOAPHeader.new + body = SOAP::SOAPBody.new(body) + SOAP::SOAPEnvelope.new(header, body) + end + end end end end -- cgit v1.2.3