Class: Handsoap::Service
- Inherits:
-
Object
- Object
- Handsoap::Service
- Defined in:
- lib/handsoap/service.rb,
lib/handsoap/service.rb
Constant Summary collapse
- @@logger =
nil- @@instance =
{}
Class Method Summary collapse
-
.endpoint(args = {}) ⇒ Object
Sets the endpoint for the service.
- .envelope_namespace ⇒ Object
- .fire_on_create_document(doc) ⇒ Object
- .get_mapping(name) ⇒ Object
- .instance ⇒ Object
- .logger=(io) ⇒ Object
-
.map_method(mapping) ⇒ Object
Registers a simple method mapping without any arguments and no parsing of response.
- .method_missing(method, *args) ⇒ Object
-
.on_create_document(&block) ⇒ Object
Registers a block to call when a request document is created.
- .request_content_type ⇒ Object
- .uri ⇒ Object
Instance Method Summary collapse
-
#debug(message = nil) ⇒ Object
:nodoc:.
-
#dispatch(doc, action) ⇒ Object
Send document and parses the response into a
XmlQueryFront::XmlElement(XmlDocument). -
#invoke(action, options = { :soap_action => :auto }, &block) ⇒ Object
Creates an XML document and sends it over HTTP.
-
#make_envelope ⇒ Object
Creates a standard SOAP envelope and yields the
Bodyelement. - #method_missing(method, *args) ⇒ Object
-
#on_after_create_http_client(http_client) ⇒ Object
Hook that is called after the http_client is created.
-
#on_before_dispatch ⇒ Object
Hook that is called before the message is dispatched.
-
#on_create_document(doc) ⇒ Object
Hook that is called when a new request document is created.
-
#on_fault(fault) ⇒ Object
Hook that is called if the dispatch returns a
Fault. -
#on_http_error(status, content) ⇒ Object
Hook that is called if there is a HTTP level error.
-
#on_missing_document(http_response_body) ⇒ Object
Hook that is called if the response does not contain a valid SOAP enevlope.
-
#on_response_document(doc) ⇒ Object
Hook that is called when there is a response.
-
#parse_soap_fault(document) ⇒ Object
XmlDocument -> [Fault | nil].
-
#parse_soap_response_document(http_body) ⇒ Object
String -> [XmlDocument | nil].
-
#send_http_request(uri, post_body, headers) ⇒ Object
Does the actual HTTP level interaction.
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args) ⇒ Object
449 450 451 452 453 454 455 456 |
# File 'lib/handsoap/service.rb', line 449 def method_missing(method, *args) action = self.class.get_mapping(method) if action invoke(action, *args) else super end end |
Class Method Details
.endpoint(args = {}) ⇒ Object
Sets the endpoint for the service. Arguments:
:uri => endpoint uri of the service. Required.
:version => 1 | 2
:envelope_namespace => Namespace of SOAP-envelope
:request_content_type => Content-Type of HTTP request.
You must supply either :version or both :envelope_namspace and :request_content_type. :version is simply a shortcut for default values.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/handsoap/service.rb', line 68 def self.endpoint(args = {}) @uri = args[:uri] || raise("Missing option :uri") if args[:version] soap_namespace = { 1 => 'http://schemas.xmlsoap.org/soap/envelope/', 2 => 'http://www.w3.org/2003/05/soap-envelope' } raise("Unknown protocol version '#{@protocol_version.inspect}'") if soap_namespace[args[:version]].nil? @envelope_namespace = soap_namespace[args[:version]] @request_content_type = args[:version] == 1 ? "text/xml" : "application/soap+xml" end @envelope_namespace = args[:envelope_namespace] unless args[:envelope_namespace].nil? @request_content_type = args[:request_content_type] unless args[:request_content_type].nil? if @envelope_namespace.nil? || @request_content_type.nil? raise("Missing option :envelope_namespace, :request_content_type or :version") end end |
.envelope_namespace ⇒ Object
82 83 84 |
# File 'lib/handsoap/service.rb', line 82 def self.envelope_namespace @envelope_namespace end |
.fire_on_create_document(doc) ⇒ Object
463 464 465 466 467 |
# File 'lib/handsoap/service.rb', line 463 def self.fire_on_create_document(doc) if @create_document_callback @create_document_callback.call doc end end |
.get_mapping(name) ⇒ Object
446 447 448 |
# File 'lib/handsoap/service.rb', line 446 def self.get_mapping(name) @mapping[name] if @mapping end |
.instance ⇒ Object
92 93 94 |
# File 'lib/handsoap/service.rb', line 92 def self.instance @@instance[self.to_s] ||= self.new end |
.logger=(io) ⇒ Object
57 58 59 |
# File 'lib/handsoap/service.rb', line 57 def self.logger=(io) @@logger = io end |
.map_method(mapping) ⇒ Object
Registers a simple method mapping without any arguments and no parsing of response.
This is deprecated
440 441 442 443 444 445 |
# File 'lib/handsoap/service.rb', line 440 def self.map_method(mapping) if @mapping.nil? @mapping = {} end @mapping.merge! mapping end |
.method_missing(method, *args) ⇒ Object
95 96 97 98 99 100 101 |
# File 'lib/handsoap/service.rb', line 95 def self.method_missing(method, *args) if instance.respond_to?(method) instance.__send__ method, *args else super end end |
.on_create_document(&block) ⇒ Object
Registers a block to call when a request document is created.
This is deprecated, in favour of #on_create_document
460 461 462 |
# File 'lib/handsoap/service.rb', line 460 def self.on_create_document(&block) @create_document_callback = block end |
.request_content_type ⇒ Object
85 86 87 |
# File 'lib/handsoap/service.rb', line 85 def self.request_content_type @request_content_type end |
.uri ⇒ Object
88 89 90 |
# File 'lib/handsoap/service.rb', line 88 def self.uri @uri end |
Instance Method Details
#debug(message = nil) ⇒ Object
:nodoc:
175 176 177 178 179 180 181 182 183 184 |
# File 'lib/handsoap/service.rb', line 175 def debug( = nil) #:nodoc: if @@logger if @@logger.puts() end if block_given? yield @@logger end end end |
#dispatch(doc, action) ⇒ Object
Send document and parses the response into a XmlQueryFront::XmlElement (XmlDocument)
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/handsoap/service.rb', line 219 def dispatch(doc, action) on_before_dispatch headers = { "Content-Type" => "#{self.class.request_content_type};charset=UTF-8" } headers["SOAPAction"] = action unless action.nil? body = doc.to_s debug do |logger| logger.puts "===============" logger.puts "--- Request ---" logger.puts "URI: %s" % [self.class.uri] logger.puts headers.map { |key,value| key + ": " + value }.join("\n") logger.puts "---" logger.puts body end response = send_http_request(self.class.uri, body, headers) debug do |logger| logger.puts "--- Response ---" logger.puts "HTTP Status: %s" % [response[:status]] logger.puts "Content-Type: %s" % [response[:content_type]] if response[:multipart] num = 0 response[:parts].each do |part| num += 1 logger.puts "--- Part ##{num} ---" logger.puts part[:head].gsub(/\r\n/, "\n") logger.puts "---" logger.puts Handsoap.pretty_format_envelope(part[:body]) end else logger.puts "---" logger.puts Handsoap.pretty_format_envelope(response[:body]) end end # Start the parsing pipe-line. # There are various stages and hooks for each, so that you can override those in your service classes. xml_document = parse_soap_response_document(response[:parts].first[:body]) # Strictly speaking, the main part doesn't need to be first, but until proven otherwise, we'll just assume that. soap_fault = parse_soap_fault(xml_document) # Is the response a soap-fault? unless soap_fault.nil? return on_fault(soap_fault) end # Does the http-status indicate an error? if response[:status] >= 400 return on_http_error(response[:status], response[:body]) end # Does the response contain a valid xml-document? if xml_document.nil? return on_missing_document(response[:body]) end # Everything seems in order. on_response_document(xml_document) # BC hack def xml_document.document self end # I should probably use a class for this response object instead ... def xml_document.parts response[:parts] end return xml_document end |
#invoke(action, options = { :soap_action => :auto }, &block) ⇒ Object
Creates an XML document and sends it over HTTP.
action is the QName of the rootnode of the envelope.
options currently takes one option :soap_action, which can be one of:
:auto sends a SOAPAction http header, deduced from the action name. (This is the default)
String sends a SOAPAction http header.
nil sends no SOAPAction http header.
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/handsoap/service.rb', line 113 def invoke(action, = { :soap_action => :auto }, &block) # :yields: Handsoap::XmlMason::Element if action if .kind_of? String = { :soap_action => } end if [:soap_action] == :auto [:soap_action] = action.gsub(/^.+:/, "") elsif [:soap_action] == :none [:soap_action] = nil end doc = make_envelope do |body| body.add action end if block_given? yield doc.find(action) end dispatch(doc, [:soap_action]) end end |
#make_envelope ⇒ Object
Creates a standard SOAP envelope and yields the Body element.
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/handsoap/service.rb', line 282 def make_envelope # :yields: Handsoap::XmlMason::Element doc = XmlMason::Document.new do |doc| doc.alias 'env', self.class.envelope_namespace doc.add "env:Envelope" do |env| env.add "*:Header" env.add "*:Body" end end self.class.fire_on_create_document doc # deprecated .. use instance method on_create_document(doc) if block_given? yield doc.find("Body") end return doc end |
#on_after_create_http_client(http_client) ⇒ Object
Hook that is called after the http_client is created.
You can override this to customize the http_client
145 146 |
# File 'lib/handsoap/service.rb', line 145 def on_after_create_http_client(http_client) end |
#on_before_dispatch ⇒ Object
Hook that is called before the message is dispatched.
You can override this to provide filtering and logging.
140 141 |
# File 'lib/handsoap/service.rb', line 140 def on_before_dispatch end |
#on_create_document(doc) ⇒ Object
Hook that is called when a new request document is created.
You can override this to add namespaces and other elements that are common to all requests (Such as authentication).
135 136 |
# File 'lib/handsoap/service.rb', line 135 def on_create_document(doc) end |
#on_fault(fault) ⇒ Object
Hook that is called if the dispatch returns a Fault.
Default behaviour is to raise the Fault, but you can override this to provide logging and more fine-grained handling faults.
See also: parse_soap_fault
163 164 165 |
# File 'lib/handsoap/service.rb', line 163 def on_fault(fault) raise fault end |
#on_http_error(status, content) ⇒ Object
Hook that is called if there is a HTTP level error.
Default behaviour is to raise an error.
155 156 157 |
# File 'lib/handsoap/service.rb', line 155 def on_http_error(status, content) raise "HTTP error #{status}" end |
#on_missing_document(http_response_body) ⇒ Object
Hook that is called if the response does not contain a valid SOAP enevlope.
Default behaviour is to raise an error
Note that if your service has operations that are one-way, you shouldn’t raise an error here. This is however a fairly exotic case, so that is why the default behaviour is to raise an error.
172 173 174 |
# File 'lib/handsoap/service.rb', line 172 def on_missing_document(http_response_body) raise "The response is not a valid SOAP envelope" end |
#on_response_document(doc) ⇒ Object
Hook that is called when there is a response.
You can override this to register common namespaces, useful for parsing the document.
150 151 |
# File 'lib/handsoap/service.rb', line 150 def on_response_document(doc) end |
#parse_soap_fault(document) ⇒ Object
XmlDocument -> [Fault | nil]
306 307 308 309 310 311 |
# File 'lib/handsoap/service.rb', line 306 def parse_soap_fault(document) unless document.nil? node = document.xpath('/env:Envelope/env:Body/descendant-or-self::env:Fault', { 'env' => self.class.envelope_namespace }).first Fault.from_xml(node, :namespace => self.class.envelope_namespace) unless node.nil? end end |
#parse_soap_response_document(http_body) ⇒ Object
String -> [XmlDocument | nil]
298 299 300 301 302 303 304 |
# File 'lib/handsoap/service.rb', line 298 def parse_soap_response_document(http_body) begin Handsoap::XmlQueryFront.parse_string(http_body, Handsoap.xml_query_driver) rescue Handsoap::XmlQueryFront::ParseError => ex nil end end |
#send_http_request(uri, post_body, headers) ⇒ Object
Does the actual HTTP level interaction.
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/handsoap/service.rb', line 186 def send_http_request(uri, post_body, headers) if Handsoap.http_driver == :curb http_client = Curl::Easy.new(uri) on_after_create_http_client(http_client) http_client.headers = headers http_client.http_post post_body if %r|\Amultipart.*boundary=\"?([^\";,]+)\"?|n.match(http_client.content_type) boundary = $1.dup parts = Handsoap.parse_multipart(boundary, http_client.body_str) is_multipart = true else parts = [{:head => http_client.header_str, :body => http_client.body_str}] is_multipart = false end return { :status => http_client.response_code, :body => http_client.body_str, :content_type => http_client.content_type, :parts => parts, :multipart => is_multipart } elsif Handsoap.http_driver == :httpclient http_client = HTTPClient.new on_after_create_http_client(http_client) response = http_client.post(uri, post_body, headers) if %r|\Amultipart.*boundary=\"?([^\";,]+)\"?|n.match(response.contenttype) boundary = $1.dup parts = Handsoap.parse_multipart(boundary, response.content) is_multipart = true else parts = [{:head => response.header.all.join("\r\n"), :body => response.content}] is_multipart = false end return { :status => response.status, :body => response.content, :content_type => response.contenttype, :parts => parts, :multipart => is_multipart } else raise "Unknown http driver #{Handsoap.http_driver}" end end |