summaryrefslogtreecommitdiff
path: root/lib/soap
diff options
context:
space:
mode:
authornahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-07-03 15:29:32 +0000
committernahi <nahi@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2004-07-03 15:29:32 +0000
commitab31bf0d4d44942e46d98d8848b788ac6df32a46 (patch)
tree303d964b43831f02c94f3970c9df2aca6e851edc /lib/soap
parentdf2066555d03b919be6f25fb7569abde8296d2f5 (diff)
* added files
* lib/soap/attachment.rb * lib/soap/header * lib/soap/mimemessage.rb * lib/soap/rpc/httpserver.rb * lib/wsdl/soap/cgiStubCreator.rb * lib/wsdl/soap/classDefCreator.rb * lib/wsdl/soap/classDefCreatorSupport.rb * lib/wsdl/soap/clientSkeltonCreator.rb * lib/wsdl/soap/driverCreator.rb * lib/wsdl/soap/mappingRegistryCreator.rb * lib/wsdl/soap/methodDefCreator.rb * lib/wsdl/soap/servantSkeltonCreator.rb * lib/wsdl/soap/standaloneServerStubCreator.rb * lib/wsdl/xmlSchema/enumeration.rb * lib/wsdl/xmlSchema/simpleRestriction.rb * lib/wsdl/xmlSchema/simpleType.rb * lib/xsd/codegen * lib/xsd/codegen.rb * sample/soap/authheader * sample/soap/raa2.4 * sample/soap/ssl * sample/soap/swa * sample/soap/whois.rb * sample/soap/calc/samplehttpd.conf * sample/soap/exchange/samplehttpd.conf * sample/soap/sampleStruct/samplehttpd.conf * sample/wsdl/raa2.4 * sample/wsdl/googleSearch/samplehttpd.conf * test/openssl/_test_ssl.rb * test/soap/header * test/soap/ssl * test/soap/struct * test/soap/swa * test/soap/wsdlDriver * test/wsdl/multiplefault.wsdl * test/wsdl/simpletype * test/wsdl/test_multiplefault.rb * modified files * lib/soap/baseData.rb * lib/soap/element.rb * lib/soap/generator.rb * lib/soap/marshal.rb * lib/soap/netHttpClient.rb * lib/soap/parser.rb * lib/soap/processor.rb * lib/soap/property.rb * lib/soap/soap.rb * lib/soap/streamHandler.rb * lib/soap/wsdlDriver.rb * lib/soap/encodingstyle/handler.rb * lib/soap/encodingstyle/literalHandler.rb * lib/soap/encodingstyle/soapHandler.rb * lib/soap/mapping/factory.rb * lib/soap/mapping/mapping.rb * lib/soap/mapping/registry.rb * lib/soap/mapping/rubytypeFactory.rb * lib/soap/mapping/wsdlRegistry.rb * lib/soap/rpc/cgistub.rb * lib/soap/rpc/driver.rb * lib/soap/rpc/element.rb * lib/soap/rpc/proxy.rb * lib/soap/rpc/router.rb * lib/soap/rpc/soaplet.rb * lib/soap/rpc/standaloneServer.rb * lib/wsdl/data.rb * lib/wsdl/definitions.rb * lib/wsdl/operation.rb * lib/wsdl/parser.rb * lib/wsdl/soap/definitions.rb * lib/wsdl/xmlSchema/complexContent.rb * lib/wsdl/xmlSchema/complexType.rb * lib/wsdl/xmlSchema/data.rb * lib/wsdl/xmlSchema/parser.rb * lib/wsdl/xmlSchema/schema.rb * lib/xsd/datatypes.rb * lib/xsd/qname.rb * sample/soap/calc/httpd.rb * sample/soap/exchange/httpd.rb * sample/soap/sampleStruct/httpd.rb * sample/soap/sampleStruct/server.rb * sample/wsdl/amazon/AmazonSearch.rb * sample/wsdl/amazon/AmazonSearchDriver.rb * sample/wsdl/googleSearch/httpd.rb * test/soap/test_basetype.rb * test/soap/test_property.rb * test/soap/test_streamhandler.rb * test/soap/calc/test_calc.rb * test/soap/calc/test_calc2.rb * test/soap/calc/test_calc_cgi.rb * test/soap/helloworld/test_helloworld.rb * test/wsdl/test_emptycomplextype.rb * test/wsdl/axisArray/test_axisarray.rb * test/wsdl/datetime/test_datetime.rb * test/wsdl/raa/test_raa.rb * test/xsd/test_xmlschemaparser.rb * test/xsd/test_xsd.rb * summary * add SOAP Header mustUnderstand support. * add HTTP client SSL configuration and Cookies support (works completely with http-access2). * add header handler for handling sending/receiving SOAP Header. * map Ruby's anonymous Struct to common SOAP Struct in SOAP Object Model. it caused error. * add WSDL simpleType support to restrict lexical value space. * add SOAP with Attachment support. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@6567 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/soap')
-rw-r--r--lib/soap/baseData.rb168
-rw-r--r--lib/soap/element.rb70
-rw-r--r--lib/soap/encodingstyle/handler.rb4
-rw-r--r--lib/soap/encodingstyle/literalHandler.rb50
-rw-r--r--lib/soap/encodingstyle/soapHandler.rb93
-rw-r--r--lib/soap/generator.rb15
-rw-r--r--lib/soap/mapping/factory.rb1
-rw-r--r--lib/soap/mapping/mapping.rb24
-rw-r--r--lib/soap/mapping/registry.rb99
-rw-r--r--lib/soap/mapping/rubytypeFactory.rb73
-rw-r--r--lib/soap/mapping/wsdlRegistry.rb48
-rw-r--r--lib/soap/marshal.rb9
-rw-r--r--lib/soap/netHttpClient.rb14
-rw-r--r--lib/soap/parser.rb15
-rw-r--r--lib/soap/processor.rb16
-rw-r--r--lib/soap/property.rb88
-rw-r--r--lib/soap/rpc/cgistub.rb52
-rw-r--r--lib/soap/rpc/driver.rb57
-rw-r--r--lib/soap/rpc/element.rb5
-rw-r--r--lib/soap/rpc/proxy.rb56
-rw-r--r--lib/soap/rpc/router.rb110
-rw-r--r--lib/soap/rpc/soaplet.rb108
-rw-r--r--lib/soap/rpc/standaloneServer.rb102
-rw-r--r--lib/soap/soap.rb5
-rw-r--r--lib/soap/streamHandler.rb116
-rw-r--r--lib/soap/wsdlDriver.rb182
26 files changed, 1019 insertions, 561 deletions
diff --git a/lib/soap/baseData.rb b/lib/soap/baseData.rb
index 91f5a0433f..49c1d2d1f4 100644
--- a/lib/soap/baseData.rb
+++ b/lib/soap/baseData.rb
@@ -1,5 +1,5 @@
# soap/baseData.rb: SOAP4R - Base type library
-# Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2000, 2001, 2003, 2004 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;
@@ -30,20 +30,10 @@ end
###
-## Marker of SOAP/DM types.
+## for SOAP type(base and compound)
#
-module SOAPType; end
-
-
-###
-## Mix-in module for SOAP base type instances.
-#
-module SOAPBasetype
- include SOAPType
- include SOAP
-
+module SOAPType
attr_accessor :encodingstyle
-
attr_accessor :elename
attr_accessor :id
attr_reader :precedents
@@ -51,55 +41,53 @@ module SOAPBasetype
attr_accessor :parent
attr_accessor :position
attr_reader :extraattr
+ attr_accessor :definedtype
-public
-
- def initialize(*vars)
- super(*vars)
+ def initialize(*arg)
+ super(*arg)
@encodingstyle = nil
@elename = XSD::QName.new
@id = nil
@precedents = []
+ @root = false
@parent = nil
@position = nil
+ @definedtype = nil
@extraattr = {}
end
+
+ def rootnode
+ node = self
+ while node = node.parent
+ break if SOAPEnvelope === node
+ end
+ node
+ end
end
###
-## Mix-in module for SOAP compound type instances.
+## for SOAP base type
#
-module SOAPCompoundtype
+module SOAPBasetype
include SOAPType
include SOAP
- attr_accessor :encodingstyle
-
- attr_accessor :elename
- attr_accessor :id
- attr_reader :precedents
- attr_accessor :root
- attr_accessor :parent
- attr_accessor :position
- attr_reader :extraattr
+ def initialize(*arg)
+ super(*arg)
+ end
+end
- attr_accessor :definedtype
-public
+###
+## for SOAP compound type
+#
+module SOAPCompoundtype
+ include SOAPType
+ include SOAP
- def initialize(type)
- super()
- @type = type
- @encodingstyle = nil
- @elename = XSD::QName.new
- @id = nil
- @precedents = []
- @root = false
- @parent = nil
- @position = nil
- @definedtype = nil
- @extraattr = {}
+ def initialize(*arg)
+ super(*arg)
end
end
@@ -114,20 +102,14 @@ class SOAPReference < XSD::NSDBase
public
attr_accessor :refid
- attr_accessor :elename
# Override the definition in SOAPBasetype.
- def initialize(refid = nil)
+ def initialize(obj = nil)
super()
@type = XSD::QName.new
- @encodingstyle = nil
- @elename = XSD::QName.new
- @id = nil
- @precedents = []
- @root = false
- @parent = nil
- @refid = refid
+ @refid = nil
@obj = nil
+ __setobj__(obj) if obj
end
def __getobj__
@@ -136,7 +118,7 @@ public
def __setobj__(obj)
@obj = obj
- @refid = SOAPReference.create_refid(@obj)
+ @refid = @obj.id || SOAPReference.create_refid(@obj)
@obj.id = @refid unless @obj.id
@obj.precedents << self
# Copies NSDBase information
@@ -159,17 +141,54 @@ public
end
end
- def self.decode(elename, refid)
+ def refidstr
+ '#' + @refid
+ end
+
+ def self.create_refid(obj)
+ 'id' + obj.__id__.to_s
+ end
+
+ def self.decode(elename, refidstr)
+ if /\A#(.*)\z/ =~ refidstr
+ refid = $1
+ elsif /\Acid:(.*)\z/ =~ refidstr
+ refid = $1
+ else
+ raise ArgumentError.new("illegal refid #{refidstr}")
+ end
d = super(elename)
d.refid = refid
d
end
+end
- def self.create_refid(obj)
- 'id' << obj.__id__.to_s
+
+class SOAPExternalReference < XSD::NSDBase
+ include SOAPBasetype
+ extend SOAPModuleUtils
+
+ def initialize
+ super()
+ @type = XSD::QName.new
+ end
+
+ def referred
+ rootnode.external_content[external_contentid] = self
+ end
+
+ def refidstr
+ 'cid:' + external_contentid
+ end
+
+private
+
+ def external_contentid
+ raise NotImplementedError.new
end
end
+
class SOAPNil < XSD::XSDNil
include SOAPBasetype
extend SOAPModuleUtils
@@ -326,7 +345,8 @@ class SOAPStruct < XSD::NSDBase
public
def initialize(type = nil)
- super(type || XSD::QName.new)
+ super()
+ @type = type || XSD::QName.new
@array = []
@data = []
end
@@ -362,6 +382,7 @@ public
def []=(idx, data)
if @array.include?(idx)
+ data.parent = self if data.respond_to?(:parent=)
@data[@array.index(idx)] = data
else
add(idx, data)
@@ -401,31 +422,42 @@ private
@array.push(name)
value.elename = value.elename.dup_name(name)
@data.push(value)
+ value.parent = self if value.respond_to?(:parent=)
+ value
end
end
-# SOAPElement is not typed so it does not derive NSDBase.
+# SOAPElement is not typed so it is not derived from NSDBase.
class SOAPElement
include Enumerable
attr_accessor :encodingstyle
- attr_accessor :extraattr
+
+ attr_accessor :elename
+ attr_accessor :id
attr_reader :precedents
+ attr_accessor :root
+ attr_accessor :parent
+ attr_accessor :position
+ attr_accessor :extraattr
attr_accessor :qualified
- attr_accessor :elename
def initialize(elename, text = nil)
if !elename.is_a?(XSD::QName)
elename = XSD::QName.new(nil, elename)
end
@encodingstyle = LiteralNamespace
- @extraattr = {}
+ @elename = elename
+ @id = nil
@precedents = []
+ @root = false
+ @parent = nil
+ @position = nil
+ @extraattr = {}
@qualified = false
- @elename = elename
@array = []
@data = []
@@ -450,6 +482,7 @@ class SOAPElement
def []=(idx, data)
if @array.include?(idx)
+ data.parent = self if data.respond_to?(:parent=)
@data[@array.index(idx)] = data
else
add(data)
@@ -470,7 +503,7 @@ class SOAPElement
else
hash = {}
each do |k, v|
- hash[k] = v.to_obj
+ hash[k] = v.is_a?(SOAPElement) ? v.to_obj : v.to_s
end
hash
end
@@ -483,8 +516,7 @@ class SOAPElement
end
def self.decode(elename)
- o = SOAPElement.new
- o.elename = elename
+ o = SOAPElement.new(elename)
o
end
@@ -493,7 +525,7 @@ class SOAPElement
if hash_or_string.is_a?(Hash)
hash_or_string.each do |k, v|
child = self.from_obj(v)
- child.elename = XSD::QName.new(nil, k)
+ child.elename = k.is_a?(XSD::QName) ? k : XSD::QName.new(nil, k.to_s)
o.add(child)
end
else
@@ -508,6 +540,8 @@ private
add_accessor(name)
@array.push(name)
@data.push(value)
+ value.parent = self if value.respond_to?(:parent=)
+ value
end
def add_accessor(name)
@@ -550,7 +584,8 @@ public
attr_reader :arytype
def initialize(type = nil, rank = 1, arytype = nil)
- super(type || XSD::QName.new)
+ super()
+ @type = type || XSD::QName.new
@rank = rank
@data = Array.new
@sparse = false
@@ -609,6 +644,7 @@ public
end
@offset = idxary
+ value.parent = self if value.respond_to?(:parent=)
offsetnext
end
diff --git a/lib/soap/element.rb b/lib/soap/element.rb
index ae600a08e2..1494cd61dd 100644
--- a/lib/soap/element.rb
+++ b/lib/soap/element.rb
@@ -1,5 +1,5 @@
# SOAP4R - SOAP elements library
-# Copyright (C) 2000, 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2000, 2001, 2003, 2004 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;
@@ -71,6 +71,10 @@ public
self.faultactor.elename = EleFaultActorName if self.faultactor
self.detail.elename = EleFaultDetailName if self.detail
end
+ faultcode.parent = self if faultcode
+ faultstring.parent = self if faultstring
+ faultactor.parent = self if faultactor
+ detail.parent = self if detail
end
def encode(generator, ns, attrs = {})
@@ -91,8 +95,6 @@ end
class SOAPBody < SOAPStruct
include SOAPEnvelopeElement
-public
-
def initialize(data = nil, is_fault = false)
super(nil)
@elename = EleBodyName
@@ -138,38 +140,39 @@ class SOAPHeaderItem < XSD::NSDBase
public
- attr_accessor :content
+ attr_accessor :element
attr_accessor :mustunderstand
attr_accessor :encodingstyle
- def initialize(content, mustunderstand = true, encodingstyle = nil)
- super(nil)
- @content = content
+ def initialize(element, mustunderstand = true, encodingstyle = nil)
+ super()
+ @type = nil
+ @element = element
@mustunderstand = mustunderstand
- @encodingstyle = encodingstyle || LiteralNamespace
+ @encodingstyle = encodingstyle
+ element.parent = self if element
end
def encode(generator, ns, attrs = {})
attrs.each do |key, value|
- @content.attr[key] = value
+ @element.extraattr[key] = value
end
- @content.attr[ns.name(EnvelopeNamespace, AttrMustUnderstand)] =
+ @element.extraattr[ns.name(AttrMustUnderstandName)] =
(@mustunderstand ? '1' : '0')
if @encodingstyle
- @content.attr[ns.name(EnvelopeNamespace, AttrEncodingStyle)] =
- @encodingstyle
+ @element.extraattr[ns.name(AttrEncodingStyleName)] = @encodingstyle
end
- @content.encodingstyle = @encodingstyle if !@content.encodingstyle
- yield(@content, true)
+ @element.encodingstyle = @encodingstyle if !@element.encodingstyle
+ yield(@element, true)
end
end
-class SOAPHeader < SOAPArray
+class SOAPHeader < SOAPStruct
include SOAPEnvelopeElement
- def initialize()
- super(nil, 1) # rank == 1
+ def initialize
+ super(nil)
@elename = EleHeaderName
@encodingstyle = nil
end
@@ -183,9 +186,17 @@ class SOAPHeader < SOAPArray
generator.encode_tag_end(name, true)
end
+ def add(name, value)
+ mu = (value.extraattr[AttrMustUnderstandName] == '1')
+ encstyle = value.extraattr[AttrEncodingStyleName]
+ item = SOAPHeaderItem.new(value, mu, encstyle)
+ super(name, item)
+ end
+
def length
@data.length
end
+ alias size length
end
@@ -193,15 +204,30 @@ class SOAPEnvelope < XSD::NSDBase
include SOAPEnvelopeElement
include SOAPCompoundtype
- attr_accessor :header
- attr_accessor :body
+ attr_reader :header
+ attr_reader :body
+ attr_reader :external_content
def initialize(header = nil, body = nil)
- super(nil)
+ super()
+ @type = nil
@elename = EleEnvelopeName
@encodingstyle = nil
@header = header
@body = body
+ @external_content = {}
+ header.parent = self if header
+ body.parent = self if body
+ end
+
+ def header=(header)
+ header.parent = self
+ @header = header
+ end
+
+ def body=(body)
+ body.parent = self
+ @body = body
end
def encode(generator, ns, attrs = {})
@@ -215,6 +241,10 @@ class SOAPEnvelope < XSD::NSDBase
generator.encode_tag_end(name, true)
end
+
+ def to_ary
+ [header, body]
+ end
end
diff --git a/lib/soap/encodingstyle/handler.rb b/lib/soap/encodingstyle/handler.rb
index 8ea23ef146..7bf65a2fd5 100644
--- a/lib/soap/encodingstyle/handler.rb
+++ b/lib/soap/encodingstyle/handler.rb
@@ -44,8 +44,8 @@ class Handler
attr_reader :charset
attr_accessor :generate_explicit_type
- def decode_typemap=(complextypes)
- @decode_typemap = complextypes
+ def decode_typemap=(definedtypes)
+ @decode_typemap = definedtypes
end
def initialize(charset)
diff --git a/lib/soap/encodingstyle/literalHandler.rb b/lib/soap/encodingstyle/literalHandler.rb
index 995e1a5361..72a10b2daa 100644
--- a/lib/soap/encodingstyle/literalHandler.rb
+++ b/lib/soap/encodingstyle/literalHandler.rb
@@ -1,5 +1,5 @@
# SOAP4R - XML Literal EncodingStyle handler library
-# Copyright (C) 2001, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2001, 2003, 2004 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;
@@ -41,14 +41,18 @@ class LiteralHandler < Handler
generator.encode_rawstring(data.to_s)
when XSD::XSDString
generator.encode_tag(name, attrs)
- generator.encode_string(@charset ? XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s)
+ str = data.to_s
+ str = XSD::Charset.encoding_to_xml(str, @charset) if @charset
+ generator.encode_string(str)
when XSD::XSDAnySimpleType
generator.encode_tag(name, attrs)
generator.encode_string(data.to_s)
when SOAPStruct
generator.encode_tag(name, attrs)
data.each do |key, value|
- value.elename.namespace = data.elename.namespace if !value.elename.namespace
+ if !value.elename.namespace
+ value.elename.namespace = data.elename.namespace
+ end
yield(value, true)
end
when SOAPArray
@@ -61,8 +65,6 @@ class LiteralHandler < Handler
generator.encode_tag(name, attrs.update(data.extraattr))
generator.encode_rawstring(data.text) if data.text
data.each do |key, value|
- value.elename.namespace = data.elename.namespace if !value.elename.namespace
- #yield(value, data.qualified)
yield(value, qualified)
end
else
@@ -76,7 +78,8 @@ class LiteralHandler < Handler
else
data.elename.name
end
- generator.encode_tag_end(name)
+ cr = data.is_a?(SOAPElement) && !data.text
+ generator.encode_tag_end(name, cr)
end
@@ -92,15 +95,17 @@ class LiteralHandler < Handler
end
class SOAPUnknown < SOAPTemporalObject
- def initialize(handler, elename)
+ def initialize(handler, elename, extraattr)
super()
@handler = handler
@elename = elename
+ @extraattr = extraattr
end
- def as_struct
- o = SOAPStruct.decode(@elename, XSD::AnyTypeName)
+ def as_element
+ o = SOAPElement.decode(@elename)
o.parent = @parent
+ o.extraattr.update(@extraattr)
@handler.decode_parent(@parent, o)
o
end
@@ -108,6 +113,7 @@ class LiteralHandler < Handler
def as_string
o = SOAPString.decode(@elename)
o.parent = @parent
+ o.extraattr.update(@extraattr)
@handler.decode_parent(@parent, o)
o
end
@@ -115,6 +121,7 @@ class LiteralHandler < Handler
def as_nil
o = SOAPNil.decode(@elename)
o.parent = @parent
+ o.extraattr.update(@extraattr)
@handler.decode_parent(@parent, o)
o
end
@@ -123,7 +130,7 @@ class LiteralHandler < Handler
def decode_tag(ns, elename, attrs, parent)
# ToDo: check if @textbuf is empty...
@textbuf = ''
- o = SOAPUnknown.new(self, elename)
+ o = SOAPUnknown.new(self, elename, decode_attrs(ns, attrs))
o.parent = parent
o
end
@@ -132,7 +139,7 @@ class LiteralHandler < Handler
o = node.node
if o.is_a?(SOAPUnknown)
newnode = if /\A\s*\z/ =~ @textbuf
- o.as_struct
+ o.as_element
else
o.as_string
end
@@ -149,6 +156,15 @@ class LiteralHandler < Handler
@textbuf << text
end
+ def decode_attrs(ns, attrs)
+ extraattr = {}
+ attrs.each do |key, value|
+ qname = ns.parse(key)
+ extraattr[qname] = value
+ end
+ extraattr
+ end
+
def decode_prologue
end
@@ -158,13 +174,18 @@ class LiteralHandler < Handler
def decode_parent(parent, node)
case parent.node
when SOAPUnknown
- newparent = parent.node.as_struct
+ newparent = parent.node.as_element
node.parent = newparent
parent.replace_node(newparent)
decode_parent(parent, node)
+ when SOAPElement
+ parent.node.add(node)
+ node.parent = parent.node
+
when SOAPStruct
- parent.node.add(node.name, node)
+ parent.node.add(node.elename.name, node)
+ node.parent = parent.node
when SOAPArray
if node.position
@@ -173,13 +194,14 @@ class LiteralHandler < Handler
else
parent.node.add(node)
end
+ node.parent = parent.node
when SOAPBasetype
raise EncodingStyleError.new("SOAP base type must not have a child.")
else
# SOAPUnknown does not have parent.
- # raise EncodingStyleError.new("Illegal parent: #{ parent }.")
+ raise EncodingStyleError.new("Illegal parent: #{ parent }.")
end
end
diff --git a/lib/soap/encodingstyle/soapHandler.rb b/lib/soap/encodingstyle/soapHandler.rb
index 9aa732535e..114060bd02 100644
--- a/lib/soap/encodingstyle/soapHandler.rb
+++ b/lib/soap/encodingstyle/soapHandler.rb
@@ -33,7 +33,7 @@ class SOAPHandler < Handler
attrs = encode_attrs(generator, ns, data, parent)
if parent && parent.is_a?(SOAPArray) && parent.position
- attrs[ns.name(AttrPositionName)] = '[' << parent.position.join(',') << ']'
+ attrs[ns.name(AttrPositionName)] = "[#{ parent.position.join(',') }]"
end
name = nil
@@ -46,14 +46,19 @@ class SOAPHandler < Handler
case data
when SOAPReference
- attrs['href'] = '#' << data.refid
+ attrs['href'] = data.refidstr
+ generator.encode_tag(name, attrs)
+ when SOAPExternalReference
+ data.referred
+ attrs['href'] = data.refidstr
generator.encode_tag(name, attrs)
when SOAPRawString
generator.encode_tag(name, attrs)
generator.encode_rawstring(data.to_s)
when XSD::XSDString
generator.encode_tag(name, attrs)
- generator.encode_string(@charset ? XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s)
+ generator.encode_string(@charset ?
+ XSD::Charset.encoding_to_xml(data.to_s, @charset) : data.to_s)
when XSD::XSDAnySimpleType
generator.encode_tag(name, attrs)
generator.encode_string(data.to_s)
@@ -202,16 +207,12 @@ class SOAPHandler < Handler
node.replace_node(newnode)
o = node.node
end
- if o.is_a?(SOAPCompoundtype)
- o.definedtype = nil
- end
-
decode_textbuf(o)
- @textbuf = ''
+ # unlink definedtype
+ o.definedtype = nil
end
def decode_text(ns, text)
- # @textbuf is set at decode_tag_end.
@textbuf << text
end
@@ -235,11 +236,9 @@ class SOAPHandler < Handler
end
parent.replace_node(newparent)
decode_parent(parent, node)
-
when SOAPStruct
parent.node.add(node.elename.name, node)
node.parent = parent.node
-
when SOAPArray
if node.position
parent.node[*(decode_arypos(node.position))] = node
@@ -248,10 +247,8 @@ class SOAPHandler < Handler
parent.node.add(node)
end
node.parent = parent.node
-
when SOAPBasetype
raise EncodingStyleError.new("SOAP base type must not have a child.")
-
else
raise EncodingStyleError.new("Illegal parent: #{ parent.node }.")
end
@@ -269,7 +266,7 @@ private
def create_arytype(ns, data)
XSD::QName.new(data.arytype.namespace,
- content_typename(data.arytype.name) << '[' << data.size.join(',') << ']')
+ content_typename(data.arytype.name) + "[#{ data.size.join(',') }]")
end
def encode_attrs(generator, ns, data, parent)
@@ -320,10 +317,9 @@ private
def encode_attr_value(generator, ns, qname, value)
if value.is_a?(SOAPType)
- refid = SOAPReference.create_refid(value)
- value.id = refid
+ ref = SOAPReference.new(value)
generator.add_reftarget(qname.name, value)
- '#' + refid
+ ref.refidstr
else
value.to_s
end
@@ -349,8 +345,7 @@ private
typename = ns.parse(typestr)
typedef = @decode_typemap[typename]
if typedef
- return decode_defined_compoundtype(elename, typename, typedef,
- arytypestr)
+ return decode_definedtype(elename, typename, typedef, arytypestr)
end
end
return decode_tag_by_type(ns, elename, typestr, parent, arytypestr,
@@ -372,21 +367,42 @@ private
definedtype_name = parenttype.child_type(elename)
if definedtype_name and (klass = TypeMap[definedtype_name])
- return klass.decode(elename)
+ return decode_basetype(klass, elename)
elsif definedtype_name == XSD::AnyTypeName
return decode_tag_by_type(ns, elename, typestr, parent, arytypestr,
extraattr)
end
- typedef = definedtype_name ? @decode_typemap[definedtype_name] :
- parenttype.child_defined_complextype(elename)
- decode_defined_compoundtype(elename, definedtype_name, typedef, arytypestr)
+ if definedtype_name
+ typedef = @decode_typemap[definedtype_name]
+ else
+ typedef = parenttype.child_defined_complextype(elename)
+ end
+ decode_definedtype(elename, definedtype_name, typedef, arytypestr)
end
- def decode_defined_compoundtype(elename, typename, typedef, arytypestr)
+ def decode_definedtype(elename, typename, typedef, arytypestr)
unless typedef
raise EncodingStyleError.new("Unknown type '#{ typename }'.")
end
+ if typedef.is_a?(::WSDL::XMLSchema::SimpleType)
+ decode_defined_simpletype(elename, typename, typedef, arytypestr)
+ else
+ decode_defined_complextype(elename, typename, typedef, arytypestr)
+ end
+ end
+
+ def decode_basetype(klass, elename)
+ klass.decode(elename)
+ end
+
+ def decode_defined_simpletype(elename, typename, typedef, arytypestr)
+ o = decode_basetype(TypeMap[typedef.base], elename)
+ o.definedtype = typedef
+ o
+ end
+
+ def decode_defined_complextype(elename, typename, typedef, arytypestr)
case typedef.compoundtype
when :TYPE_STRUCT
o = SOAPStruct.decode(elename, typename)
@@ -406,7 +422,7 @@ private
o.definedtype = typedef
return o
end
- return nil
+ nil
end
def decode_tag_by_type(ns, elename, typestr, parent, arytypestr, extraattr)
@@ -431,7 +447,7 @@ private
end
if (klass = TypeMap[type])
- node = klass.decode(elename)
+ node = decode_basetype(klass, elename)
node.extraattr.update(extraattr)
return node
end
@@ -446,10 +462,12 @@ private
node.set_encoded(@textbuf)
when XSD::XSDString
if @charset
- node.set(XSD::Charset.encoding_from_xml(@textbuf, @charset))
- else
- node.set(@textbuf)
+ @textbuf = XSD::Charset.encoding_from_xml(@textbuf, @charset)
+ end
+ if node.definedtype
+ node.definedtype.check_lexical_format(@textbuf)
end
+ node.set(@textbuf)
when SOAPNil
# Nothing to do.
when SOAPBasetype
@@ -457,6 +475,7 @@ private
else
# Nothing to do...
end
+ @textbuf = ''
end
NilLiteralMap = {
@@ -526,7 +545,7 @@ private
def decode_attr_value(ns, qname, value)
if /\A#/ =~ value
- o = SOAPReference.new(value)
+ o = SOAPReference.decode(nil, value)
@refpool << o
o
else
@@ -544,16 +563,18 @@ private
while !@refpool.empty? && count > 0
@refpool = @refpool.find_all { |ref|
o = @idpool.find { |item|
- '#' + item.id == ref.refid
+ item.id == ref.refid
}
- unless o
- raise EncodingStyleError.new("Unresolved reference: #{ ref.refid }.")
- end
if o.is_a?(SOAPReference)
- true
- else
+ true # link of link.
+ elsif o
ref.__setobj__(o)
false
+ elsif o = ref.rootnode.external_content[ref.refid]
+ ref.__setobj__(o)
+ false
+ else
+ raise EncodingStyleError.new("Unresolved reference: #{ ref.refid }.")
end
}
count -= 1
diff --git a/lib/soap/generator.rb b/lib/soap/generator.rb
index dd868fb9db..edd90492c6 100644
--- a/lib/soap/generator.rb
+++ b/lib/soap/generator.rb
@@ -68,9 +68,8 @@ public
if @reftarget && !obj.precedents.empty?
add_reftarget(obj.elename.name, obj)
- ref = SOAPReference.new
+ ref = SOAPReference.new(obj)
ref.elename.name = obj.elename.name
- ref.__setobj__(obj)
obj.precedents.clear # Avoid cyclic delay.
obj.encodingstyle = parent.encodingstyle
# SOAPReference is encoded here.
@@ -91,9 +90,9 @@ public
raise FormatEncodeError.new("Element name not defined: #{ obj }.")
end
- handler.encode_data(self, ns, qualified, obj, parent) do |child, child_q|
+ handler.encode_data(self, ns, qualified, obj, parent) do |child, nextq|
indent_backup, @indent = @indent, @indent + ' '
- encode_data(ns.clone_ns, child_q, child, obj)
+ encode_data(ns.clone_ns, nextq, child, obj)
@indent = indent_backup
end
handler.encode_data_end(self, ns, qualified, obj, parent)
@@ -110,9 +109,9 @@ public
attrs = {}
if obj.is_a?(SOAPBody)
@reftarget = obj
- obj.encode(self, ns, attrs) do |child, child_q|
+ obj.encode(self, ns, attrs) do |child, nextq|
indent_backup, @indent = @indent, @indent + ' '
- encode_data(ns.clone_ns, child_q, child, obj)
+ encode_data(ns.clone_ns, nextq, child, obj)
@indent = indent_backup
end
@reftarget = nil
@@ -125,9 +124,9 @@ public
SOAPGenerator.assign_ns(attrs, ns, XSD::Namespace, XSDNamespaceTag)
end
end
- obj.encode(self, ns, attrs) do |child, child_q|
+ obj.encode(self, ns, attrs) do |child, nextq|
indent_backup, @indent = @indent, @indent + ' '
- encode_data(ns.clone_ns, child_q, child, obj)
+ encode_data(ns.clone_ns, nextq, child, obj)
@indent = indent_backup
end
end
diff --git a/lib/soap/mapping/factory.rb b/lib/soap/mapping/factory.rb
index fe6a6de7ae..6b9ac1eeaa 100644
--- a/lib/soap/mapping/factory.rb
+++ b/lib/soap/mapping/factory.rb
@@ -70,6 +70,7 @@ class Factory
end
def setiv2soap(node, obj, map)
+ # should we sort instance_variables?
obj.instance_variables.each do |var|
name = var.sub(/^@/, '')
node.add(Mapping.name2elename(name),
diff --git a/lib/soap/mapping/mapping.rb b/lib/soap/mapping/mapping.rb
index 4b68b811fc..db7ea607fd 100644
--- a/lib/soap/mapping/mapping.rb
+++ b/lib/soap/mapping/mapping.rb
@@ -68,24 +68,26 @@ module Mapping
md_ary
end
- def self.fault2exception(e, registry = nil)
+ def self.fault2exception(fault, registry = nil)
registry ||= Mapping::DefaultRegistry
- detail = if e.detail
- soap2obj(e.detail, registry) || ""
+ detail = if fault.detail
+ soap2obj(fault.detail, registry) || ""
else
""
end
if detail.is_a?(Mapping::SOAPException)
begin
- remote_backtrace = detail.to_e.backtrace
- raise detail.to_e
- rescue Exception => e2
- e2.set_backtrace(remote_backtrace + e2.backtrace)
+ e = detail.to_e
+ remote_backtrace = e.backtrace
+ e.set_backtrace(nil)
+ raise e # ruby sets current caller as local backtrace of e => e2.
+ rescue Exception => e
+ e.set_backtrace(remote_backtrace + e.backtrace[1..-1])
raise
end
else
- e.detail = detail
- e.set_backtrace(
+ fault.detail = detail
+ fault.set_backtrace(
if detail.is_a?(Array)
detail
else
@@ -98,9 +100,7 @@ module Mapping
def self._obj2soap(obj, registry, type = nil)
if referent = Thread.current[:SOAPMarshalDataKey][obj.__id__]
- soap_obj = SOAPReference.new
- soap_obj.__setobj__(referent)
- soap_obj
+ SOAPReference.new(referent)
else
registry.obj2soap(obj.class, obj, type)
end
diff --git a/lib/soap/mapping/registry.rb b/lib/soap/mapping/registry.rb
index 8142047724..1317d40cd6 100644
--- a/lib/soap/mapping/registry.rb
+++ b/lib/soap/mapping/registry.rb
@@ -44,14 +44,15 @@ class SOAPException; include Marshallable
if @cause.is_a?(::Exception)
@cause.extend(::SOAP::Mapping::MappedException)
return @cause
+ elsif @cause.respond_to?(:message) and @cause.respond_to?(:backtrace)
+ e = RuntimeError.new(@cause.message)
+ e.set_backtrace(@cause.backtrace)
+ return e
end
klass = Mapping.class_from_name(
Mapping.elename2name(@excn_type_name.to_s))
- if klass.nil?
- raise RuntimeError.new(@cause.message)
- end
- unless klass <= ::Exception
- raise NameError.new
+ if klass.nil? or not klass <= ::Exception
+ return RuntimeError.new(@cause.inspect)
end
obj = klass.new(@cause.message)
obj.extend(::SOAP::Mapping::MappedException)
@@ -62,50 +63,78 @@ end
# For anyType object: SOAP::Mapping::Object not ::Object
class Object; include Marshallable
- def set_property(name, value)
- var_name = name
- begin
- instance_eval <<-EOS
- def #{ var_name }
- @#{ var_name }
- end
+ def initialize
+ @__members = []
+ @__value_type = {}
+ end
- def #{ var_name }=(value)
- @#{ var_name } = value
- end
- EOS
- self.send(var_name + '=', value)
- rescue SyntaxError
- var_name = safe_name(var_name)
- retry
+ def [](name)
+ if @__members.include?(name)
+ self.__send__(name)
+ else
+ self.__send__(Object.safe_name(name))
end
+ end
+
+ def []=(name, value)
+ if @__members.include?(name)
+ self.__send__(name + '=', value)
+ else
+ self.__send__(Object.safe_name(name) + '=', value)
+ end
+ end
+ def __set_property(name, value)
+ var_name = name
+ unless @__members.include?(name)
+ var_name = __define_attr_accessor(var_name)
+ end
+ __set_property_value(var_name, value)
var_name
end
- def members
- instance_variables.collect { |str| str[1..-1] }
+ def __members
+ @__members
end
- def [](name)
- if self.respond_to?(name)
- self.send(name)
+private
+
+ def __set_property_value(name, value)
+ org = self.__send__(name)
+ case @__value_type[name]
+ when :single
+ self.__send__(name + '=', [org, value])
+ @__value_type[name] = :multi
+ when :multi
+ org << value
else
- self.send(safe_name(name))
+ self.__send__(name + '=', value)
+ @__value_type[name] = :single
end
+ value
end
- def []=(name, value)
- if self.respond_to?(name)
- self.send(name + '=', value)
- else
- self.send(safe_name(name) + '=', value)
+ def __define_attr_accessor(name)
+ var_name = name
+ begin
+ instance_eval <<-EOS
+ def #{ var_name }
+ @#{ var_name }
+ end
+
+ def #{ var_name }=(value)
+ @#{ var_name } = value
+ end
+ EOS
+ rescue SyntaxError
+ var_name = Object.safe_name(var_name)
+ retry
end
+ @__members << var_name
+ var_name
end
-private
-
- def safe_name(name)
+ def Object.safe_name(name)
require 'md5'
"var_" << MD5.new(name).hexdigest
end
@@ -309,7 +338,7 @@ class Registry
def add(obj_class, soap_class, factory, info = nil)
@map.add(obj_class, soap_class, factory, info)
end
- alias :set :add
+ alias set add
# This mapping registry ignores type hint.
def obj2soap(klass, obj, type_qname = nil)
diff --git a/lib/soap/mapping/rubytypeFactory.rb b/lib/soap/mapping/rubytypeFactory.rb
index f79bc78cc7..a46d93275f 100644
--- a/lib/soap/mapping/rubytypeFactory.rb
+++ b/lib/soap/mapping/rubytypeFactory.rb
@@ -38,7 +38,7 @@ class RubytypeFactory < Factory
def obj2soap(soap_class, obj, info, map)
param = nil
case obj
- when String
+ when ::String
unless @allow_original_mapping
return nil
end
@@ -47,7 +47,7 @@ class RubytypeFactory < Factory
param.extraattr[RubyTypeName] = obj.class.name
end
addiv2soapattr(param, obj, map)
- when Time
+ when ::Time
unless @allow_original_mapping
return nil
end
@@ -56,7 +56,7 @@ class RubytypeFactory < Factory
param.extraattr[RubyTypeName] = obj.class.name
end
addiv2soapattr(param, obj, map)
- when Array
+ when ::Array
unless @allow_original_mapping
return nil
end
@@ -65,19 +65,19 @@ class RubytypeFactory < Factory
param.extraattr[RubyTypeName] = obj.class.name
end
addiv2soapattr(param, obj, map)
- when NilClass
+ when ::NilClass
unless @allow_original_mapping
return nil
end
param = @basetype_factory.obj2soap(SOAPNil, obj, info, map)
addiv2soapattr(param, obj, map)
- when FalseClass, TrueClass
+ when ::FalseClass, ::TrueClass
unless @allow_original_mapping
return nil
end
param = @basetype_factory.obj2soap(SOAPBoolean, obj, info, map)
addiv2soapattr(param, obj, map)
- when Integer
+ when ::Integer
unless @allow_original_mapping
return nil
end
@@ -85,7 +85,7 @@ class RubytypeFactory < Factory
param ||= @basetype_factory.obj2soap(SOAPInteger, obj, info, map)
param ||= @basetype_factory.obj2soap(SOAPDecimal, obj, info, map)
addiv2soapattr(param, obj, map)
- when Float
+ when ::Float
unless @allow_original_mapping
return nil
end
@@ -94,7 +94,7 @@ class RubytypeFactory < Factory
param.extraattr[RubyTypeName] = obj.class.name
end
addiv2soapattr(param, obj, map)
- when Hash
+ when ::Hash
unless @allow_original_mapping
return nil
end
@@ -114,7 +114,7 @@ class RubytypeFactory < Factory
end
param.add('default', Mapping._obj2soap(obj.default, map))
addiv2soapattr(param, obj, map)
- when Regexp
+ when ::Regexp
unless @allow_original_mapping
return nil
end
@@ -150,7 +150,7 @@ class RubytypeFactory < Factory
end
param.add('options', SOAPInt.new(options))
addiv2soapattr(param, obj, map)
- when Range
+ when ::Range
unless @allow_original_mapping
return nil
end
@@ -163,29 +163,29 @@ class RubytypeFactory < Factory
param.add('end', Mapping._obj2soap(obj.end, map))
param.add('exclude_end', SOAP::SOAPBoolean.new(obj.exclude_end?))
addiv2soapattr(param, obj, map)
- when Class
+ when ::Class
unless @allow_original_mapping
return nil
end
if obj.to_s[0] == ?#
- raise TypeError.new("Can't dump anonymous class #{ obj }.")
+ raise TypeError.new("can't dump anonymous class #{ obj }")
end
param = SOAPStruct.new(TYPE_CLASS)
mark_marshalled_obj(obj, param)
param.add('name', SOAPString.new(obj.name))
addiv2soapattr(param, obj, map)
- when Module
+ when ::Module
unless @allow_original_mapping
return nil
end
if obj.to_s[0] == ?#
- raise TypeError.new("Can't dump anonymous module #{ obj }.")
+ raise TypeError.new("can't dump anonymous module #{ obj }")
end
param = SOAPStruct.new(TYPE_MODULE)
mark_marshalled_obj(obj, param)
param.add('name', SOAPString.new(obj.name))
addiv2soapattr(param, obj, map)
- when Symbol
+ when ::Symbol
unless @allow_original_mapping
return nil
end
@@ -193,28 +193,37 @@ class RubytypeFactory < Factory
mark_marshalled_obj(obj, param)
param.add('id', SOAPString.new(obj.id2name))
addiv2soapattr(param, obj, map)
- when Struct
+ when ::Struct
unless @allow_original_mapping
- return nil
- end
- param = SOAPStruct.new(TYPE_STRUCT)
- mark_marshalled_obj(obj, param)
- param.add('type', ele_type = SOAPString.new(obj.class.to_s))
- ele_member = SOAPStruct.new
- obj.members.each do |member|
- ele_member.add(Mapping.name2elename(member),
- Mapping._obj2soap(obj[member], map))
+ # treat it as an user defined class. [ruby-talk:104980]
+ #param = unknownobj2soap(soap_class, obj, info, map)
+ param = SOAPStruct.new(XSD::AnyTypeName)
+ mark_marshalled_obj(obj, param)
+ obj.members.each do |member|
+ param.add(Mapping.name2elename(member),
+ Mapping._obj2soap(obj[member], map))
+ end
+ else
+ param = SOAPStruct.new(TYPE_STRUCT)
+ mark_marshalled_obj(obj, param)
+ param.add('type', ele_type = SOAPString.new(obj.class.to_s))
+ ele_member = SOAPStruct.new
+ obj.members.each do |member|
+ ele_member.add(Mapping.name2elename(member),
+ Mapping._obj2soap(obj[member], map))
+ end
+ param.add('member', ele_member)
+ addiv2soapattr(param, obj, map)
end
- param.add('member', ele_member)
- addiv2soapattr(param, obj, map)
- when IO, Binding, Continuation, Data, Dir, File::Stat, MatchData, Method,
- Proc, Thread, ThreadGroup # from 1.8: Process::Status, UnboundMethod
+ when ::IO, ::Binding, ::Continuation, ::Data, ::Dir, ::File::Stat,
+ ::MatchData, Method, ::Proc, ::Thread, ::ThreadGroup
+ # from 1.8: Process::Status, UnboundMethod
return nil
when ::SOAP::Mapping::Object
param = SOAPStruct.new(XSD::AnyTypeName)
mark_marshalled_obj(obj, param)
addiv2soapattr(param, obj, map)
- when Exception
+ when ::Exception
typestr = Mapping.name2elename(obj.class.to_s)
param = SOAPStruct.new(XSD::QName.new(RubyTypeNamespace, typestr))
mark_marshalled_obj(obj, param)
@@ -249,7 +258,7 @@ private
def unknownobj2soap(soap_class, obj, info, map)
if obj.class.name.empty?
- raise TypeError.new("Can't dump anonymous class #{ obj }.")
+ raise TypeError.new("can't dump anonymous class #{ obj }")
end
singleton_class = class << obj; self; end
if !singleton_methods_true(obj).empty? or
@@ -369,7 +378,7 @@ private
obj = klass.new
mark_unmarshalled_obj(node, obj)
node.each do |name, value|
- obj.set_property(name, Mapping._soap2obj(value, map))
+ obj.__set_property(name, Mapping._soap2obj(value, map))
end
return true, obj
else
diff --git a/lib/soap/mapping/wsdlRegistry.rb b/lib/soap/mapping/wsdlRegistry.rb
index 66d16c6f90..64f49f2265 100644
--- a/lib/soap/mapping/wsdlRegistry.rb
+++ b/lib/soap/mapping/wsdlRegistry.rb
@@ -18,10 +18,10 @@ module Mapping
class WSDLRegistry
include TraverseSupport
- attr_reader :complextypes
+ attr_reader :definedtypes
- def initialize(complextypes, config = {})
- @complextypes = complextypes
+ def initialize(definedtypes, config = {})
+ @definedtypes = definedtypes
@config = config
@excn_handler_obj2soap = nil
# For mapping AnyType element.
@@ -37,27 +37,20 @@ class WSDLRegistry
soap_obj = SOAPNil.new
elsif obj.is_a?(XSD::NSDBase)
soap_obj = soap2soap(obj, type_qname)
- elsif (type = @complextypes[type_qname])
- case type.compoundtype
- when :TYPE_STRUCT
- soap_obj = struct2soap(obj, type_qname, type)
- when :TYPE_ARRAY
- soap_obj = array2soap(obj, type_qname, type)
- end
+ elsif type = @definedtypes[type_qname]
+ soap_obj = obj2type(obj, type)
elsif (type = TypeMap[type_qname])
soap_obj = base2soap(obj, type)
elsif type_qname == XSD::AnyTypeName
soap_obj = @rubytype_factory.obj2soap(nil, obj, nil, nil)
end
return soap_obj if soap_obj
-
if @excn_handler_obj2soap
soap_obj = @excn_handler_obj2soap.call(obj) { |yield_obj|
Mapping._obj2soap(yield_obj, self)
}
end
return soap_obj if soap_obj
-
raise MappingError.new("Cannot map #{ klass.name } to SOAP/OM.")
end
@@ -74,12 +67,12 @@ private
def soap2soap(obj, type_qname)
if obj.is_a?(SOAPBasetype)
obj
- elsif obj.is_a?(SOAPStruct) && (type = @complextypes[type_qname])
+ elsif obj.is_a?(SOAPStruct) && (type = @definedtypes[type_qname])
soap_obj = obj
mark_marshalled_obj(obj, soap_obj)
elements2soap(obj, soap_obj, type.content.elements)
soap_obj
- elsif obj.is_a?(SOAPArray) && (type = @complextypes[type_qname])
+ elsif obj.is_a?(SOAPArray) && (type = @definedtypes[type_qname])
soap_obj = obj
contenttype = type.child_type
mark_marshalled_obj(obj, soap_obj)
@@ -92,6 +85,33 @@ private
end
end
+ def obj2type(obj, type)
+ if type.is_a?(::WSDL::XMLSchema::SimpleType)
+ simple2soap(obj, type)
+ else
+ complex2soap(obj, type)
+ end
+ end
+
+ def simple2soap(obj, type)
+ o = base2soap(obj, TypeMap[type.base])
+ if type.restriction.enumeration.empty?
+ STDERR.puts("#{type.name}: simpleType which is not enum type not supported.")
+ return o
+ end
+ type.check_lexical_format(obj)
+ o
+ end
+
+ def complex2soap(obj, type)
+ case type.compoundtype
+ when :TYPE_STRUCT
+ struct2soap(obj, type.name, type)
+ when :TYPE_ARRAY
+ array2soap(obj, type.name, type)
+ end
+ end
+
def base2soap(obj, type)
soap_obj = nil
if type <= XSD::XSDString
diff --git a/lib/soap/marshal.rb b/lib/soap/marshal.rb
index 910ab24527..7202a6aba8 100644
--- a/lib/soap/marshal.rb
+++ b/lib/soap/marshal.rb
@@ -37,15 +37,16 @@ module Marshal
soap_obj = Mapping.obj2soap(obj, mapping_registry)
body = SOAPBody.new
body.add(elename, soap_obj)
- SOAP::Processor.marshal(nil, body, {}, io)
+ env = SOAPEnvelope.new(nil, body)
+ SOAP::Processor.marshal(env, {}, io)
end
def unmarshal(stream, mapping_registry = MarshalMappingRegistry)
- header, body = SOAP::Processor.unmarshal(stream)
- if body.nil?
+ env = SOAP::Processor.unmarshal(stream)
+ if env.nil?
raise ArgumentError.new("Illegal SOAP marshal format.")
end
- Mapping.soap2obj(body.root_node, mapping_registry)
+ Mapping.soap2obj(env.body.root_node, mapping_registry)
end
end
end
diff --git a/lib/soap/netHttpClient.rb b/lib/soap/netHttpClient.rb
index 4505de815e..1e9d71c5a3 100644
--- a/lib/soap/netHttpClient.rb
+++ b/lib/soap/netHttpClient.rb
@@ -34,6 +34,10 @@ class NetHttpClient
@session_manager = SessionManager.new
@no_proxy = nil
end
+
+ def test_loopback_response
+ raise NotImplementedError.new("not supported for now")
+ end
def proxy=(proxy_str)
if proxy_str.nil?
@@ -54,7 +58,11 @@ class NetHttpClient
end
def set_cookie_store(filename)
- # ignored.
+ raise NotImplementedError.new
+ end
+
+ def save_cookie_store(filename)
+ raise NotImplementedError.new
end
def reset(url)
@@ -70,8 +78,8 @@ class NetHttpClient
extra = header.dup
extra['User-Agent'] = @agent if @agent
res = start(url) { |http|
- http.post(url.request_uri, req_body, extra)
- }
+ http.post(url.request_uri, req_body, extra)
+ }
Response.new(res)
end
diff --git a/lib/soap/parser.rb b/lib/soap/parser.rb
index 1395be55ec..14704a6d9b 100644
--- a/lib/soap/parser.rb
+++ b/lib/soap/parser.rb
@@ -65,6 +65,7 @@ public
attr_accessor :allow_unqualified_element
def initialize(opt = {})
+ @opt = opt
@parser = XSD::XMLParser.create_parser(self, opt)
@parsestack = nil
@lastnode = nil
@@ -116,7 +117,13 @@ public
encodingstyle = find_encodingstyle(ns, attrs)
# Children's encodingstyle is derived from its parent.
- encodingstyle ||= parent_encodingstyle || @default_encodingstyle
+ if encodingstyle.nil?
+ if parent.node.is_a?(SOAPHeader)
+ encodingstyle = LiteralNamespace
+ else
+ encodingstyle = parent_encodingstyle || @default_encodingstyle
+ end
+ end
node = decode_tag(ns, name, attrs, parent, encodingstyle)
@@ -201,6 +208,11 @@ private
o = nil
if ele.name == EleEnvelope
o = SOAPEnvelope.new
+ if ext = @opt[:external_content]
+ ext.each do |k, v|
+ o.external_content[k] = v
+ end
+ end
elsif ele.name == EleHeader
unless parent.node.is_a?(SOAPEnvelope)
raise FormatDecodeError.new("Header should be a child of Envelope.")
@@ -220,7 +232,6 @@ private
o = SOAPFault.new
parent.node.fault = o
end
- o.parent = parent if o
o
end
diff --git a/lib/soap/processor.rb b/lib/soap/processor.rb
index 9cf00e8340..3c6dbedf2f 100644
--- a/lib/soap/processor.rb
+++ b/lib/soap/processor.rb
@@ -25,20 +25,18 @@ module Processor
class << self
public
- def marshal(header, body, opt = {}, io = nil)
- env = SOAPEnvelope.new(header, body)
+ def marshal(env, opt = {}, io = nil)
generator = create_generator(opt)
- generator.generate(env, io)
+ marshalled_str = generator.generate(env, io)
+ unless env.external_content.empty?
+ opt[:external_content] = env.external_content
+ end
+ marshalled_str
end
def unmarshal(stream, opt = {})
parser = create_parser(opt)
- env = parser.parse(stream)
- if env
- return env.header, env.body
- else
- return nil, nil
- end
+ parser.parse(stream)
end
def default_parser_option=(rhs)
diff --git a/lib/soap/property.rb b/lib/soap/property.rb
index 079c294a77..113cc64f3c 100644
--- a/lib/soap/property.rb
+++ b/lib/soap/property.rb
@@ -34,22 +34,24 @@ module SOAP
class Property
include Enumerable
- def self.load(stream)
- new.load(stream)
+ module Util
+ def const_from_name(fqname)
+ fqname.split("::").inject(Kernel) { |klass, name| klass.const_get(name) }
+ end
+ module_function :const_from_name
+
+ def require_from_name(fqname)
+ require File.join(fqname.split("::").collect { |ele| ele.downcase })
+ end
+ module_function :require_from_name
end
- def self.open(filename)
- File.open(filename) { |f| load(f) }
+ def self.load(stream)
+ new.load(stream)
end
- # find property from $:.
def self.loadproperty(propname)
- $:.each do |path|
- if File.file?(file = File.join(path, propname))
- return open(file)
- end
- end
- nil
+ new.loadproperty(propname)
end
def initialize
@@ -87,6 +89,17 @@ class Property
self
end
+ # find property from $:.
+ def loadproperty(propname)
+ return loadpropertyfile(propname) if File.file?(propname)
+ $:.each do |path|
+ if File.file?(file = File.join(path, propname))
+ return loadpropertyfile(file)
+ end
+ end
+ nil
+ end
+
# name: a Symbol, String or an Array
def [](name)
referent(name_to_a(name))
@@ -95,10 +108,10 @@ class Property
# name: a Symbol, String or an Array
# value: an Object
def []=(name, value)
- hooks = assign(name_to_a(name), value)
- normalized_name = normalize_name(name)
+ name_pair = name_to_a(name).freeze
+ hooks = assign(name_pair, value)
hooks.each do |hook|
- hook.call(normalized_name, value)
+ hook.call(name_pair, value)
end
value
end
@@ -109,13 +122,15 @@ class Property
self[generate_new_key] = value
end
- # name: a Symbol, String or an Array. nil means hook to the root
+ # name: a Symbol, String or an Array; nil means hook to the root
+ # cascade: true/false; for cascading hook of sub key
# hook: block which will be called with 2 args, name and value
- def add_hook(name = nil, &hook)
- if name.nil?
- assign_self_hook(&hook)
+ def add_hook(name = nil, cascade = false, &hook)
+ if name == nil or name == true or name == false
+ cascade = name
+ assign_self_hook(cascade, &hook)
else
- assign_hook(name_to_a(name), &hook)
+ assign_hook(name_to_a(name), cascade, &hook)
end
end
@@ -192,14 +207,18 @@ protected
@store[key] = value
end
- def local_hook(key)
- @self_hook + (@hook[key] || NO_HOOK)
+ def local_hook(key, direct)
+ hooks = []
+ (@self_hook + (@hook[key] || NO_HOOK)).each do |hook, cascade|
+ hooks << hook if direct or cascade
+ end
+ hooks
end
- def local_assign_hook(key, &hook)
+ def local_assign_hook(key, cascade, &hook)
check_lock(key)
@store[key] ||= nil
- (@hook[key] ||= []) << hook
+ (@hook[key] ||= []) << [hook, cascade]
end
private
@@ -217,23 +236,23 @@ private
hook = NO_HOOK
ary[0..-2].each do |name|
key = to_key(name)
- hook += ref.local_hook(key)
+ hook += ref.local_hook(key, false)
ref = ref.deref_key(key)
end
last_key = to_key(ary.last)
ref.local_assign(last_key, value)
- hook + ref.local_hook(last_key)
+ hook + ref.local_hook(last_key, true)
end
- def assign_hook(ary, &hook)
+ def assign_hook(ary, cascade, &hook)
ary[0..-2].inject(self) { |ref, name|
ref.deref_key(to_key(name))
- }.local_assign_hook(to_key(ary.last), &hook)
+ }.local_assign_hook(to_key(ary.last), cascade, &hook)
end
- def assign_self_hook(&hook)
+ def assign_self_hook(cascade, &hook)
check_lock(nil)
- @self_hook << hook
+ @self_hook << [hook, cascade]
end
def each_key
@@ -267,10 +286,6 @@ private
end
end
- def normalize_name(name)
- name_to_a(name).collect { |key| to_key(key) }.join('.')
- end
-
def to_key(name)
name.to_s.downcase
end
@@ -286,6 +301,13 @@ private
def key_max
(@store.keys.max { |l, r| l.to_s.to_i <=> r.to_s.to_i }).to_s.to_i
end
+
+ def loadpropertyfile(file)
+ puts "find property at #{file}" if $DEBUG
+ File.open(file) do |f|
+ load(f)
+ end
+ end
end
diff --git a/lib/soap/rpc/cgistub.rb b/lib/soap/rpc/cgistub.rb
index 2377f343b5..55437bac59 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 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2001, 2003, 2004 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;
@@ -40,7 +40,6 @@ class CGIStub < Logger::Application
@method = ENV['REQUEST_METHOD']
@size = ENV['CONTENT_LENGTH'].to_i || 0
@contenttype = ENV['CONTENT_TYPE']
- @charset = nil
@soapaction = ENV['HTTP_SOAPAction']
@source = stream
@body = nil
@@ -48,7 +47,6 @@ class CGIStub < Logger::Application
def init
validate
- @charset = StreamHandler.parse_media_type(@contenttype)
@body = @source.read(@size)
self
end
@@ -61,8 +59,8 @@ class CGIStub < Logger::Application
@soapaction
end
- def charset
- @charset
+ def contenttype
+ @contenttype
end
def to_s
@@ -96,15 +94,21 @@ class CGIStub < Logger::Application
on_init
end
- def add_servant(obj, namespace = @default_namespace, soapaction = nil)
+ 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 }" }
+ 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.
@@ -142,8 +146,8 @@ class CGIStub < Logger::Application
@router.add_method(receiver, qname, nil, name, param_def)
end
- def route(request_string, charset)
- @router.route(request_string, charset)
+ def route(conn_data)
+ @router.route(conn_data)
end
def create_fault_response(e)
@@ -157,32 +161,30 @@ private
httpversion = WEBrick::HTTPVersion.new('1.0')
@response = WEBrick::HTTPResponse.new({:HTTPVersion => httpversion})
+ conn_data = nil
begin
log(INFO) { "Received a request from '#{ @remote_user }@#{ @remote_host }'." }
# SOAP request parsing.
@request = SOAPRequest.new.init
@response['Status'] = 200
- req_charset = @request.charset
- req_string = @request.dump
- log(DEBUG) { "XML Request: #{req_string}" }
- res_string, is_fault = route(req_string, req_charset)
- log(DEBUG) { "XML Response: #{res_string}" }
-
- @response['Cache-Control'] = 'private'
- if req_charset
- @response['content-type'] = "#{@mediatype}; charset=\"#{req_charset}\""
- else
- @response['content-type'] = @mediatype
- end
- if is_fault
+ 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
end
- @response.body = res_string
+ @response['Cache-Control'] = 'private'
+ @response.body = conn_data.send_string
+ @response['content-type'] = conn_data.send_contenttype
rescue Exception
- res_string = create_fault_response($!)
+ conn_data = create_fault_response($!)
@response['Cache-Control'] = 'private'
- @response['content-type'] = @mediatype
@response['Status'] = 500
+ @response.body = conn_data.send_string
+ @response['content-type'] = conn_data.send_contenttype || @mediatype
ensure
buf = ''
@response.send_response(buf)
diff --git a/lib/soap/rpc/driver.rb b/lib/soap/rpc/driver.rb
index dd433ca33f..0e59dde9be 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 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2000, 2001, 2003, 2004 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;
@@ -13,6 +13,7 @@ require 'soap/rpc/proxy'
require 'soap/rpc/element'
require 'soap/streamHandler'
require 'soap/property'
+require 'soap/header/handlerset'
module SOAP
@@ -41,6 +42,8 @@ class Driver
end
__attr_proxy :options
+ __attr_proxy :headerhandler
+ __attr_proxy :test_loopback_response
__attr_proxy :endpoint_url, true
__attr_proxy :mapping_registry, true
__attr_proxy :soapaction, true
@@ -84,6 +87,12 @@ class Driver
@proxy = @servant.proxy
end
+ def loadproperty(propertyname)
+ unless options.loadproperty(propertyname)
+ raise LoadError.new("No such property to load -- #{propertyname}")
+ end
+ end
+
def inspect
"#<#{self.class}:#{@servant.streamhandler.inspect}>"
end
@@ -130,6 +139,7 @@ private
class Servant__
attr_reader :options
attr_reader :streamhandler
+ attr_reader :headerhandler
attr_reader :proxy
def initialize(host, endpoint_url, namespace)
@@ -141,6 +151,7 @@ private
@options = setup_options
@streamhandler = HTTPPostStreamHandler.new(endpoint_url,
@options["protocol.http"] ||= ::SOAP::Property.new)
+ @headerhandler = Header::HandlerSet.new
@proxy = Proxy.new(@streamhandler, @soapaction)
@proxy.allow_unqualified_element = true
end
@@ -178,27 +189,37 @@ private
@proxy.default_encodingstyle = encodingstyle
end
+ def test_loopback_response
+ @streamhandler.test_loopback_response
+ end
+
def invoke(headers, body)
set_wiredump_file_base(body.elename.name)
- @proxy.invoke(headers, body)
+ env = @proxy.invoke(headers, body)
+ if env.nil?
+ return nil, nil
+ else
+ return env.header, env.body
+ end
end
def call(name, *params)
set_wiredump_file_base(name)
# Convert parameters: params array => SOAPArray => members array
params = Mapping.obj2soap(params, @mapping_registry).to_a
- header, body = @proxy.call(nil, name, *params)
- raise EmptyResponseError.new("Empty response.") unless body
+ env = @proxy.call(call_headers, name, *params)
+ raise EmptyResponseError.new("Empty response.") unless env
+ receive_headers(env.header)
begin
- @proxy.check_fault(body)
+ @proxy.check_fault(env.body)
rescue SOAP::FaultError => e
Mapping.fault2exception(e)
end
- ret = body.response ?
- Mapping.soap2obj(body.response, @mapping_registry) : nil
- if body.outparams
- outparams = body.outparams.collect { |outparam|
+ 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)
}
return [ret].concat(outparams)
@@ -227,10 +248,28 @@ private
@servant.call(#{ name.dump }#{ callparam })
end
EOS
+ @host.method(name)
end
private
+ def call_headers
+ headers = @headerhandler.on_outbound
+ if headers.empty?
+ nil
+ else
+ h = ::SOAP::SOAPHeader.new
+ headers.each do |header|
+ h.add(header.elename.name, header)
+ end
+ h
+ end
+ end
+
+ def receive_headers(headers)
+ @headerhandler.on_inbound(headers) if headers
+ end
+
def set_wiredump_file_base(name)
if @wiredump_file_base
@streamhandler.wiredump_file_base = @wiredump_file_base + "_#{ name }"
diff --git a/lib/soap/rpc/element.rb b/lib/soap/rpc/element.rb
index 395823ab00..8a2f319293 100644
--- a/lib/soap/rpc/element.rb
+++ b/lib/soap/rpc/element.rb
@@ -43,7 +43,7 @@ class SOAPBody < SOAPStruct
end
def void?
- root_node.nil? # || root_node.is_a?(SOAPNil)
+ root_node.nil?
end
def fault
@@ -113,6 +113,7 @@ class SOAPMethod < SOAPStruct
params.each do |param, data|
@inparam[param] = data
data.elename.name = param
+ data.parent = self
end
end
@@ -226,6 +227,8 @@ class SOAPMethodResponse < SOAPMethod
def retval=(retval)
@retval = retval
@retval.elename = @retval.elename.dup_name(@retval_name || 'return')
+ retval.parent = self
+ retval
end
def each
diff --git a/lib/soap/rpc/proxy.rb b/lib/soap/rpc/proxy.rb
index 5825a27138..355bf2e81a 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 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2000, 2003, 2004 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;
@@ -12,6 +12,7 @@ require 'soap/mapping'
require 'soap/rpc/rpc'
require 'soap/rpc/element'
require 'soap/streamHandler'
+require 'soap/mimemessage'
module SOAP
@@ -79,7 +80,6 @@ public
raise SOAP::RPC::MethodDefinitionError.new(
"Method: #{ name } not defined.")
end
-
Request.new(method, values)
end
@@ -91,21 +91,30 @@ public
req_body = SOAPBody.new(req_body)
end
opt = create_options
- send_string = Processor.marshal(req_header, req_body, opt)
- data = @streamhandler.send(send_string, soapaction)
- if data.receive_string.empty?
+ opt[:external_content] = nil
+ req_env = SOAPEnvelope.new(req_header, req_body)
+ send_string = Processor.marshal(req_env, opt)
+ conn_data = StreamHandler::ConnectionData.new(send_string)
+ 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(conn_data, soapaction)
+ if conn_data.receive_string.empty?
return nil, nil
end
- opt = create_options
- opt[:charset] = @mandatorycharset ||
- StreamHandler.parse_media_type(data.receive_contenttype)
- res_header, res_body = Processor.unmarshal(data.receive_string, opt)
- return res_header, res_body
+ unmarshal(conn_data, opt)
end
- def call(headers, name, *values)
+ def call(req_header, name, *values)
req = create_request(name, *values)
- return invoke(headers, req.method, req.method.soapaction || @soapaction)
+ invoke(req_header, req.method, req.method.soapaction || @soapaction)
end
def check_fault(body)
@@ -116,6 +125,29 @@ public
private
+ def unmarshal(conn_data, opt)
+ contenttype = conn_data.receive_contenttype
+ if /#{MIMEMessage::MultipartContentType}/i =~ contenttype
+ opt[:external_content] = {}
+ mime = MIMEMessage.parse("Content-Type: " + contenttype,
+ conn_data.receive_string)
+ mime.parts.each do |part|
+ value = Attachment.new(part.content)
+ value.contentid = part.contentid
+ obj = SOAPAttachment.new(value)
+ opt[:external_content][value.contentid] = obj if value.contentid
+ end
+ opt[:charset] = @mandatorycharset ||
+ StreamHandler.parse_media_type(mime.root.headers['content-type'].str)
+ env = Processor.unmarshal(mime.root.content, opt)
+ else
+ opt[:charset] = @mandatorycharset ||
+ ::SOAP::StreamHandler.parse_media_type(contenttype)
+ env = Processor.unmarshal(conn_data.receive_string, opt)
+ end
+ env
+ end
+
def create_header(headers)
header = SOAPHeader.new()
headers.each do |content, mustunderstand, encodingstyle|
diff --git a/lib/soap/rpc/router.rb b/lib/soap/rpc/router.rb
index 527ec05768..9d8d1c8da6 100644
--- a/lib/soap/rpc/router.rb
+++ b/lib/soap/rpc/router.rb
@@ -11,6 +11,9 @@ require 'soap/processor'
require 'soap/mapping'
require 'soap/rpc/rpc'
require 'soap/rpc/element'
+require 'soap/streamHandler'
+require 'soap/mimemessage'
+require 'soap/header/handlerset'
module SOAP
@@ -24,6 +27,7 @@ class Router
attr_accessor :allow_unqualified_element
attr_accessor :default_encodingstyle
attr_accessor :mapping_registry
+ attr_reader :headerhandler
def initialize(actor)
@actor = actor
@@ -33,6 +37,7 @@ class Router
@allow_unqualified_element = false
@default_encodingstyle = nil
@mapping_registry = nil
+ @headerhandler = Header::HandlerSet.new
end
def add_method(receiver, qname, soapaction, name, param_def)
@@ -42,47 +47,112 @@ class Router
@method[fqname] = RPC::SOAPMethodRequest.new(qname, param_def, soapaction)
end
- def add_header_handler
- raise NotImplementedError.new
- end
-
- # Routing...
- def route(soap_string, charset = nil)
- opt = options
- opt[:charset] = charset
- is_fault = false
+ def route(conn_data)
+ soap_response = nil
begin
- header, body = Processor.unmarshal(soap_string, opt)
- # So far, header is omitted...
- soap_request = body.request
+ env = unmarshal(conn_data)
+ if env.nil?
+ raise ArgumentError.new("Illegal SOAP marshal format.")
+ end
+ receive_headers(env.header)
+ soap_request = env.body.request
unless soap_request.is_a?(SOAPStruct)
raise RPCRoutingError.new("Not an RPC style.")
end
soap_response = dispatch(soap_request)
rescue Exception
soap_response = fault($!)
- is_fault = true
+ conn_data.is_fault = true
end
- header = SOAPHeader.new
+ opt = options
+ opt[:external_content] = nil
+ header = call_headers
body = SOAPBody.new(soap_response)
- response_string = Processor.marshal(header, body, opt)
-
- return response_string, is_fault
+ env = SOAPEnvelope.new(header, body)
+ response_string = Processor.marshal(env, opt)
+ conn_data.send_string = response_string
+ 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
end
# Create fault response string.
def create_fault_response(e, charset = nil)
header = SOAPHeader.new
- soap_response = fault(e)
- body = SOAPBody.new(soap_response)
+ body = SOAPBody.new(fault(e))
+ env = SOAPEnvelope.new(header, body)
opt = options
+ opt[:external_content] = nil
opt[:charset] = charset
- Processor.marshal(header, body, opt)
+ 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
+ end
+ conn_data
end
private
+ def call_headers
+ headers = @headerhandler.on_outbound
+ if headers.empty?
+ nil
+ else
+ h = ::SOAP::SOAPHeader.new
+ headers.each do |header|
+ h.add(header.elename.name, header)
+ end
+ h
+ end
+ end
+
+ def receive_headers(headers)
+ @headerhandler.on_inbound(headers) if headers
+ end
+
+ def unmarshal(conn_data)
+ opt = options
+ contenttype = conn_data.receive_contenttype
+ if /#{MIMEMessage::MultipartContentType}/i =~ contenttype
+ opt[:external_content] = {}
+ mime = MIMEMessage.parse("Content-Type: " + contenttype,
+ conn_data.receive_string)
+ mime.parts.each do |part|
+ value = Attachment.new(part.content)
+ value.contentid = part.contentid
+ obj = SOAPAttachment.new(value)
+ opt[:external_content][value.contentid] = obj if value.contentid
+ end
+ opt[:charset] =
+ StreamHandler.parse_media_type(mime.root.headers['content-type'].str)
+ env = Processor.unmarshal(mime.root.content, opt)
+ else
+ opt[:charset] = ::SOAP::StreamHandler.parse_media_type(contenttype)
+ env = Processor.unmarshal(conn_data.receive_string, opt)
+ end
+ charset = opt[:charset]
+ conn_data.send_contenttype = "text/xml; charset=\"#{charset}\""
+ env
+ end
+
# Create new response.
def create_response(qname, result)
name = fqname(qname)
diff --git a/lib/soap/rpc/soaplet.rb b/lib/soap/rpc/soaplet.rb
index ec172fd85e..0c1427acf5 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 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2001, 2002, 2003, 2004 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,20 +22,28 @@ public
def initialize
@router_map = {}
@app_scope_router = ::SOAP::RPC::Router.new(self.class.name)
+ @headerhandlerfactory = []
+ @app_scope_headerhandler = nil
end
- # Add servant klass whose object has request scope. A servant object is
- # instantiated for each request.
+ # Add servant factory whose object has request scope. A servant object is
+ # instanciated for each request.
#
- # Bare in mind that servant klasses are distinguished by HTTP SOAPAction
+ # 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 klass.
+ # 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.
#
- def add_rpc_request_servant(klass, namespace, mapping_registry = nil)
- router = RequestRouter.new(klass, namespace, mapping_registry)
- add_router(namespace, router)
+ # 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_request_router(namespace)
+ router.factory = factory
+ router.mapping_registry = mapping_registry
end
# Add servant object which has application scope.
@@ -46,6 +54,17 @@ public
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
+ end
+
+ def add_rpc_headerhandler(obj)
+ @app_scope_headerhandler = obj
+ end
+ alias add_headerhandler add_rpc_headerhandler
###
## Servlet interfaces for WEBrick.
@@ -67,42 +86,43 @@ public
def do_POST(req, res)
namespace = parse_soapaction(req.meta_vars['HTTP_SOAPACTION'])
router = lookup_router(namespace)
-
- is_fault = false
-
- charset = ::SOAP::StreamHandler.parse_media_type(req['content-type'])
- begin
- response_stream, is_fault = router.route(req.body, charset)
- rescue Exception => e
- response_stream = router.create_fault_response(e)
- is_fault = true
+ 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)
+ if conn_data.is_fault
+ res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
+ end
+ res.body = conn_data.send_string
+ res['content-type'] = conn_data.send_contenttype
+ 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
end
- res.body = response_stream
- res['content-type'] = "text/xml; charset=\"#{charset}\""
- if response_stream.is_a?(IO)
+ if res.body.is_a?(IO)
res.chunked = true
end
-
- if is_fault
- res.status = WEBrick::HTTPStatus::RC_INTERNAL_SERVER_ERROR
- end
end
private
class RequestRouter < ::SOAP::RPC::Router
- def initialize(klass, namespace, mapping_registry = nil)
+ attr_accessor :factory
+
+ def initialize(namespace = nil)
super(namespace)
- if mapping_registry
- self.mapping_registry = mapping_registry
- end
- @klass = klass
@namespace = namespace
+ @factory = nil
end
def route(soap_string)
- obj = @klass.new
+ obj = @factory.create
namespace = self.actor
router = ::SOAP::RPC::Router.new(@namespace)
SOAPlet.add_servant_to_router(router, obj, namespace)
@@ -110,6 +130,12 @@ private
end
end
+ def setup_request_router(namespace)
+ router = @router_map[namespace] || RequestRouter.new(namespace)
+ add_router(namespace, router)
+ router
+ end
+
def add_router(namespace, router)
@router_map[namespace] = router
end
@@ -132,11 +158,29 @@ private
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) }
+ end
+ end
+
class << self
public
def add_servant_to_router(router, obj, namespace)
::SOAP::RPC.defined_methods(obj).each do |name|
- add_servant_method_to_router(router, obj, namespace, name)
+ begin
+ add_servant_method_to_router(router, obj, namespace, name)
+ rescue SOAP::RPC::MethodDefinitionError => e
+ p e if $DEBUG
+ end
end
end
@@ -145,7 +189,7 @@ private
soapaction = nil
method = obj.method(name)
param_def = ::SOAP::RPC::SOAPMethod.create_param_def(
- (1..method.arity.abs).collect { |i| "p#{ i }" })
+ (1..method.arity.abs).collect { |i| "p#{ i }" })
router.add_method(obj, qname, soapaction, name, param_def)
end
end
diff --git a/lib/soap/rpc/standaloneServer.rb b/lib/soap/rpc/standaloneServer.rb
index 42a566e088..080343ba33 100644
--- a/lib/soap/rpc/standaloneServer.rb
+++ b/lib/soap/rpc/standaloneServer.rb
@@ -6,111 +6,35 @@
# either the dual license version in 2003, or any later version.
-require 'logger'
-require 'soap/rpc/soaplet'
-require 'soap/streamHandler'
-
-# require 'webrick'
-require 'webrick/compat.rb'
-require 'webrick/version.rb'
-require 'webrick/config.rb'
-require 'webrick/log.rb'
-require 'webrick/server.rb'
-require 'webrick/utils.rb'
-require 'webrick/accesslog'
-# require 'webrick/htmlutils.rb'
-require 'webrick/httputils.rb'
-# require 'webrick/cookie.rb'
-require 'webrick/httpversion.rb'
-require 'webrick/httpstatus.rb'
-require 'webrick/httprequest.rb'
-require 'webrick/httpresponse.rb'
-require 'webrick/httpserver.rb'
-# require 'webrick/httpservlet.rb'
-# require 'webrick/httpauth.rb'
+require 'soap/rpc/httpserver'
module SOAP
module RPC
-class StandaloneServer < Logger::Application
- attr_reader :server
-
- def initialize(app_name, namespace, host = "0.0.0.0", port = 8080)
- super(app_name)
- self.level = Logger::Severity::INFO
- @namespace = namespace
+class StandaloneServer < HTTPServer
+ def initialize(appname, default_namespace, host = "0.0.0.0", port = 8080)
+ @appname = appname
+ @default_namespace = default_namespace
@host = host
@port = port
- @server = nil
- @soaplet = ::SOAP::RPC::SOAPlet.new
- on_init
- end
-
- def on_init
- # define extra methods in derived class.
- end
-
- def status
- if @server
- @server.status
- else
- nil
- end
- end
-
- def shutdown
- @server.shutdown
- end
-
- def add_rpc_request_servant(klass, namespace = @namespace, mapping_registry = nil)
- @soaplet.add_rpc_request_servant(klass, namespace, mapping_registry)
+ super(create_config)
end
- def add_rpc_servant(obj, namespace = @namespace)
- @soaplet.add_rpc_servant(obj, namespace)
- end
alias add_servant add_rpc_servant
-
- def mapping_registry
- @soaplet.app_scope_router.mapping_registry
- end
-
- def mapping_registry=(mapping_registry)
- @soaplet.app_scope_router.mapping_registry = mapping_registry
- end
-
- def add_method(obj, name, *param)
- add_method_as(obj, name, name, *param)
- end
-
- def add_method_as(obj, name, name_as, *param)
- qname = XSD::QName.new(@namespace, name_as)
- soapaction = nil
- method = obj.method(name)
- param_def = if param.size == 1 and param[0].is_a?(Array)
- param[0]
- elsif param.empty?
- ::SOAP::RPC::SOAPMethod.create_param_def(
- (1..method.arity.abs).collect { |i| "p#{ i }" })
- else
- SOAP::RPC::SOAPMethod.create_param_def(param)
- end
- @soaplet.app_scope_router.add_method(obj, qname, soapaction, name, param_def)
- end
+ alias add_headerhandler add_rpc_headerhandler
private
- def run
- @server = WEBrick::HTTPServer.new(
+ def create_config
+ {
:BindAddress => @host,
- :Logger => @log,
+ :Port => @port,
:AccessLog => [],
- :Port => @port
- )
- @server.mount('/', @soaplet)
- @server.start
+ :SOAPDefaultNamespace => @default_namespace,
+ :SOAPHTTPServerApplicationName => @appname,
+ }
end
end
diff --git a/lib/soap/soap.rb b/lib/soap/soap.rb
index d00d89b05b..02b26e4246 100644
--- a/lib/soap/soap.rb
+++ b/lib/soap/soap.rb
@@ -1,5 +1,5 @@
# soap/soap.rb: SOAP4R - Base definitions.
-# Copyright (C) 2000, 2001, 2002, 2003 NAKAMURA, Hiroshi <nahi@ruby-lang.org>.
+# Copyright (C) 2000-2004 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;
@@ -48,6 +48,7 @@ EleFaultStringName = XSD::QName.new(nil, EleFaultString)
EleFaultActorName = XSD::QName.new(nil, EleFaultActor)
EleFaultCodeName = XSD::QName.new(nil, EleFaultCode)
EleFaultDetailName = XSD::QName.new(nil, EleFaultDetail)
+AttrMustUnderstandName = XSD::QName.new(EnvelopeNamespace, AttrMustUnderstand)
AttrEncodingStyleName = XSD::QName.new(EnvelopeNamespace, AttrEncodingStyle)
AttrRootName = XSD::QName.new(EncodingNamespace, AttrRoot)
AttrArrayTypeName = XSD::QName.new(EncodingNamespace, AttrArrayType)
@@ -75,6 +76,8 @@ class ArrayStoreError < Error; end
class RPCRoutingError < Error; end
+class UnhandledMustUnderstandHeaderError < Error; end
+
class FaultError < Error
attr_reader :faultcode
attr_reader :faultstring
diff --git a/lib/soap/streamHandler.rb b/lib/soap/streamHandler.rb
index d6b9c3bdca..efadf21e07 100644
--- a/lib/soap/streamHandler.rb
+++ b/lib/soap/streamHandler.rb
@@ -33,21 +33,14 @@ class StreamHandler
attr_accessor :send_contenttype
attr_accessor :receive_string
attr_accessor :receive_contenttype
+ attr_accessor :is_fault
- def initialize
- @send_string = nil
+ def initialize(send_string = nil)
+ @send_string = send_string
@send_contenttype = nil
@receive_string = nil
@receive_contenttype = nil
- @bag = {}
- end
-
- def [](idx)
- @bag[idx]
- end
-
- def []=(idx, value)
- @bag[idx] = value
+ @is_fault = false
end
end
@@ -59,7 +52,7 @@ class StreamHandler
def self.parse_media_type(str)
if /^#{ MediaType }(?:\s*;\s*charset=([^"]+|"[^"]+"))?$/i !~ str
- raise StreamError.new("Illegal media type.");
+ return nil
end
charset = $1
charset.gsub!(/"/, '') if charset
@@ -90,18 +83,24 @@ public
@options = options
set_options
@client.debug_dev = @wiredump_dev
+ @cookie_store = nil
+ end
+
+ def test_loopback_response
+ @client.test_loopback_response
end
def inspect
"#<#{self.class}:#{endpoint_url}>"
end
- def send(soap_string, soapaction = nil, charset = @charset)
- send_post(soap_string, soapaction, charset)
+ def send(conn_data, soapaction = nil, charset = @charset)
+ send_post(conn_data, soapaction, charset)
end
def reset
@client.reset(@endpoint_url)
+ @client.save_cookie_store if @cookie_store
end
private
@@ -125,10 +124,6 @@ private
@options.add_hook("cookie_store_file") do |key, value|
set_cookie_store_file(value)
end
- set_ssl_config(@options["ssl_config"])
- @options.add_hook("ssl_config") do |key, value|
- set_ssl_config(@options["ssl_config"])
- end
@charset = @options["charset"] || XSD::Charset.charset_label($KCODE)
@options.add_hook("charset") do |key, value|
@charset = value
@@ -138,12 +133,18 @@ private
@wiredump_dev = value
@client.debug_dev = @wiredump_dev
end
+ ssl_config = @options["ssl_config"] ||= ::SOAP::Property.new
+ set_ssl_config(ssl_config)
+ ssl_config.add_hook(true) do |key, value|
+ set_ssl_config(ssl_config)
+ end
basic_auth = @options["basic_auth"] ||= ::SOAP::Property.new
set_basic_auth(basic_auth)
basic_auth.add_hook do |key, value|
set_basic_auth(basic_auth)
end
@options.lock(true)
+ ssl_config.unlock
basic_auth.unlock
end
@@ -154,34 +155,82 @@ private
end
def set_cookie_store_file(value)
- return unless value
- raise NotImplementedError.new
+ @cookie_store = value
+ @client.set_cookie_store(@cookie_store) if @cookie_store
+ end
+
+ def set_ssl_config(ssl_config)
+ ssl_config.each do |key, value|
+ cfg = @client.ssl_config
+ case key
+ when 'client_cert'
+ cfg.client_cert = cert_from_file(value)
+ when 'client_key'
+ cfg.client_key = key_from_file(value)
+ when 'client_ca'
+ cfg.client_ca = value
+ when 'ca_path'
+ cfg.set_trust_ca(value)
+ when 'ca_file'
+ cfg.set_trust_ca(value)
+ when 'crl'
+ cfg.set_crl(value)
+ when 'verify_mode'
+ cfg.verify_mode = ssl_config_int(value)
+ when 'verify_depth'
+ cfg.verify_depth = ssl_config_int(value)
+ when 'options'
+ cfg.options = value
+ when 'ciphers'
+ cfg.ciphers = value
+ when 'verify_callback'
+ cfg.verify_callback = value
+ when 'cert_store'
+ cfg.cert_store = value
+ else
+ raise ArgumentError.new("unknown ssl_config property #{key}")
+ end
+ end
+ end
+
+ def ssl_config_int(value)
+ if value.nil? or value.empty?
+ nil
+ else
+ begin
+ Integer(value)
+ rescue ArgumentError
+ ::SOAP::Property::Util.const_from_name(value)
+ end
+ end
end
- def set_ssl_config(value)
- return unless value
- raise NotImplementedError.new
+ def cert_from_file(filename)
+ OpenSSL::X509::Certificate.new(File.open(filename) { |f| f.read })
end
- def send_post(soap_string, soapaction, charset)
- data = ConnectionData.new
- data.send_string = soap_string
- data.send_contenttype = StreamHandler.create_media_type(charset)
+ def key_from_file(filename)
+ OpenSSL::PKey::RSA.new(File.open(filename) { |f| f.read })
+ end
+
+ def send_post(conn_data, soapaction, charset)
+ conn_data.send_contenttype ||= StreamHandler.create_media_type(charset)
if @wiredump_file_base
filename = @wiredump_file_base + '_request.xml'
f = File.open(filename, "w")
- f << soap_string
+ f << conn_data.send_string
f.close
end
extra = {}
- extra['Content-Type'] = data.send_contenttype
+ extra['Content-Type'] = conn_data.send_contenttype
extra['SOAPAction'] = "\"#{ soapaction }\""
+ send_string = conn_data.send_string
@wiredump_dev << "Wire dump:\n\n" if @wiredump_dev
begin
- res = @client.post(@endpoint_url, soap_string, extra)
+ res = @client.post(@endpoint_url, send_string, extra)
rescue
@client.reset(@endpoint_url)
raise
@@ -206,10 +255,9 @@ private
raise HTTPStreamError.new("#{ res.status }: #{ res.reason }")
end
- data.receive_string = receive_string
- data.receive_contenttype = res.contenttype
-
- return data
+ conn_data.receive_string = receive_string
+ conn_data.receive_contenttype = res.contenttype
+ conn_data
end
CRLF = "\r\n"
diff --git a/lib/soap/wsdlDriver.rb b/lib/soap/wsdlDriver.rb
index d1a1a6cf29..af868ea886 100644
--- a/lib/soap/wsdlDriver.rb
+++ b/lib/soap/wsdlDriver.rb
@@ -12,11 +12,13 @@ require 'xsd/qname'
require 'soap/element'
require 'soap/baseData'
require 'soap/streamHandler'
+require 'soap/mimemessage'
require 'soap/mapping'
require 'soap/mapping/wsdlRegistry'
require 'soap/rpc/rpc'
require 'soap/rpc/element'
require 'soap/processor'
+require 'soap/header/handlerset'
require 'logger'
@@ -91,6 +93,8 @@ class WSDLDriver
end
__attr_proxy :options
+ __attr_proxy :headerhandler
+ __attr_proxy :test_loopback_response
__attr_proxy :endpoint_url, true
__attr_proxy :mapping_registry, true # for RPC unmarshal
__attr_proxy :wsdl_mapping_registry, true # for RPC marshal
@@ -151,6 +155,7 @@ class WSDLDriver
attr_reader :options
attr_reader :streamhandler
+ attr_reader :headerhandler
attr_reader :port
attr_accessor :mapping_registry
@@ -175,7 +180,7 @@ class WSDLDriver
@mandatorycharset = nil
@wsdl_elements = @wsdl.collect_elements
- @wsdl_types = @wsdl.collect_complextypes
+ @wsdl_types = @wsdl.collect_complextypes + @wsdl.collect_simpletypes
@rpc_decode_typemap = @wsdl_types +
@wsdl.soap_rpc_complextypes(port.find_binding)
@wsdl_mapping_registry = Mapping::WSDLRegistry.new(@rpc_decode_typemap)
@@ -183,6 +188,7 @@ class WSDLDriver
endpoint_url = @port.soap_address.location
@streamhandler = HTTPPostStreamHandler.new(endpoint_url,
@options["protocol.http"] ||= Property.new)
+ @headerhandler = Header::HandlerSet.new
# Convert a map which key is QName, to a Hash which key is String.
@operations = {}
@port.inputoperation_map.each do |op_name, op_info|
@@ -200,14 +206,19 @@ class WSDLDriver
@streamhandler.reset
end
+ def test_loopback_response
+ @streamhandler.test_loopback_response
+ end
+
def rpc_send(method_name, *params)
log(INFO) { "call: calling method '#{ method_name }'." }
log(DEBUG) { "call: parameters '#{ params.inspect }'." }
op_info = @operations[method_name]
method = create_method_struct(op_info, params)
- req_header = nil
+ req_header = call_headers
req_body = SOAPBody.new(method)
+ req_env = SOAPEnvelope.new(req_header, req_body)
if @wiredump_file_base
@streamhandler.wiredump_file_base =
@@ -217,19 +228,20 @@ class WSDLDriver
begin
opt = create_options
opt[:decode_typemap] = @rpc_decode_typemap
- res_header, res_body = invoke(req_header, req_body, op_info, opt)
- if res_body.fault
- raise SOAP::FaultError.new(res_body.fault)
+ res_env = invoke(req_env, op_info, opt)
+ receive_headers(res_env.header)
+ if res_env.body.fault
+ raise ::SOAP::FaultError.new(res_env.body.fault)
end
- rescue SOAP::FaultError => e
+ rescue ::SOAP::FaultError => e
Mapping.fault2exception(e)
end
- ret = res_body.response ?
- Mapping.soap2obj(res_body.response, @mapping_registry) : nil
+ ret = res_env.body.response ?
+ Mapping.soap2obj(res_env.body.response, @mapping_registry) : nil
- if res_body.outparams
- outparams = res_body.outparams.collect { |outparam|
+ if res_env.body.outparams
+ outparams = res_env.body.outparams.collect { |outparam|
Mapping.soap2obj(outparam)
}
return [ret].concat(outparams)
@@ -245,18 +257,36 @@ class WSDLDriver
op_info = @operations[name]
req_header = header_from_obj(header_obj, op_info)
req_body = body_from_obj(body_obj, op_info)
+ req_env = SOAPEnvelope.new(req_header, req_body)
opt = create_options
- res_header, res_body = invoke(req_header, req_body, op_info, opt)
- if res_body.fault
- raise SOAP::FaultError.new(res_body.fault)
+ res_env = invoke(req_env, op_info, opt)
+ if res_env.body.fault
+ raise ::SOAP::FaultError.new(res_env.body.fault)
end
- res_body_obj = res_body.response ?
- Mapping.soap2obj(res_body.response, @mapping_registry) : nil
- return res_header, res_body_obj
+ res_body_obj = res_env.body.response ?
+ Mapping.soap2obj(res_env.body.response, @mapping_registry) : nil
+ return res_env.header, res_body_obj
end
private
+ def call_headers
+ headers = @headerhandler.on_outbound
+ if headers.empty?
+ nil
+ else
+ h = ::SOAP::SOAPHeader.new
+ headers.each do |header|
+ h.add(header.elename.name, header)
+ end
+ h
+ end
+ end
+
+ def receive_headers(headers)
+ @headerhandler.on_inbound(headers) if headers
+ end
+
def create_method_struct(op_info, params)
parts_names = op_info.bodyparts.collect { |part| part.name }
obj = create_method_obj(parts_names, params)
@@ -283,18 +313,50 @@ class WSDLDriver
o
end
- def invoke(req_header, req_body, op_info, opt)
- send_string = Processor.marshal(req_header, req_body, opt)
+ def invoke(req_env, op_info, opt)
+ opt[:external_content] = nil
+ send_string = Processor.marshal(req_env, opt)
log(DEBUG) { "invoke: sending string #{ send_string }" }
- data = @streamhandler.send(send_string, op_info.soapaction)
- log(DEBUG) { "invoke: received string #{ data.receive_string }" }
- if data.receive_string.empty?
+ conn_data = StreamHandler::ConnectionData.new(send_string)
+ 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(conn_data, op_info.soapaction)
+ log(DEBUG) { "invoke: received string #{ conn_data.receive_string }" }
+ if conn_data.receive_string.empty?
return nil, nil
end
- opt[:charset] = @mandatorycharset ||
- StreamHandler.parse_media_type(data.receive_contenttype)
- res_header, res_body = Processor.unmarshal(data.receive_string, opt)
- return res_header, res_body
+ unmarshal(conn_data, opt)
+ end
+
+ def unmarshal(conn_data, opt)
+ contenttype = conn_data.receive_contenttype
+ if /#{MIMEMessage::MultipartContentType}/i =~ contenttype
+ opt[:external_content] = {}
+ mime = MIMEMessage.parse("Content-Type: " + contenttype,
+ conn_data.receive_string)
+ mime.parts.each do |part|
+ value = Attachment.new(part.content)
+ value.contentid = part.contentid
+ obj = SOAPAttachment.new(value)
+ opt[:external_content][value.contentid] = obj if value.contentid
+ end
+ opt[:charset] = @mandatorycharset ||
+ StreamHandler.parse_media_type(mime.root.headers['content-type'].str)
+ env = Processor.unmarshal(mime.root.content, opt)
+ else
+ opt[:charset] = @mandatorycharset ||
+ ::SOAP::StreamHandler.parse_media_type(contenttype)
+ env = Processor.unmarshal(conn_data.receive_string, opt)
+ end
+ env
end
def header_from_obj(obj, op_info)
@@ -314,9 +376,9 @@ class WSDLDriver
else
header = SOAPHeader.new()
op_info.headerparts.each do |part|
- child = obj[part.elename.name]
+ child = Mapper.find_attribute(obj, part.name)
ele = headeritem_from_obj(child, part.element || part.eletype)
- header.add(ele)
+ header.add(part.name, ele)
end
header
end
@@ -348,7 +410,7 @@ class WSDLDriver
else
body = SOAPBody.new
op_info.bodyparts.each do |part|
- child = obj[part.elename.name]
+ child = Mapper.find_attribute(obj, part.name)
ele = bodyitem_from_obj(child, part.element || part.type)
body.add(ele.elename.name, ele)
end
@@ -426,6 +488,7 @@ class WSDLDriver
opt
end
+ class MappingError < StandardError; end
class Mapper
def initialize(elements, types)
@elements = elements
@@ -438,7 +501,7 @@ class WSDLDriver
elsif type = @types[name]
obj2type(obj, type)
else
- raise RuntimeError.new("Cannot find name #{name} in schema.")
+ raise MappingError.new("Cannot find name #{name} in schema.")
end
end
@@ -446,6 +509,16 @@ class WSDLDriver
raise NotImplementedError.new
end
+ def Mapper.find_attribute(obj, attr_name)
+ if obj.respond_to?(attr_name)
+ obj.__send__(attr_name)
+ elsif obj.is_a?(Hash)
+ obj[attr_name] || obj[attr_name.intern]
+ else
+ obj.instance_eval("@#{ attr_name }")
+ end
+ end
+
private
def _obj2ele(obj, ele)
@@ -456,25 +529,47 @@ class WSDLDriver
elsif type = TypeMap[ele.type]
o = base2soap(obj, type)
else
- raise RuntimeError.new("Cannot find type #{ele.type}.")
+ raise MappingError.new("Cannot find type #{ele.type}.")
end
o.elename = ele.name
elsif ele.local_complextype
o = SOAPElement.new(ele.name)
- ele.local_complextype.each_element do |child_name, child_ele|
- o.add(_obj2ele(find_attribute(obj, child_name.name), child_ele))
+ ele.local_complextype.each_element do |child_ele|
+ o.add(_obj2ele(Mapper.find_attribute(obj, child_ele.name.name),
+ child_ele))
end
else
- raise RuntimeError.new("Illegal schema?")
+ raise MappingError.new("Illegal schema?")
end
o
end
def obj2type(obj, type)
- o = SOAPElement.new(type.name)
- type.each_element do |child_name, child_ele|
- o.add(_obj2ele(find_attribute(obj, child_name.name), child_ele))
- end
+ if type.is_a?(::WSDL::XMLSchema::SimpleType)
+ simple2soap(obj, type)
+ else
+ complex2soap(obj, type)
+ end
+ end
+
+ def simple2soap(obj, type)
+ o = base2soap(obj, TypeMap[type.base])
+ if type.restriction.enumeration.empty?
+ STDERR.puts("#{type.name}: simpleType which is not enum type not supported.")
+ return o
+ end
+ if type.restriction.enumeration.include?(o)
+ raise MappingError.new("#{o} is not allowed for #{type.name}")
+ end
+ o
+ end
+
+ def complex2soap(obj, type)
+ o = SOAPElement.new(type.name)
+ type.each_element do |child_ele|
+ o.add(_obj2ele(Mapper.find_attribute(obj, child_ele.name.name),
+ child_ele))
+ end
o
end
@@ -486,22 +581,13 @@ class WSDLDriver
soap_obj = nil
if type <= XSD::XSDString
soap_obj = type.new(XSD::Charset.is_ces(obj, $KCODE) ?
- XSD::Charset.encoding_conv(obj, $KCODE, XSD::Charset.encoding) : obj)
+ XSD::Charset.encoding_conv(obj, $KCODE, XSD::Charset.encoding) :
+ obj)
else
soap_obj = type.new(obj)
end
soap_obj
end
-
- def find_attribute(obj, attr_name)
- if obj.respond_to?(attr_name)
- obj.__send__(attr_name)
- elsif obj.is_a?(Hash)
- obj[attr_name] || obj[attr_name.intern]
- else
- obj.instance_eval("@#{ attr_name }")
- end
- end
end
end
end