summaryrefslogtreecommitdiff
path: root/lib/soap/rpc
diff options
context:
space:
mode:
authornahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2005-05-22 13:20:28 +0000
committernahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2005-05-22 13:20:28 +0000
commit991d0c409cc6b1d916330a32a9624aef808176a4 (patch)
tree5e2cc150dc84ab3f6f64685ec7f54e6b2077eae7 /lib/soap/rpc
parent15b7d439885f4aa97e0f508ef485cadab4b23577 (diff)
* lib/{soap,wsdl,xsd}, test/{soap,wsdl,xsd}: imported soap4r/1.5.4.
== SOAP client and server == === for both client side and server side === * improved document/literal service support. style(rpc,document)/use(encoding, literal) combination are all supported. for the detail about combination, see test/soap/test_style.rb. * let WSDLEncodedRegistry#soap2obj map SOAP/OM to Ruby according to WSDL as well as obj2soap. closes #70. * let SOAP::Mapping::Object handle XML attribute for doc/lit service. you can set/get XML attribute via accessor methods which as a name 'xmlattr_' prefixed (<foo name="bar"/> -> Foo#xmlattr_name). === client side === * WSDLDriver capitalized name operation bug fixed. from 1.5.3-ruby1.8.2, operation which has capitalized name (such as KeywordSearchRequest in AWS) is defined as a method having uncapitalized name. (converted with GenSupport.safemethodname to handle operation name 'foo-bar'). it introduced serious incompatibility; in the past, it was defined as a capitalized. define capitalized method as well under that circumstance. * added new factory interface 'WSDLDriverFactory#create_rpc_driver' to create RPC::Driver, not WSDLDriver (RPC::Driver and WSDLDriver are merged). 'WSDLDriverFactory#create_driver' still creates WSDLDriver for compatibility but it warns that the method is deprecated. please use create_rpc_driver instead of create_driver. * allow to use an URI object as an endpoint_url even with net/http, not http-access2. === server side === * added mod_ruby support to SOAP::CGIStub. rename a CGI script server.cgi to server.rb and let mod_ruby's RubyHandler handles the script. CGIStub detects if it's running under mod_ruby environment or not. * added fcgi support to SOAP::CGIStub. see the sample at sample/soap/calc/server.fcgi. (almost same as server.cgi but has fcgi handler at the bottom.) * allow to return a SOAPFault object to respond customized SOAP fault. * added the interface 'generate_explicit_type' for server side (CGIStub, HTTPServer). call 'self.generate_explicit_type = true' if you want to return simplified XML even if it's rpc/encoded service. == WSDL == === WSDL definition === * improved XML Schema support such as extension, restriction, simpleType, complexType + simpleContent, ref, length, import, include. * reduced "unknown element/attribute" warnings (warn only 1 time for each QName). * importing XSD file at schemaLocation with xsd:import. === code generation from WSDL === * generator crashed when there's '-' in defined element/attribute name. * added ApacheMap WSDL definition. * sample/{soap,wsdl}: removed. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@8502 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/soap/rpc')
-rw-r--r--lib/soap/rpc/cgistub.rb234
-rw-r--r--lib/soap/rpc/driver.rb347
-rw-r--r--lib/soap/rpc/element.rb101
-rw-r--r--lib/soap/rpc/httpserver.rb99
-rw-r--r--lib/soap/rpc/proxy.rb286
-rw-r--r--lib/soap/rpc/router.rb526
-rw-r--r--lib/soap/rpc/soaplet.rb219
7 files changed, 1031 insertions, 781 deletions
diff --git a/lib/soap/rpc/cgistub.rb b/lib/soap/rpc/cgistub.rb
index e545d53c42..487f05a9bf 100644
--- a/lib/soap/rpc/cgistub.rb
+++ b/lib/soap/rpc/cgistub.rb
@@ -1,5 +1,5 @@
-# SOAP4R - CGI stub library
-# Copyright (C) 2001, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# SOAP4R - CGI/mod_ruby stub library
+# Copyright (C) 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
@@ -10,7 +10,7 @@ require 'soap/streamHandler'
require 'webrick/httpresponse'
require 'webrick/httpstatus'
require 'logger'
-require 'soap/rpc/router'
+require 'soap/rpc/soaplet'
module SOAP
@@ -26,57 +26,51 @@ module RPC
#
class CGIStub < Logger::Application
include SOAP
+ include WEBrick
- # There is a client which does not accept the media-type which is defined in
- # SOAP spec.
- attr_accessor :mediatype
+ class SOAPRequest
+ attr_reader :body
- class CGIError < Error; end
+ def [](var); end
- class SOAPRequest
- ALLOWED_LENGTH = 1024 * 1024
-
- def initialize(stream = $stdin)
- @method = ENV['REQUEST_METHOD']
- @size = ENV['CONTENT_LENGTH'].to_i || 0
- @contenttype = ENV['CONTENT_TYPE']
- @soapaction = ENV['HTTP_SOAPAction']
- @source = stream
- @body = nil
- end
+ def meta_vars; end
+ end
- def init
- validate
- @body = @source.read(@size)
- self
- end
+ class SOAPStdinRequest < SOAPRequest
+ attr_reader :body
- def dump
- @body.dup
+ def initialize(stream)
+ size = ENV['CONTENT_LENGTH'].to_i || 0
+ @body = stream.read(size)
end
- def soapaction
- @soapaction
+ def [](var)
+ ENV[var.gsub(/-/, '_').upcase]
end
- def contenttype
- @contenttype
+ def meta_vars
+ {
+ 'HTTP_SOAPACTION' => ENV['HTTP_SOAPAction']
+ }
end
+ end
- def to_s
- "method: #{ @method }, size: #{ @size }"
- end
+ class SOAPFCGIRequest < SOAPRequest
+ attr_reader :body
- private
+ def initialize(request)
+ @request = request
+ @body = @request.in.read
+ end
- def validate # raise CGIError
- if @method != 'POST'
- raise CGIError.new("Method '#{ @method }' not allowed.")
- end
+ def [](var)
+ @request.env[var.gsub(/-/, '_').upcase]
+ end
- if @size > ALLOWED_LENGTH
- raise CGIError.new("Content-length too long.")
- end
+ def meta_vars
+ {
+ 'HTTP_SOAPACTION' => @request.env['HTTP_SOAPAction']
+ }
end
end
@@ -85,33 +79,14 @@ class CGIStub < Logger::Application
set_log(STDERR)
self.level = ERROR
@default_namespace = default_namespace
- @router = SOAP::RPC::Router.new(appname)
- @remote_user = ENV['REMOTE_USER'] || 'anonymous'
@remote_host = ENV['REMOTE_HOST'] || ENV['REMOTE_ADDR'] || 'unknown'
- @request = nil
- @response = nil
- @mediatype = MediaType
+ @router = ::SOAP::RPC::Router.new(self.class.name)
+ @soaplet = ::SOAP::RPC::SOAPlet.new(@router)
on_init
end
- def add_rpc_servant(obj, namespace = @default_namespace, soapaction = nil)
- RPC.defined_methods(obj).each do |name|
- qname = XSD::QName.new(namespace, name)
- param_size = obj.method(name).arity.abs
- params = (1..param_size).collect { |i| "p#{i}" }
- param_def = SOAP::RPC::SOAPMethod.create_param_def(params)
- @router.add_method(obj, qname, soapaction, name, param_def)
- end
- end
- alias add_servant add_rpc_servant
-
- def add_rpc_headerhandler(obj)
- @router.headerhandler << obj
- end
- alias add_headerhandler add_rpc_headerhandler
-
def on_init
- # Override this method in derived class to call 'add_method' to add methods.
+ # do extra initialization in a derived class if needed.
end
def mapping_registry
@@ -122,83 +97,108 @@ class CGIStub < Logger::Application
@router.mapping_registry = value
end
- def add_method(receiver, name, *param)
- add_method_with_namespace_as(@default_namespace, receiver,
- name, name, *param)
+ def generate_explicit_type
+ @router.generate_explicit_type
end
- def add_method_as(receiver, name, name_as, *param)
- add_method_with_namespace_as(@default_namespace, receiver,
- name, name_as, *param)
+ def generate_explicit_type=(generate_explicit_type)
+ @router.generate_explicit_type = generate_explicit_type
end
- def add_method_with_namespace(namespace, receiver, name, *param)
- add_method_with_namespace_as(namespace, receiver, name, name, *param)
+ # servant entry interface
+
+ def add_rpc_servant(obj, namespace = @default_namespace)
+ @router.add_rpc_servant(obj, namespace)
end
+ alias add_servant add_rpc_servant
- def add_method_with_namespace_as(namespace, receiver, name, name_as, *param)
- param_def = if param.size == 1 and param[0].is_a?(Array)
- param[0]
- else
- SOAP::RPC::SOAPMethod.create_param_def(param)
- end
+ def add_headerhandler(obj)
+ @router.add_headerhandler(obj)
+ end
+ alias add_rpc_headerhandler add_headerhandler
+
+ # method entry interface
+
+ def add_rpc_method(obj, name, *param)
+ add_rpc_method_with_namespace_as(@default_namespace, obj, name, name, *param)
+ end
+ alias add_method add_rpc_method
+
+ def add_rpc_method_as(obj, name, name_as, *param)
+ add_rpc_method_with_namespace_as(@default_namespace, obj, name, name_as, *param)
+ end
+ alias add_method_as add_rpc_method_as
+
+ def add_rpc_method_with_namespace(namespace, obj, name, *param)
+ add_rpc_method_with_namespace_as(namespace, obj, name, name, *param)
+ end
+ alias add_method_with_namespace add_rpc_method_with_namespace
+
+ def add_rpc_method_with_namespace_as(namespace, obj, name, name_as, *param)
qname = XSD::QName.new(namespace, name_as)
- @router.add_method(receiver, qname, nil, name, param_def)
+ soapaction = nil
+ param_def = SOAPMethod.derive_rpc_param_def(obj, name, *param)
+ @router.add_rpc_operation(obj, qname, soapaction, name, param_def)
end
+ alias add_method_with_namespace_as add_rpc_method_with_namespace_as
- def route(conn_data)
- @router.route(conn_data)
+ def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {})
+ @router.add_rpc_operation(receiver, qname, soapaction, name, param_def, opt)
end
- def create_fault_response(e)
- @router.create_fault_response(e)
+ def add_document_operation(receiver, soapaction, name, param_def, opt = {})
+ @router.add_document_operation(receiver, soapaction, name, param_def, opt)
+ end
+
+ def set_fcgi_request(request)
+ @fcgi = request
end
private
-
- def run
- prologue
- httpversion = WEBrick::HTTPVersion.new('1.0')
- @response = WEBrick::HTTPResponse.new({:HTTPVersion => httpversion})
- conn_data = nil
+ HTTPVersion = WEBrick::HTTPVersion.new('1.0') # dummy; ignored
+
+ def run
+ res = WEBrick::HTTPResponse.new({:HTTPVersion => HTTPVersion})
begin
- @log.info { "Received a request from '#{ @remote_user }@#{ @remote_host }'." }
- # SOAP request parsing.
- @request = SOAPRequest.new.init
- @response['Status'] = 200
- conn_data = ::SOAP::StreamHandler::ConnectionData.new
- conn_data.receive_string = @request.dump
- conn_data.receive_contenttype = @request.contenttype
- @log.debug { "XML Request: #{conn_data.receive_string}" }
- conn_data = route(conn_data)
- @log.debug { "XML Response: #{conn_data.send_string}" }
- if conn_data.is_fault
- @response['Status'] = 500
+ @log.info { "received a request from '#{ @remote_host }'" }
+ if @fcgi
+ req = SOAPFCGIRequest.new(@fcgi)
+ else
+ req = SOAPStdinRequest.new($stdin)
end
- @response['Cache-Control'] = 'private'
- @response.body = conn_data.send_string
- @response['content-type'] = conn_data.send_contenttype
- rescue Exception
- conn_data = create_fault_response($!)
- @response['Cache-Control'] = 'private'
- @response['Status'] = 500
- @response.body = conn_data.send_string
- @response['content-type'] = conn_data.send_contenttype || @mediatype
+ @soaplet.do_POST(req, res)
+ rescue HTTPStatus::EOFError, HTTPStatus::RequestTimeout => ex
+ res.set_error(ex)
+ rescue HTTPStatus::Error => ex
+ res.set_error(ex)
+ rescue HTTPStatus::Status => ex
+ res.status = ex.code
+ rescue StandardError, NameError => ex # for Ruby 1.6
+ res.set_error(ex, true)
ensure
- buf = ''
- @response.send_response(buf)
- buf.sub!(/^[^\r]+\r\n/, '') # Trim status line.
+ if defined?(MOD_RUBY)
+ r = Apache.request
+ r.status = res.status
+ r.content_type = res.content_type
+ r.send_http_header
+ buf = res.body
+ else
+ buf = ''
+ res.send_response(buf)
+ buf.sub!(/^[^\r]+\r\n/, '') # Trim status line.
+ end
@log.debug { "SOAP CGI Response:\n#{ buf }" }
- print buf
- epilogue
+ if @fcgi
+ @fcgi.out.print buf
+ @fcgi.finish
+ @fcgi = nil
+ else
+ print buf
+ end
end
-
0
end
-
- def prologue; end
- def epilogue; end
end
diff --git a/lib/soap/rpc/driver.rb b/lib/soap/rpc/driver.rb
index 5fd755c51a..cb10ed92b5 100644
--- a/lib/soap/rpc/driver.rb
+++ b/lib/soap/rpc/driver.rb
@@ -1,5 +1,5 @@
# SOAP4R - SOAP RPC driver
-# Copyright (C) 2000, 2001, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2000, 2001, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
@@ -22,32 +22,55 @@ module RPC
class Driver
- class EmptyResponseError < Error; end
-
class << self
- def __attr_proxy(symbol, assignable = false)
- name = symbol.to_s
- self.__send__(:define_method, name, proc {
- @servant.__send__(name)
- })
- if assignable
- self.__send__(:define_method, name + '=', proc { |rhs|
- @servant.__send__(name + '=', rhs)
+ if RUBY_VERSION >= "1.7.0"
+ def __attr_proxy(symbol, assignable = false)
+ name = symbol.to_s
+ self.__send__(:define_method, name, proc {
+ @proxy.__send__(name)
})
+ if assignable
+ self.__send__(:define_method, name + '=', proc { |rhs|
+ @proxy.__send__(name + '=', rhs)
+ })
+ end
+ end
+ else
+ def __attr_proxy(symbol, assignable = false)
+ name = symbol.to_s
+ module_eval <<-EOS
+ def #{name}
+ @proxy.#{name}
+ end
+ EOS
+ if assignable
+ module_eval <<-EOS
+ def #{name}=(value)
+ @proxy.#{name} = value
+ end
+ EOS
+ end
end
end
end
- __attr_proxy :options
- __attr_proxy :headerhandler
- __attr_proxy :streamhandler
- __attr_proxy :test_loopback_response
__attr_proxy :endpoint_url, true
__attr_proxy :mapping_registry, true
- __attr_proxy :soapaction, true
__attr_proxy :default_encodingstyle, true
__attr_proxy :generate_explicit_type, true
__attr_proxy :allow_unqualified_element, true
+ __attr_proxy :headerhandler
+ __attr_proxy :streamhandler
+ __attr_proxy :test_loopback_response
+ __attr_proxy :reset_stream
+
+ attr_reader :proxy
+ attr_reader :options
+ attr_accessor :soapaction
+
+ def inspect
+ "#<#{self.class}:#{@proxy.inspect}>"
+ end
def httpproxy
options["protocol.http.proxy"]
@@ -81,10 +104,12 @@ class Driver
options["protocol.wiredump_file_base"] = wiredump_file_base
end
- def initialize(endpoint_url, namespace, soapaction = nil)
- @servant = Servant__.new(self, endpoint_url, namespace)
- @servant.soapaction = soapaction
- @proxy = @servant.proxy
+ def initialize(endpoint_url, namespace = nil, soapaction = nil)
+ @namespace = namespace
+ @soapaction = soapaction
+ @options = setup_options
+ @wiredump_file_base = nil
+ @proxy = Proxy.new(endpoint_url, @soapaction, @options)
end
def loadproperty(propertyname)
@@ -93,28 +118,23 @@ class Driver
end
end
- def inspect
- "#<#{self.class}:#{@servant.inspect}>"
- end
-
def add_rpc_method(name, *params)
- param_def = create_rpc_param_def(params)
- @servant.add_rpc_method(name, @servant.soapaction, name, param_def)
+ add_rpc_method_with_soapaction_as(name, name, @soapaction, *params)
end
def add_rpc_method_as(name, name_as, *params)
- param_def = create_rpc_param_def(params)
- @servant.add_rpc_method(name_as, @servant.soapaction, name, param_def)
+ add_rpc_method_with_soapaction_as(name, name_as, @soapaction, *params)
end
def add_rpc_method_with_soapaction(name, soapaction, *params)
- param_def = create_rpc_param_def(params)
- @servant.add_rpc_method(name, soapaction, name, param_def)
+ add_rpc_method_with_soapaction_as(name, name, soapaction, *params)
end
def add_rpc_method_with_soapaction_as(name, name_as, soapaction, *params)
- param_def = create_rpc_param_def(params)
- @servant.add_rpc_method(name_as, soapaction, name, param_def)
+ param_def = SOAPMethod.create_rpc_param_def(params)
+ qname = XSD::QName.new(@namespace, name_as)
+ @proxy.add_rpc_method(qname, soapaction, name, param_def)
+ add_rpc_method_interface(name, param_def)
end
# add_method is for shortcut of typical rpc/encoded method definition.
@@ -123,226 +143,107 @@ class Driver
alias add_method_with_soapaction add_rpc_method_with_soapaction
alias add_method_with_soapaction_as add_rpc_method_with_soapaction_as
- def add_document_method(name, req_qname, res_qname)
- param_def = create_document_param_def(name, req_qname, res_qname)
- @servant.add_document_method(name, @servant.soapaction, name, param_def)
- end
-
- def add_document_method_as(name, name_as, req_qname, res_qname)
- param_def = create_document_param_def(name, req_qname, res_qname)
- @servant.add_document_method(name_as, @servant.soapaction, name, param_def)
- end
-
- def add_document_method_with_soapaction(name, soapaction, req_qname,
- res_qname)
- param_def = create_document_param_def(name, req_qname, res_qname)
- @servant.add_document_method(name, soapaction, name, param_def)
+ def add_document_method(name, soapaction, req_qname, res_qname)
+ param_def = SOAPMethod.create_doc_param_def(req_qname, res_qname)
+ @proxy.add_document_method(soapaction, name, param_def)
+ add_document_method_interface(name, param_def)
end
- def add_document_method_with_soapaction_as(name, name_as, soapaction,
- req_qname, res_qname)
- param_def = create_document_param_def(name, req_qname, res_qname)
- @servant.add_document_method(name_as, soapaction, name, param_def)
+ def add_rpc_operation(qname, soapaction, name, param_def, opt = {})
+ @proxy.add_rpc_operation(qname, soapaction, name, param_def, opt)
+ add_rpc_method_interface(name, param_def)
end
- def reset_stream
- @servant.reset_stream
+ def add_document_operation(soapaction, name, param_def, opt = {})
+ @proxy.add_document_operation(soapaction, name, param_def, opt)
+ add_document_method_interface(name, param_def)
end
def invoke(headers, body)
- @servant.invoke(headers, body)
+ if headers and !headers.is_a?(SOAPHeader)
+ headers = create_header(headers)
+ end
+ set_wiredump_file_base(body.elename.name)
+ env = @proxy.invoke(headers, body)
+ if env.nil?
+ return nil, nil
+ else
+ return env.header, env.body
+ end
end
def call(name, *params)
- @servant.call(name, *params)
+ set_wiredump_file_base(name)
+ @proxy.call(name, *params)
end
private
- def create_rpc_param_def(params)
- if params.size == 1 and params[0].is_a?(Array)
- params[0]
- else
- SOAPMethod.create_param_def(params)
+ def set_wiredump_file_base(name)
+ if @wiredump_file_base
+ @proxy.set_wiredump_file_base("#{@wiredump_file_base}_#{name}")
end
end
- def create_document_param_def(name, req_qname, res_qname)
- [
- ['input', name, [nil, req_qname.namespace, req_qname.name]],
- ['output', name, [nil, res_qname.namespace, res_qname.name]]
- ]
- end
-
- def add_rpc_method_interface(name, param_def)
- @servant.add_rpc_method_interface(name, param_def)
- end
-
- def add_document_method_interface(name, paramname)
- @servant.add_document_method_interface(name, paramname)
- end
-
- class Servant__
- attr_reader :proxy
- attr_reader :options
- attr_accessor :soapaction
-
- def initialize(host, endpoint_url, namespace)
- @host = host
- @namespace = namespace
- @soapaction = nil
- @options = setup_options
- @wiredump_file_base = nil
- @endpoint_url = endpoint_url
- @proxy = Proxy.new(endpoint_url, @soapaction, @options)
- end
-
- def inspect
- "#<#{self.class}:#{@proxy.inspect}>"
- end
-
- def endpoint_url
- @proxy.endpoint_url
- end
-
- def endpoint_url=(endpoint_url)
- @proxy.endpoint_url = endpoint_url
- end
-
- def mapping_registry
- @proxy.mapping_registry
- end
-
- def mapping_registry=(mapping_registry)
- @proxy.mapping_registry = mapping_registry
- end
-
- def default_encodingstyle
- @proxy.default_encodingstyle
- end
-
- def default_encodingstyle=(encodingstyle)
- @proxy.default_encodingstyle = encodingstyle
- end
-
- def generate_explicit_type
- @proxy.generate_explicit_type
- end
-
- def generate_explicit_type=(generate_explicit_type)
- @proxy.generate_explicit_type = generate_explicit_type
- end
-
- def allow_unqualified_element
- @proxy.allow_unqualified_element
- end
-
- def allow_unqualified_element=(allow_unqualified_element)
- @proxy.allow_unqualified_element = allow_unqualified_element
- end
-
- def headerhandler
- @proxy.headerhandler
- end
-
- def streamhandler
- @proxy.streamhandler
- end
-
- def test_loopback_response
- @proxy.test_loopback_response
+ def create_header(headers)
+ header = SOAPHeader.new()
+ headers.each do |content, mustunderstand, encodingstyle|
+ header.add(SOAPHeaderItem.new(content, mustunderstand, encodingstyle))
end
+ header
+ end
- def reset_stream
- @proxy.reset_stream
+ def setup_options
+ if opt = Property.loadproperty(::SOAP::PropertyName)
+ opt = opt["client"]
end
-
- def invoke(headers, body)
- if headers and !headers.is_a?(SOAPHeader)
- headers = create_header(headers)
- end
- set_wiredump_file_base(body.elename.name)
- env = @proxy.invoke(headers, body)
- if env.nil?
- return nil, nil
- else
- return env.header, env.body
- end
+ opt ||= Property.new
+ opt.add_hook("protocol.mandatorycharset") do |key, value|
+ @proxy.mandatorycharset = value
end
-
- def call(name, *params)
- set_wiredump_file_base(name)
- @proxy.call(name, *params)
+ opt.add_hook("protocol.wiredump_file_base") do |key, value|
+ @wiredump_file_base = value
end
+ opt["protocol.http.charset"] ||= XSD::Charset.encoding_label
+ opt["protocol.http.proxy"] ||= Env::HTTP_PROXY
+ opt["protocol.http.no_proxy"] ||= Env::NO_PROXY
+ opt
+ end
- def add_rpc_method(name_as, soapaction, name, param_def)
- qname = XSD::QName.new(@namespace, name_as)
- @proxy.add_rpc_method(qname, soapaction, name, param_def)
- add_rpc_method_interface(name, param_def)
- end
+ def add_rpc_method_interface(name, param_def)
+ param_count = RPC::SOAPMethod.param_count(param_def,
+ RPC::SOAPMethod::IN, RPC::SOAPMethod::INOUT)
+ add_method_interface(name, param_count)
+ end
- def add_document_method(name_as, soapaction, name, param_def)
- qname = XSD::QName.new(@namespace, name_as)
- @proxy.add_document_method(qname, soapaction, name, param_def)
- add_document_method_interface(name, param_def)
- end
+ def add_document_method_interface(name, param_def)
+ param_count = RPC::SOAPMethod.param_count(param_def, RPC::SOAPMethod::IN)
+ add_method_interface(name, param_count)
+ end
- def add_rpc_method_interface(name, param_def)
- param_count = 0
- @proxy.operation[name].each_param_name(RPC::SOAPMethod::IN,
- RPC::SOAPMethod::INOUT) do |param_name|
- param_count += 1
- end
- sclass = class << @host; self; end
- sclass.__send__(:define_method, name, proc { |*arg|
+ if RUBY_VERSION > "1.7.0"
+ def add_method_interface(name, param_count)
+ ::SOAP::Mapping.define_singleton_method(self, name) do |*arg|
unless arg.size == param_count
raise ArgumentError.new(
- "wrong number of arguments (#{arg.size} for #{param_count})")
+ "wrong number of arguments (#{arg.size} for #{param_count})")
end
- @servant.call(name, *arg)
- })
- @host.method(name)
- end
-
- def add_document_method_interface(name, paramname)
- sclass = class << @host; self; end
- sclass.__send__(:define_method, name, proc { |param|
- @servant.call(name, param)
- })
- @host.method(name)
- end
-
- private
-
- def set_wiredump_file_base(name)
- if @wiredump_file_base
- @proxy.set_wiredump_file_base(@wiredump_file_base + "_#{ name }")
- end
- end
-
- def create_header(headers)
- header = SOAPHeader.new()
- headers.each do |content, mustunderstand, encodingstyle|
- header.add(SOAPHeaderItem.new(content, mustunderstand, encodingstyle))
- end
- header
- end
-
- def setup_options
- if opt = Property.loadproperty(::SOAP::PropertyName)
- opt = opt["client"]
- end
- opt ||= Property.new
- opt.add_hook("protocol.mandatorycharset") do |key, value|
- @proxy.mandatorycharset = value
+ call(name, *arg)
end
- opt.add_hook("protocol.wiredump_file_base") do |key, value|
- @wiredump_file_base = value
- end
- opt["protocol.http.charset"] ||= XSD::Charset.encoding_label
- opt["protocol.http.proxy"] ||= Env::HTTP_PROXY
- opt["protocol.http.no_proxy"] ||= Env::NO_PROXY
- opt
+ self.method(name)
+ end
+ else
+ def add_method_interface(name, param_count)
+ instance_eval <<-EOS
+ def #{name}(*arg)
+ unless arg.size == #{param_count}
+ raise ArgumentError.new(
+ "wrong number of arguments (\#{arg.size} for #{param_count})")
+ end
+ call(#{name.dump}, *arg)
+ end
+ EOS
+ self.method(name)
end
end
end
diff --git a/lib/soap/rpc/element.rb b/lib/soap/rpc/element.rb
index 8a2f319293..e6cae2f7e0 100644
--- a/lib/soap/rpc/element.rb
+++ b/lib/soap/rpc/element.rb
@@ -1,5 +1,5 @@
# SOAP4R - RPC element definition.
-# Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2000, 2001, 2003, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
@@ -77,6 +77,8 @@ class SOAPMethod < SOAPStruct
attr_reader :param_def
attr_reader :inparam
attr_reader :outparam
+ attr_reader :retval_name
+ attr_reader :retval_class_name
def initialize(qname, param_def = nil)
super(nil)
@@ -93,6 +95,7 @@ class SOAPMethod < SOAPStruct
@inparam = {}
@outparam = {}
@retval_name = nil
+ @retval_class_name = nil
init_param(@param_def) if @param_def
end
@@ -101,12 +104,12 @@ class SOAPMethod < SOAPStruct
@outparam_names.size > 0
end
- def each_param_name(*type)
- @signature.each do |io_type, name, param_type|
- if type.include?(io_type)
- yield(name)
- end
- end
+ def input_params
+ collect_params(IN, INOUT)
+ end
+
+ def output_params
+ collect_params(OUT, INOUT)
end
def set_param(params)
@@ -124,7 +127,30 @@ class SOAPMethod < SOAPStruct
end
end
- def SOAPMethod.create_param_def(param_names)
+ def SOAPMethod.param_count(param_def, *type)
+ count = 0
+ param_def.each do |io_type, name, param_type|
+ if type.include?(io_type)
+ count += 1
+ end
+ end
+ count
+ end
+
+ def SOAPMethod.derive_rpc_param_def(obj, name, *param)
+ if param.size == 1 and param[0].is_a?(Array)
+ return param[0]
+ end
+ if param.empty?
+ method = obj.method(name)
+ param_names = (1..method.arity.abs).collect { |i| "p#{i}" }
+ else
+ param_names = param
+ end
+ create_rpc_param_def(param_names)
+ end
+
+ def SOAPMethod.create_rpc_param_def(param_names)
param_def = []
param_names.each do |param_name|
param_def.push([IN, param_name, nil])
@@ -133,8 +159,29 @@ class SOAPMethod < SOAPStruct
param_def
end
+ def SOAPMethod.create_doc_param_def(req_qnames, res_qnames)
+ req_qnames = [req_qnames] if req_qnames.is_a?(XSD::QName)
+ res_qnames = [res_qnames] if res_qnames.is_a?(XSD::QName)
+ param_def = []
+ req_qnames.each do |qname|
+ param_def << [IN, qname.name, [nil, qname.namespace, qname.name]]
+ end
+ res_qnames.each do |qname|
+ param_def << [OUT, qname.name, [nil, qname.namespace, qname.name]]
+ end
+ param_def
+ end
+
private
+ def collect_params(*type)
+ names = []
+ @signature.each do |io_type, name, param_type|
+ names << name if type.include?(io_type)
+ end
+ names
+ end
+
def init_param(param_def)
param_def.each do |io_type, name, param_type|
case io_type
@@ -148,12 +195,20 @@ private
@signature.push([INOUT, name, param_type])
@inoutparam_names.push(name)
when RETVAL
- if (@retval_name)
- raise MethodDefinitionError.new('Duplicated retval')
+ if @retval_name
+ raise MethodDefinitionError.new('duplicated retval')
end
@retval_name = name
+ @retval_class_name = nil
+ if param_type
+ if param_type[0].is_a?(String)
+ @retval_class_name = Mapping.class_from_name(param_type[0])
+ else
+ @retval_class_name = param_type[0]
+ end
+ end
else
- raise MethodDefinitionError.new("Unknown type: #{ io_type }")
+ raise MethodDefinitionError.new("unknown type: #{io_type}")
end
end
end
@@ -168,7 +223,7 @@ class SOAPMethodRequest < SOAPMethod
param_value = []
i = 0
params.each do |param|
- param_name = "p#{ i }"
+ param_name = "p#{i}"
i += 1
param_def << [IN, param_name, nil]
param_value << [param_name, param]
@@ -186,9 +241,9 @@ class SOAPMethodRequest < SOAPMethod
end
def each
- each_param_name(IN, INOUT) do |name|
+ input_params.each do |name|
unless @inparam[name]
- raise ParameterError.new("Parameter: #{ name } was not given.")
+ raise ParameterError.new("parameter: #{name} was not given")
end
yield(name, @inparam[name])
end
@@ -200,10 +255,10 @@ class SOAPMethodRequest < SOAPMethod
req
end
- def create_method_response
- SOAPMethodResponse.new(
- XSD::QName.new(@elename.namespace, @elename.name + 'Response'),
- @param_def)
+ def create_method_response(response_name = nil)
+ response_name ||=
+ XSD::QName.new(@elename.namespace, @elename.name + 'Response')
+ SOAPMethodResponse.new(response_name, @param_def)
end
private
@@ -211,7 +266,7 @@ private
def check_elename(qname)
# NCName & ruby's method name
unless /\A[\w_][\w\d_\-]*\z/ =~ qname.name
- raise MethodDefinitionError.new("Element name '#{qname.name}' not allowed")
+ raise MethodDefinitionError.new("element name '#{qname.name}' not allowed")
end
end
end
@@ -236,11 +291,11 @@ class SOAPMethodResponse < SOAPMethod
yield(@retval_name, @retval)
end
- each_param_name(OUT, INOUT) do |param_name|
- unless @outparam[param_name]
- raise ParameterError.new("Parameter: #{ param_name } was not given.")
+ output_params.each do |name|
+ unless @outparam[name]
+ raise ParameterError.new("parameter: #{name} was not given")
end
- yield(param_name, @outparam[param_name])
+ yield(name, @outparam[name])
end
end
end
diff --git a/lib/soap/rpc/httpserver.rb b/lib/soap/rpc/httpserver.rb
index dccf950480..6d2a72ebe3 100644
--- a/lib/soap/rpc/httpserver.rb
+++ b/lib/soap/rpc/httpserver.rb
@@ -24,55 +24,62 @@ class HTTPServer < Logger::Application
super(config[:SOAPHTTPServerApplicationName] || self.class.name)
@default_namespace = config[:SOAPDefaultNamespace]
@webrick_config = config.dup
+ self.level = Logger::Severity::ERROR # keep silent by default
@webrick_config[:Logger] ||= @log
- @server = nil
- @soaplet = ::SOAP::RPC::SOAPlet.new
- self.level = Logger::Severity::INFO
+ @log = @webrick_config[:Logger] # sync logger of App and HTTPServer
+ @router = ::SOAP::RPC::Router.new(self.class.name)
+ @soaplet = ::SOAP::RPC::SOAPlet.new(@router)
on_init
+ @server = WEBrick::HTTPServer.new(@webrick_config)
+ @server.mount('/', @soaplet)
end
def on_init
- # define extra methods in derived class.
+ # do extra initialization in a derived class if needed.
end
def status
- if @server
- @server.status
- else
- nil
- end
+ @server.status if @server
end
def shutdown
@server.shutdown if @server
end
-
+
def mapping_registry
- @soaplet.app_scope_router.mapping_registry
+ @router.mapping_registry
end
def mapping_registry=(mapping_registry)
- @soaplet.app_scope_router.mapping_registry = mapping_registry
+ @router.mapping_registry = mapping_registry
+ end
+
+ def generate_explicit_type
+ @router.generate_explicit_type
+ end
+
+ def generate_explicit_type=(generate_explicit_type)
+ @router.generate_explicit_type = generate_explicit_type
end
# servant entry interface
- def add_rpc_request_servant(factory, namespace = @default_namespace,
- mapping_registry = nil)
- @soaplet.add_rpc_request_servant(factory, namespace, mapping_registry)
+ def add_rpc_request_servant(factory, namespace = @default_namespace)
+ @router.add_rpc_request_servant(factory, namespace)
end
def add_rpc_servant(obj, namespace = @default_namespace)
- @soaplet.add_rpc_servant(obj, namespace)
+ @router.add_rpc_servant(obj, namespace)
end
- def add_rpc_request_headerhandler(factory)
- @soaplet.add_rpc_request_headerhandler(factory)
+ def add_request_headerhandler(factory)
+ @router.add_request_headerhandler(factory)
end
- def add_rpc_headerhandler(obj)
- @soaplet.add_rpc_headerhandler(obj)
+ def add_headerhandler(obj)
+ @router.add_headerhandler(obj)
end
+ alias add_rpc_headerhandler add_headerhandler
# method entry interface
@@ -81,52 +88,38 @@ class HTTPServer < Logger::Application
end
alias add_method add_rpc_method
- def add_document_method(obj, name, req_qname, res_qname)
- opt = {}
- opt[:request_style] = opt[:response_style] = :document
- opt[:request_use] = opt[:response_use] = :literal
- param_def = [
- ['input', req_qname.name, [nil, req_qname.namespace, req_qname.name]],
- ['output', req_qname.name, [nil, res_qname.namespace, res_qname.name]]
- ]
- @soaplet.app_scope_router.add_operation(req_qname, nil, obj, name,
- param_def, opt)
- end
-
def add_rpc_method_as(obj, name, name_as, *param)
qname = XSD::QName.new(@default_namespace, name_as)
soapaction = nil
- param_def = create_param_def(obj, name, param)
- add_operation(qname, soapaction, obj, name, param_def)
+ param_def = SOAPMethod.derive_rpc_param_def(obj, name, *param)
+ @router.add_rpc_operation(obj, qname, soapaction, name, param_def)
end
alias add_method_as add_rpc_method_as
- def add_operation(qname, soapaction, obj, name, param_def, opt = {})
- opt[:request_style] ||= :rpc
- opt[:response_style] ||= :rpc
- opt[:request_use] ||= :encoded
- opt[:response_use] ||= :encoded
- @soaplet.app_scope_router.add_operation(qname, soapaction, obj, name,
- param_def, opt)
+ def add_document_method(obj, soapaction, name, req_qnames, res_qnames)
+ param_def = SOAPMethod.create_doc_param_def(req_qnames, res_qnames)
+ @router.add_document_operation(obj, soapaction, name, param_def)
+ end
+
+ def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {})
+ @router.add_rpc_operation(receiver, qname, soapaction, name, param_def, opt)
+ end
+
+ def add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt = {})
+ @router.add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt)
end
- def create_param_def(obj, name, param = nil)
- if param.nil? or param.empty?
- method = obj.method(name)
- ::SOAP::RPC::SOAPMethod.create_param_def(
- (1..method.arity.abs).collect { |i| "p#{i}" })
- elsif param.size == 1 and param[0].is_a?(Array)
- param[0]
- else
- ::SOAP::RPC::SOAPMethod.create_param_def(param)
- end
+ def add_document_operation(receiver, soapaction, name, param_def, opt = {})
+ @router.add_document_operation(receiver, soapaction, name, param_def, opt)
+ end
+
+ def add_document_request_operation(factory, soapaction, name, param_def, opt = {})
+ @router.add_document_request_operation(factory, soapaction, name, param_def, opt)
end
private
def run
- @server = WEBrick::HTTPServer.new(@webrick_config)
- @server.mount('/', @soaplet)
@server.start
end
end
diff --git a/lib/soap/rpc/proxy.rb b/lib/soap/rpc/proxy.rb
index ca110664f9..b9d80541af 100644
--- a/lib/soap/rpc/proxy.rb
+++ b/lib/soap/rpc/proxy.rb
@@ -1,5 +1,5 @@
# SOAP4R - RPC Proxy library.
-# Copyright (C) 2000, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2000, 2003-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
@@ -78,66 +78,77 @@ public
@streamhandler.test_loopback_response
end
- def add_rpc_method(qname, soapaction, name, param_def, opt = {})
+ def add_rpc_operation(qname, soapaction, name, param_def, opt = {})
+ opt[:request_qname] = qname
opt[:request_style] ||= :rpc
opt[:response_style] ||= :rpc
opt[:request_use] ||= :encoded
opt[:response_use] ||= :encoded
- @operation[name] = Operation.new(qname, soapaction, name, param_def, opt)
+ @operation[name] = Operation.new(soapaction, param_def, opt)
end
- def add_document_method(qname, soapaction, name, param_def, opt = {})
+ def add_document_operation(soapaction, name, param_def, opt = {})
opt[:request_style] ||= :document
opt[:response_style] ||= :document
opt[:request_use] ||= :literal
opt[:response_use] ||= :literal
- @operation[name] = Operation.new(qname, soapaction, name, param_def, opt)
+ @operation[name] = Operation.new(soapaction, param_def, opt)
end
# add_method is for shortcut of typical rpc/encoded method definition.
- alias add_method add_rpc_method
+ alias add_method add_rpc_operation
+ alias add_rpc_method add_rpc_operation
+ alias add_document_method add_document_operation
- def invoke(req_header, req_body, opt = create_options)
- req_env = SOAPEnvelope.new(req_header, req_body)
- opt[:external_content] = nil
- conn_data = marshal(req_env, opt)
- if ext = opt[:external_content]
- mime = MIMEMessage.new
- ext.each do |k, v|
- mime.add_attachment(v.data)
- end
- mime.add_part(conn_data.send_string + "\r\n")
- mime.close
- conn_data.send_string = mime.content_str
- conn_data.send_contenttype = mime.headers['content-type'].str
- end
- conn_data = @streamhandler.send(@endpoint_url, conn_data, opt[:soapaction])
- if conn_data.receive_string.empty?
- return nil
- end
- unmarshal(conn_data, opt)
+ def invoke(req_header, req_body, opt = nil)
+ opt ||= create_options
+ route(req_header, req_body, opt, opt)
end
def call(name, *params)
unless op_info = @operation[name]
- raise MethodDefinitionError, "Method: #{name} not defined."
+ raise MethodDefinitionError, "method: #{name} not defined"
end
req_header = create_request_header
- req_body = op_info.create_request_body(params, @mapping_registry,
- @literal_mapping_registry)
- opt = create_options({
+ req_body = SOAPBody.new(
+ op_info.request_body(params, @mapping_registry, @literal_mapping_registry)
+ )
+ reqopt = create_options({
:soapaction => op_info.soapaction || @soapaction,
+ :default_encodingstyle => op_info.request_default_encodingstyle})
+ resopt = create_options({
:default_encodingstyle => op_info.response_default_encodingstyle})
- env = invoke(req_header, req_body, opt)
+ env = route(req_header, req_body, reqopt, resopt)
+ raise EmptyResponseError unless env
receive_headers(env.header)
- raise EmptyResponseError.new("Empty response.") unless env
begin
check_fault(env.body)
rescue ::SOAP::FaultError => e
- Mapping.fault2exception(e)
+ op_info.raise_fault(e, @mapping_registry, @literal_mapping_registry)
end
- op_info.create_response_obj(env, @mapping_registry,
- @literal_mapping_registry)
+ op_info.response_obj(env.body, @mapping_registry, @literal_mapping_registry)
+ end
+
+ def route(req_header, req_body, reqopt, resopt)
+ req_env = SOAPEnvelope.new(req_header, req_body)
+ reqopt[:external_content] = nil
+ conn_data = marshal(req_env, reqopt)
+ if ext = reqopt[:external_content]
+ mime = MIMEMessage.new
+ ext.each do |k, v|
+ mime.add_attachment(v.data)
+ end
+ mime.add_part(conn_data.send_string + "\r\n")
+ mime.close
+ conn_data.send_string = mime.content_str
+ conn_data.send_contenttype = mime.headers['content-type'].str
+ end
+ conn_data = @streamhandler.send(@endpoint_url, conn_data,
+ reqopt[:soapaction])
+ if conn_data.receive_string.empty?
+ return nil
+ end
+ unmarshal(conn_data, resopt)
end
def check_fault(body)
@@ -217,97 +228,202 @@ private
attr_reader :request_use
attr_reader :response_use
- def initialize(qname, soapaction, name, param_def, opt)
+ def initialize(soapaction, param_def, opt)
@soapaction = soapaction
@request_style = opt[:request_style]
@response_style = opt[:response_style]
@request_use = opt[:request_use]
@response_use = opt[:response_use]
- @rpc_method_factory = @document_method_name = nil
check_style(@request_style)
check_style(@response_style)
+ check_use(@request_use)
+ check_use(@response_use)
if @request_style == :rpc
- @rpc_method_factory = SOAPMethodRequest.new(qname, param_def,
- @soapaction)
+ @rpc_request_qname = opt[:request_qname]
+ if @rpc_request_qname.nil?
+ raise MethodDefinitionError.new("rpc_request_qname must be given")
+ end
+ @rpc_method_factory =
+ RPC::SOAPMethodRequest.new(@rpc_request_qname, param_def, @soapaction)
else
- @document_method_name = {}
+ @doc_request_qnames = []
+ @doc_response_qnames = []
param_def.each do |inout, paramname, typeinfo|
- klass, namespace, name = typeinfo
- case inout.to_s
- when "input"
- @document_method_name[:input] = ::XSD::QName.new(namespace, name)
- when "output"
- @document_method_name[:output] = ::XSD::QName.new(namespace, name)
+ klass_not_used, nsdef, namedef = typeinfo
+ if namedef.nil?
+ raise MethodDefinitionError.new("qname must be given")
+ end
+ case inout
+ when SOAPMethod::IN
+ @doc_request_qnames << XSD::QName.new(nsdef, namedef)
+ when SOAPMethod::OUT
+ @doc_response_qnames << XSD::QName.new(nsdef, namedef)
else
- raise MethodDefinitionError, "unknown type: " + inout
+ raise MethodDefinitionError.new(
+ "illegal inout definition for document style: #{inout}")
end
end
end
end
def request_default_encodingstyle
- (@request_style == :rpc) ? EncodingNamespace : LiteralNamespace
+ (@request_use == :encoded) ? EncodingNamespace : LiteralNamespace
end
def response_default_encodingstyle
- (@response_style == :rpc) ? EncodingNamespace : LiteralNamespace
+ (@response_use == :encoded) ? EncodingNamespace : LiteralNamespace
end
- # for rpc
- def each_param_name(*target)
+ def request_body(values, mapping_registry, literal_mapping_registry)
if @request_style == :rpc
- @rpc_method_factory.each_param_name(*target) do |name|
- yield(name)
- end
+ request_rpc(values, mapping_registry, literal_mapping_registry)
else
- yield(@document_method_name[:input].name)
+ request_doc(values, mapping_registry, literal_mapping_registry)
end
end
- def create_request_body(values, mapping_registry, literal_mapping_registry)
- if @request_style == :rpc
- values = Mapping.obj2soap(values, mapping_registry).to_a
- method = @rpc_method_factory.dup
- params = {}
- idx = 0
- method.each_param_name(::SOAP::RPC::SOAPMethod::IN,
- ::SOAP::RPC::SOAPMethod::INOUT) do |name|
- params[name] = values[idx] || SOAPNil.new
- idx += 1
- end
- method.set_param(params)
- SOAPBody.new(method)
+ def response_obj(body, mapping_registry, literal_mapping_registry)
+ if @response_style == :rpc
+ response_rpc(body, mapping_registry, literal_mapping_registry)
else
- name = @document_method_name[:input]
- document = literal_mapping_registry.obj2soap(values[0], name)
- SOAPBody.new(document)
+ response_doc(body, mapping_registry, literal_mapping_registry)
end
end
- def create_response_obj(env, mapping_registry, literal_mapping_registry)
+ def raise_fault(e, mapping_registry, literal_mapping_registry)
if @response_style == :rpc
- ret = env.body.response ?
- Mapping.soap2obj(env.body.response, mapping_registry) : nil
- if env.body.outparams
- outparams = env.body.outparams.collect { |outparam|
- Mapping.soap2obj(outparam)
- }
- [ret].concat(outparams)
- else
- ret
- end
+ Mapping.fault2exception(e, mapping_registry)
else
- Mapping.soap2obj(env.body.root_node, literal_mapping_registry)
+ Mapping.fault2exception(e, literal_mapping_registry)
end
end
private
- ALLOWED_STYLE = [:rpc, :document]
def check_style(style)
- unless ALLOWED_STYLE.include?(style)
- raise MethodDefinitionError, "unknown style: " + style
+ unless [:rpc, :document].include?(style)
+ raise MethodDefinitionError.new("unknown style: #{style}")
+ end
+ end
+
+ def check_use(use)
+ unless [:encoded, :literal].include?(use)
+ raise MethodDefinitionError.new("unknown use: #{use}")
+ end
+ end
+
+ def request_rpc(values, mapping_registry, literal_mapping_registry)
+ if @request_use == :encoded
+ request_rpc_enc(values, mapping_registry)
+ else
+ request_rpc_lit(values, literal_mapping_registry)
+ end
+ end
+
+ def request_doc(values, mapping_registry, literal_mapping_registry)
+ if @request_use == :encoded
+ request_doc_enc(values, mapping_registry)
+ else
+ request_doc_lit(values, literal_mapping_registry)
+ end
+ end
+
+ def request_rpc_enc(values, mapping_registry)
+ method = @rpc_method_factory.dup
+ names = method.input_params
+ obj = create_request_obj(names, values)
+ soap = Mapping.obj2soap(obj, mapping_registry, @rpc_request_qname)
+ method.set_param(soap)
+ method
+ end
+
+ def request_rpc_lit(values, mapping_registry)
+ method = @rpc_method_factory.dup
+ params = {}
+ idx = 0
+ method.input_params.each do |name|
+ params[name] = Mapping.obj2soap(values[idx], mapping_registry,
+ XSD::QName.new(nil, name))
+ idx += 1
+ end
+ method.set_param(params)
+ method
+ end
+
+ def request_doc_enc(values, mapping_registry)
+ (0...values.size).collect { |idx|
+ ele = Mapping.obj2soap(values[idx], mapping_registry)
+ ele.elename = @doc_request_qnames[idx]
+ ele
+ }
+ end
+
+ def request_doc_lit(values, mapping_registry)
+ (0...values.size).collect { |idx|
+ ele = Mapping.obj2soap(values[idx], mapping_registry,
+ @doc_request_qnames[idx])
+ ele.encodingstyle = LiteralNamespace
+ ele
+ }
+ end
+
+ def response_rpc(body, mapping_registry, literal_mapping_registry)
+ if @response_use == :encoded
+ response_rpc_enc(body, mapping_registry)
+ else
+ response_rpc_lit(body, literal_mapping_registry)
+ end
+ end
+
+ def response_doc(body, mapping_registry, literal_mapping_registry)
+ if @response_use == :encoded
+ return *response_doc_enc(body, mapping_registry)
+ else
+ return *response_doc_lit(body, literal_mapping_registry)
+ end
+ end
+
+ def response_rpc_enc(body, mapping_registry)
+ ret = nil
+ if body.response
+ ret = Mapping.soap2obj(body.response, mapping_registry,
+ @rpc_method_factory.retval_class_name)
+ end
+ if body.outparams
+ outparams = body.outparams.collect { |outparam|
+ Mapping.soap2obj(outparam, mapping_regisry)
+ }
+ [ret].concat(outparams)
+ else
+ ret
+ end
+ end
+
+ def response_rpc_lit(body, mapping_registry)
+ body.root_node.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry,
+ @rpc_method_factory.retval_class_name)
+ }
+ end
+
+ def response_doc_enc(body, mapping_registry)
+ body.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry)
+ }
+ end
+
+ def response_doc_lit(body, mapping_registry)
+ body.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry)
+ }
+ end
+
+ def create_request_obj(names, params)
+ o = Object.new
+ for idx in 0 ... params.length
+ o.instance_variable_set('@' + names[idx], params[idx])
end
+ o
end
end
end
diff --git a/lib/soap/rpc/router.rb b/lib/soap/rpc/router.rb
index e9147af13a..1d11bc17dc 100644
--- a/lib/soap/rpc/router.rb
+++ b/lib/soap/rpc/router.rb
@@ -1,5 +1,5 @@
# SOAP4R - RPC Routing library
-# Copyright (C) 2001, 2002 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2001, 2002, 2004, 2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
@@ -25,101 +25,229 @@ class Router
include SOAP
attr_reader :actor
- attr_accessor :allow_unqualified_element
- attr_accessor :default_encodingstyle
attr_accessor :mapping_registry
attr_accessor :literal_mapping_registry
- attr_reader :headerhandler
+ attr_accessor :generate_explicit_type
def initialize(actor)
@actor = actor
- @allow_unqualified_element = false
- @default_encodingstyle = nil
@mapping_registry = nil
@headerhandler = Header::HandlerSet.new
@literal_mapping_registry = ::SOAP::Mapping::WSDLLiteralRegistry.new
- @operation = {}
+ @generate_explicit_type = true
+ @operation_by_soapaction = {}
+ @operation_by_qname = {}
+ @headerhandlerfactory = []
end
- def add_rpc_method(receiver, qname, soapaction, name, param_def, opt = {})
- opt[:request_style] ||= :rpc
- opt[:response_style] ||= :rpc
- opt[:request_use] ||= :encoded
- opt[:response_use] ||= :encoded
- add_operation(qname, soapaction, receiver, name, param_def, opt)
+ ###
+ ## header handler interface
+ #
+ def add_request_headerhandler(factory)
+ unless factory.respond_to?(:create)
+ raise TypeError.new("factory must respond to 'create'")
+ end
+ @headerhandlerfactory << factory
end
- def add_document_method(receiver, qname, soapaction, name, param_def, opt = {})
- opt[:request_style] ||= :document
- opt[:response_style] ||= :document
- opt[:request_use] ||= :encoded
- opt[:response_use] ||= :encoded
- if opt[:request_style] == :document
- inputdef = param_def.find { |inout, paramname, typeinfo| inout == "input" }
- klass, nsdef, namedef = inputdef[2]
- qname = ::XSD::QName.new(nsdef, namedef)
- end
- add_operation(qname, soapaction, receiver, name, param_def, opt)
+ def add_headerhandler(handler)
+ @headerhandler.add(handler)
end
- def add_operation(qname, soapaction, receiver, name, param_def, opt)
- @operation[fqname(qname)] = Operation.new(qname, soapaction, receiver,
- name, param_def, opt)
+ ###
+ ## servant definition interface
+ #
+ def add_rpc_request_servant(factory, namespace)
+ unless factory.respond_to?(:create)
+ raise TypeError.new("factory must respond to 'create'")
+ end
+ obj = factory.create # a dummy instance for introspection
+ ::SOAP::RPC.defined_methods(obj).each do |name|
+ begin
+ qname = XSD::QName.new(namespace, name)
+ param_def = ::SOAP::RPC::SOAPMethod.derive_rpc_param_def(obj, name)
+ opt = create_styleuse_option(:rpc, :encoded)
+ add_rpc_request_operation(factory, qname, nil, name, param_def, opt)
+ rescue SOAP::RPC::MethodDefinitionError => e
+ p e if $DEBUG
+ end
+ end
+ end
+
+ def add_rpc_servant(obj, namespace)
+ ::SOAP::RPC.defined_methods(obj).each do |name|
+ begin
+ qname = XSD::QName.new(namespace, name)
+ param_def = ::SOAP::RPC::SOAPMethod.derive_rpc_param_def(obj, name)
+ opt = create_styleuse_option(:rpc, :encoded)
+ add_rpc_operation(obj, qname, nil, name, param_def, opt)
+ rescue SOAP::RPC::MethodDefinitionError => e
+ p e if $DEBUG
+ end
+ end
+ end
+ alias add_servant add_rpc_servant
+
+ ###
+ ## operation definition interface
+ #
+ def add_rpc_operation(receiver, qname, soapaction, name, param_def, opt = {})
+ ensure_styleuse_option(opt, :rpc, :encoded)
+ opt[:request_qname] = qname
+ op = ApplicationScopeOperation.new(soapaction, receiver, name, param_def,
+ opt)
+ if opt[:request_style] != :rpc
+ raise RPCRoutingError.new("illegal request_style given")
+ end
+ assign_operation(soapaction, qname, op)
+ end
+ alias add_method add_rpc_operation
+ alias add_rpc_method add_rpc_operation
+
+ def add_rpc_request_operation(factory, qname, soapaction, name, param_def, opt = {})
+ ensure_styleuse_option(opt, :rpc, :encoded)
+ opt[:request_qname] = qname
+ op = RequestScopeOperation.new(soapaction, factory, name, param_def, opt)
+ if opt[:request_style] != :rpc
+ raise RPCRoutingError.new("illegal request_style given")
+ end
+ assign_operation(soapaction, qname, op)
end
- # add_method is for shortcut of typical use="encoded" method definition.
- alias add_method add_rpc_method
+ def add_document_operation(receiver, soapaction, name, param_def, opt = {})
+ #
+ # adopt workaround for doc/lit wrapper method
+ # (you should consider to simply use rpc/lit service)
+ #
+ #unless soapaction
+ # raise RPCRoutingError.new("soapaction is a must for document method")
+ #end
+ ensure_styleuse_option(opt, :document, :literal)
+ op = ApplicationScopeOperation.new(soapaction, receiver, name, param_def,
+ opt)
+ if opt[:request_style] != :document
+ raise RPCRoutingError.new("illegal request_style given")
+ end
+ assign_operation(soapaction, first_input_part_qname(param_def), op)
+ end
+ alias add_document_method add_document_operation
+
+ def add_document_request_operation(factory, soapaction, name, param_def, opt = {})
+ #
+ # adopt workaround for doc/lit wrapper method
+ # (you should consider to simply use rpc/lit service)
+ #
+ #unless soapaction
+ # raise RPCRoutingError.new("soapaction is a must for document method")
+ #end
+ ensure_styleuse_option(opt, :document, :literal)
+ op = RequestScopeOperation.new(soapaction, receiver, name, param_def, opt)
+ if opt[:request_style] != :document
+ raise RPCRoutingError.new("illegal request_style given")
+ end
+ assign_operation(soapaction, first_input_part_qname(param_def), op)
+ end
def route(conn_data)
- soap_response = nil
+ # we cannot set request_default_encodingsyle before parsing the content.
+ env = unmarshal(conn_data)
+ if env.nil?
+ raise ArgumentError.new("illegal SOAP marshal format")
+ end
+ op = lookup_operation(conn_data.soapaction, env.body)
+ headerhandler = @headerhandler.dup
+ @headerhandlerfactory.each do |f|
+ headerhandler.add(f.create)
+ end
+ receive_headers(headerhandler, env.header)
+ soap_response = default_encodingstyle = nil
begin
- env = unmarshal(conn_data)
- if env.nil?
- raise ArgumentError.new("Illegal SOAP marshal format.")
- end
- receive_headers(env.header)
- request = env.body.request
- op = @operation[fqname(request.elename)]
- unless op
- raise RPCRoutingError.new("Method: #{request.elename} not supported.")
- end
- soap_response = op.call(request, @mapping_registry, @literal_mapping_registry)
+ soap_response =
+ op.call(env.body, @mapping_registry, @literal_mapping_registry)
+ default_encodingstyle = op.response_default_encodingstyle
rescue Exception
soap_response = fault($!)
- conn_data.is_fault = true
+ default_encodingstyle = nil
end
- marshal(conn_data, op, soap_response)
- conn_data
+ conn_data.is_fault = true if soap_response.is_a?(SOAPFault)
+ header = call_headers(headerhandler)
+ body = SOAPBody.new(soap_response)
+ env = SOAPEnvelope.new(header, body)
+ marshal(conn_data, env, default_encodingstyle)
end
# Create fault response string.
- def create_fault_response(e, charset = nil)
- header = SOAPHeader.new
- body = SOAPBody.new(fault(e))
- env = SOAPEnvelope.new(header, body)
- opt = options
+ def create_fault_response(e)
+ env = SOAPEnvelope.new(SOAPHeader.new, SOAPBody.new(fault(e)))
+ opt = {}
opt[:external_content] = nil
- opt[:charset] = charset
response_string = Processor.marshal(env, opt)
conn_data = StreamHandler::ConnectionData.new(response_string)
conn_data.is_fault = true
if ext = opt[:external_content]
- mime = MIMEMessage.new
- ext.each do |k, v|
- mime.add_attachment(v.data)
- end
- mime.add_part(conn_data.send_string + "\r\n")
- mime.close
- conn_data.send_string = mime.content_str
- conn_data.send_contenttype = mime.headers['content-type'].str
+ mimeize(conn_data, ext)
end
conn_data
end
private
- def call_headers
- headers = @headerhandler.on_outbound
+ def first_input_part_qname(param_def)
+ param_def.each do |inout, paramname, typeinfo|
+ if inout == SOAPMethod::IN
+ klass, nsdef, namedef = typeinfo
+ return XSD::QName.new(nsdef, namedef)
+ end
+ end
+ nil
+ end
+
+ def create_styleuse_option(style, use)
+ opt = {}
+ opt[:request_style] = opt[:response_style] = style
+ opt[:request_use] = opt[:response_use] = use
+ opt
+ end
+
+ def ensure_styleuse_option(opt, style, use)
+ opt[:request_style] ||= style
+ opt[:response_style] ||= style
+ opt[:request_use] ||= use
+ opt[:response_use] ||= use
+ end
+
+ def assign_operation(soapaction, qname, op)
+ assigned = false
+ if soapaction and !soapaction.empty?
+ @operation_by_soapaction[soapaction] = op
+ assigned = true
+ end
+ if qname
+ @operation_by_qname[qname] = op
+ assigned = true
+ end
+ unless assigned
+ raise RPCRoutingError.new("cannot assign operation")
+ end
+ end
+
+ def lookup_operation(soapaction, body)
+ if op = @operation_by_soapaction[soapaction]
+ return op
+ end
+ qname = body.root_node.elename
+ if op = @operation_by_qname[qname]
+ return op
+ end
+ if soapaction
+ raise RPCRoutingError.new("operation: #{soapaction} not supported")
+ else
+ raise RPCRoutingError.new("operation: #{qname} not supported")
+ end
+ end
+
+ def call_headers(headerhandler)
+ headers = headerhandler.on_outbound
if headers.empty?
nil
else
@@ -131,17 +259,17 @@ private
end
end
- def receive_headers(headers)
- @headerhandler.on_inbound(headers) if headers
+ def receive_headers(headerhandler, headers)
+ headerhandler.on_inbound(headers) if headers
end
def unmarshal(conn_data)
- opt = options
+ opt = {}
contenttype = conn_data.receive_contenttype
if /#{MIMEMessage::MultipartContentType}/i =~ contenttype
opt[:external_content] = {}
mime = MIMEMessage.parse("Content-Type: " + contenttype,
- conn_data.receive_string)
+ conn_data.receive_string)
mime.parts.each do |part|
value = Attachment.new(part.content)
value.contentid = part.contentid
@@ -160,28 +288,29 @@ private
env
end
- def marshal(conn_data, op, soap_response)
- response_opt = options
- response_opt[:external_content] = nil
- if op and !conn_data.is_fault and op.response_use == :document
- response_opt[:default_encodingstyle] =
- ::SOAP::EncodingStyle::ASPDotNetHandler::Namespace
- end
- header = call_headers
- body = SOAPBody.new(soap_response)
- env = SOAPEnvelope.new(header, body)
- response_string = Processor.marshal(env, response_opt)
+ def marshal(conn_data, env, default_encodingstyle = nil)
+ opt = {}
+ opt[:external_content] = nil
+ opt[:default_encodingstyle] = default_encodingstyle
+ opt[:generate_explicit_type] = @generate_explicit_type
+ response_string = Processor.marshal(env, opt)
conn_data.send_string = response_string
- if ext = response_opt[:external_content]
- mime = MIMEMessage.new
- ext.each do |k, v|
- mime.add_attachment(v.data)
- end
- mime.add_part(conn_data.send_string + "\r\n")
- mime.close
- conn_data.send_string = mime.content_str
- conn_data.send_contenttype = mime.headers['content-type'].str
+ if ext = opt[:external_content]
+ mimeize(conn_data, ext)
end
+ conn_data
+ end
+
+ def mimeize(conn_data, ext)
+ mime = MIMEMessage.new
+ ext.each do |k, v|
+ mime.add_attachment(v.data)
+ end
+ mime.add_part(conn_data.send_string + "\r\n")
+ mime.close
+ conn_data.send_string = mime.content_str
+ conn_data.send_contenttype = mime.headers['content-type'].str
+ conn_data
end
# Create fault response.
@@ -194,84 +323,156 @@ private
Mapping.obj2soap(detail, @mapping_registry))
end
- def fqname(qname)
- "#{ qname.namespace }:#{ qname.name }"
- end
-
- def options
- opt = {}
- opt[:default_encodingstyle] = @default_encodingstyle
- if @allow_unqualified_element
- opt[:allow_unqualified_element] = true
- end
- opt
- end
-
class Operation
- attr_reader :receiver
attr_reader :name
attr_reader :soapaction
attr_reader :request_style
attr_reader :response_style
attr_reader :request_use
attr_reader :response_use
-
- def initialize(qname, soapaction, receiver, name, param_def, opt)
+
+ def initialize(soapaction, name, param_def, opt)
@soapaction = soapaction
- @receiver = receiver
@name = name
@request_style = opt[:request_style]
@response_style = opt[:response_style]
@request_use = opt[:request_use]
@response_use = opt[:response_use]
+ check_style(@request_style)
+ check_style(@response_style)
+ check_use(@request_use)
+ check_use(@response_use)
if @response_style == :rpc
- @rpc_response_factory =
- RPC::SOAPMethodRequest.new(qname, param_def, @soapaction)
+ request_qname = opt[:request_qname] or raise
+ @rpc_method_factory =
+ RPC::SOAPMethodRequest.new(request_qname, param_def, @soapaction)
+ @rpc_response_qname = opt[:response_qname]
else
- outputdef = param_def.find { |inout, paramname, typeinfo| inout == "output" }
- klass, nsdef, namedef = outputdef[2]
- @document_response_qname = ::XSD::QName.new(nsdef, namedef)
+ @doc_request_qnames = []
+ @doc_response_qnames = []
+ param_def.each do |inout, paramname, typeinfo|
+ klass, nsdef, namedef = typeinfo
+ case inout
+ when SOAPMethod::IN
+ @doc_request_qnames << XSD::QName.new(nsdef, namedef)
+ when SOAPMethod::OUT
+ @doc_response_qnames << XSD::QName.new(nsdef, namedef)
+ else
+ raise ArgumentError.new(
+ "illegal inout definition for document style: #{inout}")
+ end
+ end
end
end
- def call(request, mapping_registry, literal_mapping_registry)
+ def request_default_encodingstyle
+ (@request_use == :encoded) ? EncodingNamespace : LiteralNamespace
+ end
+
+ def response_default_encodingstyle
+ (@response_use == :encoded) ? EncodingNamespace : LiteralNamespace
+ end
+
+ def call(body, mapping_registry, literal_mapping_registry)
if @request_style == :rpc
- param = Mapping.soap2obj(request, mapping_registry)
- result = rpc_call(request, param)
+ values = request_rpc(body, mapping_registry, literal_mapping_registry)
else
- param = Mapping.soap2obj(request, literal_mapping_registry)
- result = document_call(request, param)
+ values = request_document(body, mapping_registry, literal_mapping_registry)
end
+ result = receiver.method(@name.intern).call(*values)
+ return result if result.is_a?(SOAPFault)
if @response_style == :rpc
- rpc_response(result, mapping_registry)
+ response_rpc(result, mapping_registry, literal_mapping_registry)
else
- document_response(result, literal_mapping_registry)
+ response_doc(result, mapping_registry, literal_mapping_registry)
end
end
private
- def rpc_call(request, param)
+ def receiver
+ raise NotImplementedError.new('must be defined in derived class')
+ end
+
+ def request_rpc(body, mapping_registry, literal_mapping_registry)
+ request = body.request
unless request.is_a?(SOAPStruct)
- raise RPCRoutingError.new("Not an RPC style.")
+ raise RPCRoutingError.new("not an RPC style")
end
- values = request.collect { |key, value| param[key] }
- @receiver.method(@name.intern).call(*values)
+ if @request_use == :encoded
+ request_rpc_enc(request, mapping_registry)
+ else
+ request_rpc_lit(request, literal_mapping_registry)
+ end
+ end
+
+ def request_document(body, mapping_registry, literal_mapping_registry)
+ # ToDo: compare names with @doc_request_qnames
+ if @request_use == :encoded
+ request_doc_enc(body, mapping_registry)
+ else
+ request_doc_lit(body, literal_mapping_registry)
+ end
+ end
+
+ def request_rpc_enc(request, mapping_registry)
+ param = Mapping.soap2obj(request, mapping_registry)
+ request.collect { |key, value|
+ param[key]
+ }
end
- def document_call(request, param)
- @receiver.method(@name.intern).call(param)
+ def request_rpc_lit(request, mapping_registry)
+ request.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry)
+ }
end
- def rpc_response(result, mapping_registry)
- soap_response = @rpc_response_factory.create_method_response
+ def request_doc_enc(body, mapping_registry)
+ body.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry)
+ }
+ end
+
+ def request_doc_lit(body, mapping_registry)
+ body.collect { |key, value|
+ Mapping.soap2obj(value, mapping_registry)
+ }
+ end
+
+ def response_rpc(result, mapping_registry, literal_mapping_registry)
+ if @response_use == :encoded
+ response_rpc_enc(result, mapping_registry)
+ else
+ response_rpc_lit(result, literal_mapping_registry)
+ end
+ end
+
+ def response_doc(result, mapping_registry, literal_mapping_registry)
+ if @doc_response_qnames.size == 1 and !result.is_a?(Array)
+ result = [result]
+ end
+ if result.size != @doc_response_qnames.size
+ raise "required #{@doc_response_qnames.size} responses " +
+ "but #{result.size} given"
+ end
+ if @response_use == :encoded
+ response_doc_enc(result, mapping_registry)
+ else
+ response_doc_lit(result, literal_mapping_registry)
+ end
+ end
+
+ def response_rpc_enc(result, mapping_registry)
+ soap_response =
+ @rpc_method_factory.create_method_response(@rpc_response_qname)
if soap_response.have_outparam?
unless result.is_a?(Array)
- raise RPCRoutingError.new("Out parameter was not returned.")
+ raise RPCRoutingError.new("out parameter was not returned")
end
outparams = {}
i = 1
- soap_response.each_param_name('out', 'inout') do |outparam|
+ soap_response.output_params.each do |outparam|
outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry)
i += 1
end
@@ -283,8 +484,83 @@ private
soap_response
end
- def document_response(result, literal_mapping_registry)
- literal_mapping_registry.obj2soap(result, @document_response_qname)
+ def response_rpc_lit(result, mapping_registry)
+ soap_response =
+ @rpc_method_factory.create_method_response(@rpc_response_qname)
+ if soap_response.have_outparam?
+ unless result.is_a?(Array)
+ raise RPCRoutingError.new("out parameter was not returned")
+ end
+ outparams = {}
+ i = 1
+ soap_response.output_params.each do |outparam|
+ outparams[outparam] = Mapping.obj2soap(result[i], mapping_registry,
+ XSD::QName.new(nil, outparam))
+ i += 1
+ end
+ soap_response.set_outparam(outparams)
+ soap_response.retval = Mapping.obj2soap(result[0], mapping_registry,
+ XSD::QName.new(nil, soap_response.elename))
+ else
+ soap_response.retval = Mapping.obj2soap(result, mapping_registry,
+ XSD::QName.new(nil, soap_response.elename))
+ end
+ soap_response
+ end
+
+ def response_doc_enc(result, mapping_registry)
+ (0...result.size).collect { |idx|
+ ele = Mapping.obj2soap(result[idx], mapping_registry)
+ ele.elename = @doc_response_qnames[idx]
+ ele
+ }
+ end
+
+ def response_doc_lit(result, mapping_registry)
+ (0...result.size).collect { |idx|
+ mapping_registry.obj2soap(result[idx], @doc_response_qnames[idx])
+ }
+ end
+
+ def check_style(style)
+ unless [:rpc, :document].include?(style)
+ raise ArgumentError.new("unknown style: #{style}")
+ end
+ end
+
+ def check_use(use)
+ unless [:encoded, :literal].include?(use)
+ raise ArgumentError.new("unknown use: #{use}")
+ end
+ end
+ end
+
+ class ApplicationScopeOperation < Operation
+ def initialize(soapaction, receiver, name, param_def, opt)
+ super(soapaction, name, param_def, opt)
+ @receiver = receiver
+ end
+
+ private
+
+ def receiver
+ @receiver
+ end
+ end
+
+ class RequestScopeOperation < Operation
+ def initialize(soapaction, receiver_factory, name, param_def, opt)
+ super(soapaction, name, param_def, opt)
+ unless receiver_factory.respond_to?(:create)
+ raise TypeError.new("factory must respond to 'create'")
+ end
+ @receiver_factory = receiver_factory
+ end
+
+ private
+
+ def receiver
+ @receiver_factory.create
end
end
end
diff --git a/lib/soap/rpc/soaplet.rb b/lib/soap/rpc/soaplet.rb
index 8f18c53d3a..4d2538f0b3 100644
--- a/lib/soap/rpc/soaplet.rb
+++ b/lib/soap/rpc/soaplet.rb
@@ -1,5 +1,5 @@
# SOAP4R - SOAP handler servlet for WEBrick
-# Copyright (C) 2001, 2002, 2003, 2004 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2001-2005 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
# This program is copyrighted free software by NAKAMURA, Hiroshi. You can
# redistribute it and/or modify it under the same terms of Ruby's license;
@@ -14,7 +14,23 @@ begin
require 'stringio'
require 'zlib'
rescue LoadError
- STDERR.puts "Loading stringio or zlib failed. No gzipped response support." if $DEBUG
+ warn("Loading stringio or zlib failed. No gzipped response supported.") if $DEBUG
+end
+
+
+warn("Overriding WEBrick::Log#debug") if $DEBUG
+require 'webrick/log'
+module WEBrick
+ class Log < BasicLog
+ alias __debug debug
+ def debug(msg = nil)
+ if block_given? and msg.nil?
+ __debug(yield)
+ else
+ __debug(msg)
+ end
+ end
+ end
end
@@ -24,60 +40,27 @@ module RPC
class SOAPlet < WEBrick::HTTPServlet::AbstractServlet
public
- attr_reader :app_scope_router
attr_reader :options
- def initialize
- @rpc_router_map = {}
- @app_scope_router = ::SOAP::RPC::Router.new(self.class.name)
- @headerhandlerfactory = []
- @app_scope_headerhandler = nil
+ def initialize(router = nil)
+ @router = router || ::SOAP::RPC::Router.new(self.class.name)
@options = {}
+ @config = {}
end
- def allow_content_encoding_gzip=(allow)
- @options[:allow_content_encoding_gzip] = allow
- end
-
- # Add servant factory whose object has request scope. A servant object is
- # instanciated for each request.
- #
- # Bear in mind that servant factories are distinguished by HTTP SOAPAction
- # header in request. Client which calls request-scoped servant must have a
- # SOAPAction header which is a namespace of the servant factory.
- # I mean, use Driver#add_method_with_soapaction instead of Driver#add_method
- # at client side.
- #
- # A factory must respond to :create.
- #
- def add_rpc_request_servant(factory, namespace, mapping_registry = nil)
- unless factory.respond_to?(:create)
- raise TypeError.new("factory must respond to 'create'")
- end
- router = setup_rpc_request_router(namespace)
- router.factory = factory
- router.mapping_registry = mapping_registry
- end
-
- # Add servant object which has application scope.
- def add_rpc_servant(obj, namespace)
- router = @app_scope_router
- SOAPlet.add_rpc_servant_to_router(router, obj, namespace)
- add_rpc_router(namespace, router)
+ # for backward compatibility
+ def app_scope_router
+ @router
end
- alias add_servant add_rpc_servant
- def add_rpc_request_headerhandler(factory)
- unless factory.respond_to?(:create)
- raise TypeError.new("factory must respond to 'create'")
- end
- @headerhandlerfactory << factory
+ # for backward compatibility
+ def add_servant(obj, namespace)
+ @router.add_rpc_servant(obj, namespace)
end
- def add_rpc_headerhandler(obj)
- @app_scope_headerhandler = obj
+ def allow_content_encoding_gzip=(allow)
+ @options[:allow_content_encoding_gzip] = allow
end
- alias add_headerhandler add_rpc_headerhandler
###
## Servlet interfaces for WEBrick.
@@ -93,111 +76,63 @@ public
def do_GET(req, res)
res.header['Allow'] = 'POST'
- raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed."
+ raise WEBrick::HTTPStatus::MethodNotAllowed, "GET request not allowed"
end
def do_POST(req, res)
- @config[:Logger].debug { "SOAP request: " + req.body }
- soapaction = parse_soapaction(req.meta_vars['HTTP_SOAPACTION'])
- router = lookup_router(soapaction)
- with_headerhandler(router) do |router|
- begin
- conn_data = ::SOAP::StreamHandler::ConnectionData.new
- conn_data.receive_string = req.body
- conn_data.receive_contenttype = req['content-type']
- conn_data = router.route(conn_data)
- res['content-type'] = conn_data.send_contenttype
- if conn_data.is_fault
- res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
- end
- if outstring = encode_gzip(req, conn_data.send_string)
- res['content-encoding'] = 'gzip'
- res['content-length'] = outstring.size
- res.body = outstring
- else
- res.body = conn_data.send_string
- end
- rescue Exception => e
- conn_data = router.create_fault_response(e)
- res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
- res.body = conn_data.send_string
- res['content-type'] = conn_data.send_contenttype || "text/xml"
- end
+ logger.debug { "SOAP request: " + req.body } if logger
+ begin
+ conn_data = ::SOAP::StreamHandler::ConnectionData.new
+ setup_req(conn_data, req)
+ conn_data = @router.route(conn_data)
+ setup_res(conn_data, req, res)
+ rescue Exception => e
+ conn_data = @router.create_fault_response(e)
+ res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
+ res.body = conn_data.send_string
+ res['content-type'] = conn_data.send_contenttype || "text/xml"
end
-
if res.body.is_a?(IO)
res.chunked = true
- @config[:Logger].debug { "SOAP response: (chunked response not logged)" }
+ logger.debug { "SOAP response: (chunked response not logged)" } if logger
else
- @config[:Logger].debug { "SOAP response: " + res.body }
+ logger.debug { "SOAP response: " + res.body } if logger
end
end
private
- class RequestRouter < ::SOAP::RPC::Router
- attr_accessor :factory
-
- def initialize(style = :rpc, namespace = nil)
- super(namespace)
- @style = style
- @namespace = namespace
- @factory = nil
- end
-
- def route(soap_string)
- obj = @factory.create
- namespace = self.actor
- router = ::SOAP::RPC::Router.new(@namespace)
- if @style == :rpc
- SOAPlet.add_rpc_servant_to_router(router, obj, namespace)
- else
- raise RuntimeError.new("'document' style not supported.")
- end
- router.route(soap_string)
- end
- end
-
- def setup_rpc_request_router(namespace)
- router = @rpc_router_map[namespace] || RequestRouter.new(:rpc, namespace)
- add_rpc_router(namespace, router)
- router
+ def logger
+ @config[:Logger]
end
- def add_rpc_router(namespace, router)
- @rpc_router_map[namespace] = router
+ def setup_req(conn_data, req)
+ conn_data.receive_string = req.body
+ conn_data.receive_contenttype = req['content-type']
+ conn_data.soapaction = parse_soapaction(req.meta_vars['HTTP_SOAPACTION'])
end
- def parse_soapaction(soapaction)
- if /^"(.*)"$/ =~ soapaction
- soapaction = $1
- end
- if soapaction.empty?
- return nil
+ def setup_res(conn_data, req, res)
+ res['content-type'] = conn_data.send_contenttype
+ if conn_data.is_fault
+ res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
end
- soapaction
- end
-
- def lookup_router(namespace)
- if namespace
- @rpc_router_map[namespace] || @app_scope_router
+ if outstring = encode_gzip(req, conn_data.send_string)
+ res['content-encoding'] = 'gzip'
+ res['content-length'] = outstring.size
+ res.body = outstring
else
- @app_scope_router
+ res.body = conn_data.send_string
end
end
- def with_headerhandler(router)
- if @app_scope_headerhandler and
- !router.headerhandler.include?(@app_scope_headerhandler)
- router.headerhandler.add(@app_scope_headerhandler)
- end
- handlers = @headerhandlerfactory.collect { |f| f.create }
- begin
- handlers.each { |h| router.headerhandler.add(h) }
- yield(router)
- ensure
- handlers.each { |h| router.headerhandler.delete(h) }
+ def parse_soapaction(soapaction)
+ if !soapaction.nil? and !soapaction.empty?
+ if /^"(.+)"$/ =~ soapaction
+ return $1
+ end
end
+ nil
end
def encode_gzip(req, outstring)
@@ -219,32 +154,6 @@ private
req['accept-encoding'] and
req['accept-encoding'].split(/,\s*/).include?('gzip')
end
-
- class << self
- public
- def add_rpc_servant_to_router(router, obj, namespace)
- ::SOAP::RPC.defined_methods(obj).each do |name|
- begin
- add_rpc_servant_method_to_router(router, obj, namespace, name)
- rescue SOAP::RPC::MethodDefinitionError => e
- p e if $DEBUG
- end
- end
- end
-
- def add_rpc_servant_method_to_router(router, obj, namespace, name,
- style = :rpc, use = :encoded)
- qname = XSD::QName.new(namespace, name)
- soapaction = nil
- method = obj.method(name)
- param_def = ::SOAP::RPC::SOAPMethod.create_param_def(
- (1..method.arity.abs).collect { |i| "p#{ i }" })
- opt = {}
- opt[:request_style] = opt[:response_style] = style
- opt[:request_use] = opt[:response_use] = use
- router.add_operation(qname, soapaction, obj, name, param_def, opt)
- end
- end
end