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, &block) ⇒ 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
-
#async(user_block) {|dispatcher| ... } ⇒ Object
Async invocation.
-
#debug(message = nil) ⇒ Object
:nodoc:.
- #envelope_namespace ⇒ Object
- #http_driver_instance ⇒ Object
-
#invoke(action, options = { :soap_action => :auto, :http_options => nil }, &block) ⇒ Object
Creates an XML document and sends it over HTTP.
-
#iterate_hash_array(element, hash_array) ⇒ Object
Used to iterate over a Hash, that can include Hash, Array or String/Float/Integer etc and insert it in the correct element.
-
#make_envelope ⇒ Object
Creates a standard SOAP envelope and yields the
Body
element. - #make_http_request(uri, post_body, headers, http_options = nil) ⇒ Object
- #method_missing(method, *args, &block) ⇒ Object
-
#on_after_create_http_request(http_request) ⇒ Object
Hook that is called after the http_client is created.
-
#on_before_dispatch(doc) ⇒ 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(response) ⇒ Object
Hook that is called if there is a HTTP level error.
-
#on_missing_document(response) ⇒ 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_http_response(response) ⇒ Object
Start the parsing pipe-line.
-
#parse_soap_fault(document) ⇒ Object
XmlDocument -> [Fault | nil].
-
#parse_soap_response_document(http_body) ⇒ Object
String -> [XmlDocument | nil].
- #request_content_type ⇒ Object
- #uri ⇒ Object
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args, &block) ⇒ Object
539 540 541 542 543 544 545 546 |
# File 'lib/handsoap/service.rb', line 539 def method_missing(method, *args, &block) action = self.class.get_mapping(method) if action invoke(action, *args, &block) 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.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/handsoap/service.rb', line 173 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
187 188 189 |
# File 'lib/handsoap/service.rb', line 187 def self.envelope_namespace @envelope_namespace end |
.fire_on_create_document(doc) ⇒ Object
553 554 555 556 557 |
# File 'lib/handsoap/service.rb', line 553 def self.fire_on_create_document(doc) if @create_document_callback @create_document_callback.call doc end end |
.get_mapping(name) ⇒ Object
536 537 538 |
# File 'lib/handsoap/service.rb', line 536 def self.get_mapping(name) @mapping[name] if @mapping end |
.instance ⇒ Object
197 198 199 |
# File 'lib/handsoap/service.rb', line 197 def self.instance @@instance[self.to_s] ||= self.new end |
.logger=(io) ⇒ Object
162 163 164 |
# File 'lib/handsoap/service.rb', line 162 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
530 531 532 533 534 535 |
# File 'lib/handsoap/service.rb', line 530 def self.map_method(mapping) if @mapping.nil? @mapping = {} end @mapping.merge! mapping end |
.method_missing(method, *args, &block) ⇒ Object
200 201 202 203 204 205 206 |
# File 'lib/handsoap/service.rb', line 200 def self.method_missing(method, *args, &block) if instance.respond_to?(method) instance.__send__ method, *args, &block 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
550 551 552 |
# File 'lib/handsoap/service.rb', line 550 def self.on_create_document(&block) @create_document_callback = block end |
.request_content_type ⇒ Object
190 191 192 |
# File 'lib/handsoap/service.rb', line 190 def self.request_content_type @request_content_type end |
.uri ⇒ Object
193 194 195 |
# File 'lib/handsoap/service.rb', line 193 def self.uri @uri end |
Instance Method Details
#async(user_block) {|dispatcher| ... } ⇒ Object
Async invocation
Creates an XML document and sends it over HTTP.
user_block
Block from userland
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
# File 'lib/handsoap/service.rb', line 274 def async(user_block, &block) # :yields: Handsoap::AsyncDispatch # Setup userland handlers userland = Handsoap::Deferred.new user_block.call(userland) raise "Missing :callback" unless userland.has_callback? raise "Missing :errback" unless userland.has_errback? # Setup service level handlers dispatcher = Handsoap::AsyncDispatch.new yield dispatcher raise "Missing :request_block" unless dispatcher.request_block raise "Missing :response_block" unless dispatcher.response_block # Done with the external configuration .. let's roll action = dispatcher.action = dispatcher. if action #TODO: What if no 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,header| if [:soap_header] iterate_hash_array(header, [:soap_header]) end if [:soap_body] action_hash = { action => [:soap_body] } iterate_hash_array(body, action_hash) else body.add(action) end end dispatcher.request_block.call doc.find(action) # ready to dispatch headers = { "Content-Type" => "#{self.request_content_type}; charset=UTF-8" } headers["SOAPAction"] = [:soap_action] unless [:soap_action].nil? on_before_dispatch(doc) request = make_http_request(self.uri, doc.to_s, headers) driver = self.http_driver_instance if driver.respond_to? :send_http_request_async deferred = driver.send_http_request_async(request) else # Fake async for sync-only drivers deferred = Handsoap::Deferred.new begin deferred.trigger_callback driver.send_http_request(request) rescue deferred.trigger_errback $! end end deferred.callback do |http_response| begin # Parse response response_document = parse_http_response(http_response) # Transform response result = dispatcher.response_block.call(response_document) # Yield to userland code userland.trigger_callback(result) rescue userland.trigger_errback $! end end # Pass driver level errors on deferred.errback do |ex| userland.trigger_errback(ex) end end return nil end |
#debug(message = nil) ⇒ Object
:nodoc:
413 414 415 416 417 418 419 420 421 422 |
# File 'lib/handsoap/service.rb', line 413 def debug( = nil) #:nodoc: if @@logger if @@logger.puts() end if block_given? yield @@logger end end end |
#envelope_namespace ⇒ Object
207 208 209 |
# File 'lib/handsoap/service.rb', line 207 def envelope_namespace self.class.envelope_namespace end |
#http_driver_instance ⇒ Object
216 217 218 |
# File 'lib/handsoap/service.rb', line 216 def http_driver_instance Handsoap::Http.drivers[Handsoap.http_driver].new end |
#invoke(action, options = { :soap_action => :auto, :http_options => nil }, &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.
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 |
# File 'lib/handsoap/service.rb', line 230 def invoke(action, = { :soap_action => :auto, :http_options => nil }, &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,header| if [:soap_header] iterate_hash_array(header, [:soap_header]) end if [:soap_body] action_hash = { action => [:soap_body] } iterate_hash_array(body, action_hash) else body.add(action) end end if block_given? yield doc.find(action) end # ready to dispatch headers = { "Content-Type" => "#{self.request_content_type}; charset=UTF-8" } headers["SOAPAction"] = [:soap_action] unless [:soap_action].nil? on_before_dispatch(doc) request = make_http_request(self.uri, doc.to_s, headers, [:http_options]) response = http_driver_instance.send_http_request(request) parse_http_response(response) end end |
#iterate_hash_array(element, hash_array) ⇒ Object
Used to iterate over a Hash, that can include Hash, Array or String/Float/Integer etc and insert it in the correct element.
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/handsoap/service.rb', line 352 def iterate_hash_array(element, hash_array) hash_array.each {|hash| iterate_hash_array(element, hash) } if hash_array.is_a?(Array) hash_array.each do |name,value| if value.is_a?(Hash) element.add(name){|subelement| iterate_hash_array(subelement, value)} elsif value.is_a?(Array) element.add(name) do |subelement| value.each do |item| iterate_hash_array(subelement, item) if item.is_a?(Hash) end end else element.add name, value.to_s end end end |
#make_envelope ⇒ Object
Creates a standard SOAP envelope and yields the Body
element.
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 |
# File 'lib/handsoap/service.rb', line 476 def make_envelope # :yields: Handsoap::XmlMason::Element doc = XmlMason::Document.new do |doc| doc.alias 'env', self.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"),doc.find("Header") end return doc end |
#make_http_request(uri, post_body, headers, http_options = nil) ⇒ Object
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
# File 'lib/handsoap/service.rb', line 424 def make_http_request(uri, post_body, headers, =nil) request = Handsoap::Http::Request.new(uri, :post) # SSL CA AND CLIENT CERTIFICATES if request.set_trust_ca_file([:trust_ca_file]) if [:trust_ca_file] request.set_client_cert_files([:client_cert_file], [:client_cert_key_file]) if [:client_cert_file] && [:client_cert_key_file] request.set_ssl_verify_mode([:ssl_verify_mode]) if [:ssl_verify_mode] end headers.each do |key, value| request.add_header(key, value) end request.body = post_body debug do |logger| logger.puts request.inspect end on_after_create_http_request(request) request end |
#on_after_create_http_request(http_request) ⇒ Object
Hook that is called after the http_client is created.
You can override this to customize the http_client
382 383 |
# File 'lib/handsoap/service.rb', line 382 def on_after_create_http_request(http_request) end |
#on_before_dispatch(doc) ⇒ Object
Hook that is called before the message is dispatched.
You can override this to provide filtering and logging.
377 378 |
# File 'lib/handsoap/service.rb', line 377 def on_before_dispatch(doc) 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).
372 373 |
# File 'lib/handsoap/service.rb', line 372 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
400 401 402 |
# File 'lib/handsoap/service.rb', line 400 def on_fault(fault) raise fault end |
#on_http_error(response) ⇒ Object
Hook that is called if there is a HTTP level error.
Default behaviour is to raise an error.
392 393 394 |
# File 'lib/handsoap/service.rb', line 392 def on_http_error(response) raise HttpError, response end |
#on_missing_document(response) ⇒ 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.
409 410 411 |
# File 'lib/handsoap/service.rb', line 409 def on_missing_document(response) 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.
387 388 |
# File 'lib/handsoap/service.rb', line 387 def on_response_document(doc) end |
#parse_http_response(response) ⇒ Object
Start the parsing pipe-line. There are various stages and hooks for each, so that you can override those in your service classes.
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 |
# File 'lib/handsoap/service.rb', line 447 def parse_http_response(response) debug do |logger| logger.puts(response.inspect do |body| Handsoap.pretty_format_envelope(body.force_encoding('utf-8')).chomp end) end raw_xml_document = response.primary_part.body.force_encoding('utf-8') xml_document = parse_soap_response_document(raw_xml_document) 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) end # Does the response contain a valid xml-document? if xml_document.nil? return on_missing_document(response) end # Everything seems in order. on_response_document(xml_document) args = [xml_document, response] args << raw_xml_document if Handsoap.store_raw_response? return SoapResponse.new(*args) end |
#parse_soap_fault(document) ⇒ Object
XmlDocument -> [Fault | nil]
502 503 504 505 506 507 |
# File 'lib/handsoap/service.rb', line 502 def parse_soap_fault(document) unless document.nil? node = document.xpath('/env:Envelope/env:Body/descendant-or-self::env:Fault', { 'env' => self.envelope_namespace }).first Fault.from_xml(node, :namespace => self.envelope_namespace) unless node.nil? end end |
#parse_soap_response_document(http_body) ⇒ Object
String -> [XmlDocument | nil]
493 494 495 496 497 498 499 |
# File 'lib/handsoap/service.rb', line 493 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 |
#request_content_type ⇒ Object
210 211 212 |
# File 'lib/handsoap/service.rb', line 210 def request_content_type self.class.request_content_type end |
#uri ⇒ Object
213 214 215 |
# File 'lib/handsoap/service.rb', line 213 def uri self.class.uri end |