From ff1b89a96d0f103ff0c917c458ddd3e36e9b3718 Mon Sep 17 00:00:00 2001 From: nahi Date: Tue, 6 Jan 2004 02:20:51 +0000 Subject: * import soap4r/1.5.2; * lib/soap/{attachment.rb,baseData.rb,encodingstyle/soapHandler.rb}: introduce SOAPExternalReference class as a referenct to SOAPEnvelope external content. * lib/soap/{attachment.rb,mimemessage.rb}: great SwA (SOAP messages with Attachments) support code by Jamie Herre. * lib/soap/{element.rb,marshal.rb,parser.rb,processor.rb, streamHandler.rb,wsdlDriver.rb}: SwA support. * lib/soap/rpc/{cgistub.rb,driver.rb,element.rb,proxy.rb,router.rb, soaplet.rb}: SwA support and refactoring. * lib/soap/generator.rb, lib/soap/mapping/mapping.rb: follow SOAPReference#initialize signature change. * lib/soap/mapping/factory.rb: deleted unused methods. * lib/soap/mapping/rubytypeFactory.rb: do no ignore case while xsi:type string <-> Ruby class name matching. * lib/xsd/datatypes.rb: check the smallest positive non-zero single-precision float exactly instead of packing with "f". [ruby-talk:88822] * test/soap/test_basetype.rb, test/xsd/test_xsd.rb: use 1.402e-45, not 1.4e-45. 1.4e-45 is smaller than 2 ** -149... * test/soap/test_basetype.rb, test/soap/marshal/test_marshal.rb, test/xsd/test_xsd.rb: use "(-1.0 / (1.0 / 0.0))" instead of "-0.0". * test/soap/test_streamhandler.rb: revert to the previous test that warns "basic_auth unsupported under net/http". git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@5384 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 38 +++++++++++++++ lib/soap/baseData.rb | 87 +++++++++++++++++++++++++++++++---- lib/soap/element.rb | 27 ++++++++++- lib/soap/encodingstyle/soapHandler.rb | 30 +++++++----- lib/soap/generator.rb | 3 +- lib/soap/mapping/factory.rb | 8 ---- lib/soap/mapping/mapping.rb | 4 +- lib/soap/mapping/rubytypeFactory.rb | 6 --- lib/soap/marshal.rb | 9 ++-- lib/soap/parser.rb | 7 ++- lib/soap/processor.rb | 16 +++---- lib/soap/rpc/cgistub.rb | 40 ++++++++-------- lib/soap/rpc/driver.rb | 12 +++-- lib/soap/rpc/element.rb | 5 +- lib/soap/rpc/proxy.rb | 52 +++++++++++++++++---- lib/soap/rpc/router.rb | 82 +++++++++++++++++++++++++++------ lib/soap/rpc/soaplet.rb | 28 +++++------ lib/soap/streamHandler.rb | 41 +++++++---------- lib/soap/wsdlDriver.rb | 79 ++++++++++++++++++++++--------- lib/xsd/datatypes.rb | 16 +++++-- test/soap/calc/test_calc2.rb | 1 + test/soap/calc/test_calc_cgi.rb | 1 + test/soap/marshal/test_marshal.rb | 3 +- test/soap/test_basetype.rb | 40 ++++++++-------- test/soap/test_streamhandler.rb | 13 ++++-- test/xsd/test_xsd.rb | 40 ++++++++-------- 26 files changed, 473 insertions(+), 215 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1b77e5e5fd..f9bb939d6e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,41 @@ +Tue Jan 6 09:38:27 2004 NAKAMURA, Hiroshi + + * import soap4r/1.5.2; + + * lib/soap/{attachment.rb,baseData.rb,encodingstyle/soapHandler.rb}: + introduce SOAPExternalReference class as a referenct to SOAPEnvelope + external content. + + * lib/soap/{attachment.rb,mimemessage.rb}: great SwA (SOAP messages + with Attachments) support code by Jamie Herre. + + * lib/soap/{element.rb,marshal.rb,parser.rb,processor.rb, + streamHandler.rb,wsdlDriver.rb}: SwA support. + + * lib/soap/rpc/{cgistub.rb,driver.rb,element.rb,proxy.rb,router.rb, + soaplet.rb}: SwA support and refactoring. + + * lib/soap/generator.rb, lib/soap/mapping/mapping.rb: follow + SOAPReference#initialize signature change. + + * lib/soap/mapping/factory.rb: deleted unused methods. + + * lib/soap/mapping/rubytypeFactory.rb: do no ignore case while xsi:type + string <-> Ruby class name matching. + + * lib/xsd/datatypes.rb: check the smallest positive non-zero + single-precision float exactly instead of packing with "f". + [ruby-talk:88822] + + * test/soap/test_basetype.rb, test/xsd/test_xsd.rb: use 1.402e-45, not + 1.4e-45. 1.4e-45 is smaller than 2 ** -149... + + * test/soap/test_basetype.rb, test/soap/marshal/test_marshal.rb, + test/xsd/test_xsd.rb: use "(-1.0 / (1.0 / 0.0))" instead of "-0.0". + + * test/soap/test_streamhandler.rb: revert to the previous test that + warns "basic_auth unsupported under net/http". + Tue Jan 6 06:37:53 2004 Dave Thomas * bin/rdoc: Add --ri-system switch diff --git a/lib/soap/baseData.rb b/lib/soap/baseData.rb index 91f5a0433f..30963f1d64 100644 --- a/lib/soap/baseData.rb +++ b/lib/soap/baseData.rb @@ -64,6 +64,14 @@ public @position = nil @extraattr = {} end + + def rootnode + node = self + while node = node.parent + break if SOAPEnvelope === node + end + node + end end @@ -117,7 +125,7 @@ public attr_accessor :elename # Override the definition in SOAPBasetype. - def initialize(refid = nil) + def initialize(obj = nil) super() @type = XSD::QName.new @encodingstyle = nil @@ -126,8 +134,9 @@ public @precedents = [] @root = false @parent = nil - @refid = refid + @refid = nil @obj = nil + __setobj__(obj) if obj end def __getobj__ @@ -136,7 +145,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 +168,59 @@ 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 + @encodingstyle = nil + @elename = XSD::QName.new + @precedents = [] + @root = false + @parent = nil + 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 @@ -362,6 +413,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,6 +453,8 @@ 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 @@ -410,22 +464,31 @@ 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 +513,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) @@ -508,6 +572,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) @@ -609,6 +675,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..29a075825a 100644 --- a/lib/soap/element.rb +++ b/lib/soap/element.rb @@ -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 = {}) @@ -147,6 +151,7 @@ public @content = content @mustunderstand = mustunderstand @encodingstyle = encodingstyle || LiteralNamespace + content.parent = self if content end def encode(generator, ns, attrs = {}) @@ -193,8 +198,9 @@ 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) @@ -202,6 +208,19 @@ class SOAPEnvelope < XSD::NSDBase @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 +234,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/soapHandler.rb b/lib/soap/encodingstyle/soapHandler.rb index 9aa732535e..d755d7b952 100644 --- a/lib/soap/encodingstyle/soapHandler.rb +++ b/lib/soap/encodingstyle/soapHandler.rb @@ -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) @@ -320,10 +325,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 @@ -526,7 +530,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 +548,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..5da6b8f26e 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. diff --git a/lib/soap/mapping/factory.rb b/lib/soap/mapping/factory.rb index f181773687..bf68701229 100644 --- a/lib/soap/mapping/factory.rb +++ b/lib/soap/mapping/factory.rb @@ -87,14 +87,6 @@ class Factory Thread.current[:SOAPMarshalDataKey][node.id] = obj end - def name2typename(name) - capitalize(name) - end - - def capitalize(target) - target.gsub(/^([a-z])/) { $1.tr!('[a-z]', '[A-Z]') } - end - private def setiv2ary(obj, node, map) diff --git a/lib/soap/mapping/mapping.rb b/lib/soap/mapping/mapping.rb index 4b68b811fc..38a01bac07 100644 --- a/lib/soap/mapping/mapping.rb +++ b/lib/soap/mapping/mapping.rb @@ -98,9 +98,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/rubytypeFactory.rb b/lib/soap/mapping/rubytypeFactory.rb index 0b4af8b8e4..a447715add 100644 --- a/lib/soap/mapping/rubytypeFactory.rb +++ b/lib/soap/mapping/rubytypeFactory.rb @@ -343,9 +343,6 @@ private when TYPE_STRUCT typestr = Mapping.elename2name(node['type'].data) klass = Mapping.class_from_name(typestr) - if klass.nil? - klass = Mapping.class_from_name(name2typename(typestr)) - end if klass.nil? return false end @@ -400,9 +397,6 @@ private end typestr = Mapping.elename2name(node.type.name) klass = Mapping.class_from_name(typestr) - if klass.nil? - klass = Mapping.class_from_name(name2typename(typestr)) - end if klass.nil? return nil end 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/parser.rb b/lib/soap/parser.rb index 1395be55ec..457d681d30 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 @@ -201,6 +202,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 +226,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/rpc/cgistub.rb b/lib/soap/rpc/cgistub.rb index 2377f343b5..4fc8b98cee 100644 --- a/lib/soap/rpc/cgistub.rb +++ b/lib/soap/rpc/cgistub.rb @@ -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 @@ -142,8 +140,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 +155,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..655174cf33 100644 --- a/lib/soap/rpc/driver.rb +++ b/lib/soap/rpc/driver.rb @@ -180,15 +180,21 @@ private 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(nil, name, *params) + raise EmptyResponseError.new("Empty response.") unless env + header, body = env.header, env.body begin @proxy.check_fault(body) rescue SOAP::FaultError => e 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..7d9dbe519e 100644 --- a/lib/soap/rpc/proxy.rb +++ b/lib/soap/rpc/proxy.rb @@ -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) req = create_request(name, *values) - return invoke(headers, req.method, req.method.soapaction || @soapaction) + invoke(headers, 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..12622c72b1 100644 --- a/lib/soap/rpc/router.rb +++ b/lib/soap/rpc/router.rb @@ -11,6 +11,8 @@ require 'soap/processor' require 'soap/mapping' require 'soap/rpc/rpc' require 'soap/rpc/element' +require 'soap/streamHandler' +require 'soap/mimemessage' module SOAP @@ -47,42 +49,96 @@ class Router end # Routing... - def route(soap_string, charset = nil) - opt = options - opt[:charset] = charset - is_fault = false + #def route(soap_string, charset = nil) + def route(conn_data) + soap_response = nil begin - header, body = Processor.unmarshal(soap_string, opt) + env = unmarshal(conn_data) + if env.nil? + raise ArgumentError.new("Illegal SOAP marshal format.") + end # So far, header is omitted... - soap_request = body.request + 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 + opt = options + opt[:external_content] = nil header = SOAPHeader.new 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 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 4b25c68161..e67593210d 100644 --- a/lib/soap/rpc/soaplet.rb +++ b/lib/soap/rpc/soaplet.rb @@ -67,26 +67,26 @@ 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) + 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 - response_stream = router.create_fault_response(e) - is_fault = true + 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 - 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 diff --git a/lib/soap/streamHandler.rb b/lib/soap/streamHandler.rb index d6b9c3bdca..0d08a00bd8 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 @@ -96,8 +89,8 @@ public "#<#{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 @@ -163,25 +156,24 @@ private raise NotImplementedError.new 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 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 +198,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 0961751474..cd93e3dee7 100644 --- a/lib/soap/wsdlDriver.rb +++ b/lib/soap/wsdlDriver.rb @@ -12,6 +12,7 @@ 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' @@ -212,6 +213,7 @@ class WSDLDriver method.type = XSD::QName.new # Request should not be typed. req_header = nil req_body = SOAPBody.new(method) + req_env = SOAPEnvelope.new(req_header, req_body) if @wiredump_file_base @streamhandler.wiredump_file_base = @@ -221,19 +223,19 @@ 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) + if res_env.body.fault + raise SOAP::FaultError.new(res_env.body.fault) end 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) @@ -249,14 +251,15 @@ 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 @@ -269,18 +272,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) diff --git a/lib/xsd/datatypes.rb b/lib/xsd/datatypes.rb index f318e612b1..7223632b04 100644 --- a/lib/xsd/datatypes.rb +++ b/lib/xsd/datatypes.rb @@ -268,8 +268,11 @@ end module FloatConstants NaN = 0.0/0.0 - POSITIVE_INF = 1.0/0.0 + POSITIVE_INF = +1.0/0.0 NEGATIVE_INF = -1.0/0.0 + POSITIVE_ZERO = +1.0/POSITIVE_INF + NEGATIVE_ZERO = -1.0/POSITIVE_INF + MIN_POSITIVE_SINGLE = 2 ** -149 end class XSDFloat < XSDAnySimpleType @@ -320,7 +323,7 @@ private elsif @data.infinite? == -1 '-INF' else - sign = (1 / @data > 0.0) ? '+' : '-' + sign = XSDFloat.positive?(@data) ? '+' : '-' sign + sprintf("%.10g", @data.abs).sub(/[eE]([+-])?0+/) { 'e' + $1 } end end @@ -329,11 +332,16 @@ private def narrow32bit(f) if f.nan? || f.infinite? f + elsif f.abs < MIN_POSITIVE_SINGLE + XSDFloat.positive?(f) ? POSITIVE_ZERO : NEGATIVE_ZERO else - packed = [f].pack("f") - (/\A\0*\z/ =~ packed)? 0.0 : f + f end end + + def self.positive?(value) + (1 / value) > 0.0 + end end # Ruby's Float is double-precision 64-bit floating point value. diff --git a/test/soap/calc/test_calc2.rb b/test/soap/calc/test_calc2.rb index b7fee71526..c066d374fb 100644 --- a/test/soap/calc/test_calc2.rb +++ b/test/soap/calc/test_calc2.rb @@ -30,6 +30,7 @@ class TestCalc2 < Test::Unit::TestCase end @endpoint = "http://localhost:#{Port}/" @var = SOAP::RPC::Driver.new(@endpoint, 'http://tempuri.org/calcService') + @var.wiredump_dev = STDERR if $DEBUG @var.add_method('set', 'newValue') @var.add_method('get') @var.add_method_as('+', 'add', 'rhs') diff --git a/test/soap/calc/test_calc_cgi.rb b/test/soap/calc/test_calc_cgi.rb index 523379f302..d1655b0b01 100644 --- a/test/soap/calc/test_calc_cgi.rb +++ b/test/soap/calc/test_calc_cgi.rb @@ -44,6 +44,7 @@ class TestCalcCGI < Test::Unit::TestCase end @endpoint = "http://localhost:#{Port}/server.cgi" @calc = SOAP::RPC::Driver.new(@endpoint, 'http://tempuri.org/calcService') + @calc.wiredump_dev = STDERR if $DEBUG @calc.add_method('add', 'lhs', 'rhs') @calc.add_method('sub', 'lhs', 'rhs') @calc.add_method('multi', 'lhs', 'rhs') diff --git a/test/soap/marshal/test_marshal.rb b/test/soap/marshal/test_marshal.rb index d530c06c24..1bbaa91a08 100644 --- a/test/soap/marshal/test_marshal.rb +++ b/test/soap/marshal/test_marshal.rb @@ -7,6 +7,7 @@ module Marshal module MarshalTestLib + NegativeZero = (-1.0 / (1.0 / 0.0)) module Mod1; end module Mod2; end @@ -205,7 +206,7 @@ module MarshalTestLib marshal_equal(1.0/0.0) marshal_equal(-1.0/0.0) marshal_equal(0.0/0.0) {|o| o.nan?} - marshal_equal(-1.0 / (1.0 / 0.0)) {|o| 1.0/o} # -0.0 + marshal_equal(NegativeZero) {|o| 1.0/o} end def test_float_ivar diff --git a/test/soap/test_basetype.rb b/test/soap/test_basetype.rb index 7a77946c2c..afd550f996 100644 --- a/test/soap/test_basetype.rb +++ b/test/soap/test_basetype.rb @@ -156,8 +156,8 @@ class TestSOAP < Test::Unit::TestCase targets = [ 3.14159265358979, 12.34e36, - 1.4e-45, - -1.4e-45, + 1.402e-45, + -1.402e-45, ] targets.each do |f| assert_equal(f, SOAP::SOAPFloat.new(f).data) @@ -166,8 +166,8 @@ class TestSOAP < Test::Unit::TestCase targets = [ "+3.141592654", "+1.234e+37", - "+1.4e-45", - "-1.4e-45", + "+1.402e-45", + "-1.402e-45", ] targets.each do |f| assert_equal(f, SOAP::SOAPFloat.new(f).to_s) @@ -178,13 +178,13 @@ class TestSOAP < Test::Unit::TestCase [-2, "-2"], # ditto [3.14159265358979, "+3.141592654"], [12.34e36, "+1.234e+37"], - [1.4e-45, "+1.4e-45"], - [-1.4e-45, "-1.4e-45"], - ["1.4e", "+1.4"], + [1.402e-45, "+1.402e-45"], + [-1.402e-45, "-1.402e-45"], + ["1.402e", "+1.402"], ["12.34E36", "+1.234e+37"], - ["1.4E-45", "+1.4e-45"], - ["-1.4E-45", "-1.4e-45"], - ["1.4E", "+1.4"], + ["1.402E-45", "+1.402e-45"], + ["-1.402E-45", "-1.402e-45"], + ["1.402E", "+1.402"], ] targets.each do |f, str| assert_equal(str, SOAP::SOAPFloat.new(f).to_s) @@ -221,8 +221,8 @@ class TestSOAP < Test::Unit::TestCase targets = [ 3.14159265358979, 12.34e36, - 1.4e-45, - -1.4e-45, + 1.402e-45, + -1.402e-45, ] targets.each do |f| assert_equal(f, SOAP::SOAPDouble.new(f).data) @@ -231,8 +231,8 @@ class TestSOAP < Test::Unit::TestCase targets = [ "+3.14159265358979", "+1.234e+37", - "+1.4e-45", - "-1.4e-45", + "+1.402e-45", + "-1.402e-45", ] targets.each do |f| assert_equal(f, SOAP::SOAPDouble.new(f).to_s) @@ -243,13 +243,13 @@ class TestSOAP < Test::Unit::TestCase [-2, "-2"], # ditto. [3.14159265358979, "+3.14159265358979"], [12.34e36, "+1.234e+37"], - [1.4e-45, "+1.4e-45"], - [-1.4e-45, "-1.4e-45"], - ["1.4e", "+1.4"], + [1.402e-45, "+1.402e-45"], + [-1.402e-45, "-1.402e-45"], + ["1.402e", "+1.402"], ["12.34E36", "+1.234e+37"], - ["1.4E-45", "+1.4e-45"], - ["-1.4E-45", "-1.4e-45"], - ["1.4E", "+1.4"], + ["1.402E-45", "+1.402e-45"], + ["-1.402E-45", "-1.402e-45"], + ["1.402E", "+1.402"], ] targets.each do |f, str| assert_equal(str, SOAP::SOAPDouble.new(f).to_s) diff --git a/test/soap/test_streamhandler.rb b/test/soap/test_streamhandler.rb index d38b84ed97..e5b578d7a6 100644 --- a/test/soap/test_streamhandler.rb +++ b/test/soap/test_streamhandler.rb @@ -142,9 +142,16 @@ __EOX__ end def test_basic_auth - # soap4r + basic_auth is not officially supported in ruby/1.8.1 even though - # soap4r + basic_auth + http-access2 should run fine. - return + unless Object.const_defined?('HTTPAccess2') + STDERR.puts("basic_auth is not supported under soap4r + net/http for now.") + return + end + str = "" + @client.wiredump_dev = str + @client.options["protocol.http.basic_auth"] << [@url, "foo", "bar"] + assert_nil(@client.do_server_proc) + r, h = parse_req_header(str) + assert_equal("Basic Zm9vOmJhcg==", h["authorization"]) end def test_proxy diff --git a/test/xsd/test_xsd.rb b/test/xsd/test_xsd.rb index db38aee7ca..ce1b263625 100644 --- a/test/xsd/test_xsd.rb +++ b/test/xsd/test_xsd.rb @@ -189,8 +189,8 @@ class TestXSD < Test::Unit::TestCase targets = [ 3.14159265358979, 12.34e36, - 1.4e-45, - -1.4e-45, + 1.402e-45, + -1.402e-45, ] targets.each do |f| assert_equal(f, XSD::XSDFloat.new(f).data) @@ -199,8 +199,8 @@ class TestXSD < Test::Unit::TestCase targets = [ "+3.141592654", "+1.234e+37", - "+1.4e-45", - "-1.4e-45", + "+1.402e-45", + "-1.402e-45", ] targets.each do |f| assert_equal(f, XSD::XSDFloat.new(f).to_s) @@ -211,13 +211,13 @@ class TestXSD < Test::Unit::TestCase [-2, "-2"], # ditto [3.14159265358979, "+3.141592654"], [12.34e36, "+1.234e+37"], - [1.4e-45, "+1.4e-45"], - [-1.4e-45, "-1.4e-45"], - ["1.4e", "+1.4"], + [1.402e-45, "+1.402e-45"], + [-1.402e-45, "-1.402e-45"], + ["1.402e", "+1.402"], ["12.34E36", "+1.234e+37"], - ["1.4E-45", "+1.4e-45"], - ["-1.4E-45", "-1.4e-45"], - ["1.4E", "+1.4"], + ["1.402E-45", "+1.402e-45"], + ["-1.402E-45", "-1.402e-45"], + ["1.402E", "+1.402"], ] targets.each do |f, str| assert_equal(str, XSD::XSDFloat.new(f).to_s) @@ -254,8 +254,8 @@ class TestXSD < Test::Unit::TestCase targets = [ 3.14159265358979, 12.34e36, - 1.4e-45, - -1.4e-45, + 1.402e-45, + -1.402e-45, ] targets.each do |f| assert_equal(f, XSD::XSDDouble.new(f).data) @@ -264,8 +264,8 @@ class TestXSD < Test::Unit::TestCase targets = [ "+3.14159265358979", "+1.234e+37", - "+1.4e-45", - "-1.4e-45", + "+1.402e-45", + "-1.402e-45", ] targets.each do |f| assert_equal(f, XSD::XSDDouble.new(f).to_s) @@ -276,13 +276,13 @@ class TestXSD < Test::Unit::TestCase [-2, "-2"], # ditto. [3.14159265358979, "+3.14159265358979"], [12.34e36, "+1.234e+37"], - [1.4e-45, "+1.4e-45"], - [-1.4e-45, "-1.4e-45"], - ["1.4e", "+1.4"], + [1.402e-45, "+1.402e-45"], + [-1.402e-45, "-1.402e-45"], + ["1.402e", "+1.402"], ["12.34E36", "+1.234e+37"], - ["1.4E-45", "+1.4e-45"], - ["-1.4E-45", "-1.4e-45"], - ["1.4E", "+1.4"], + ["1.402E-45", "+1.402e-45"], + ["-1.402E-45", "-1.402e-45"], + ["1.402E", "+1.402"], ] targets.each do |f, str| assert_equal(str, XSD::XSDDouble.new(f).to_s) -- cgit v1.2.3