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 }, &block) ⇒ Object
Creates an XML document and sends it over HTTP.
-
#iterate_hash(element, hash) ⇒ 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 ⇒ 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
509 510 511 512 513 514 515 516 |
# File 'lib/handsoap/service.rb', line 509 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.
160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/handsoap/service.rb', line 160 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
174 175 176 |
# File 'lib/handsoap/service.rb', line 174 def self.envelope_namespace @envelope_namespace end |
.fire_on_create_document(doc) ⇒ Object
523 524 525 526 527 |
# File 'lib/handsoap/service.rb', line 523 def self.fire_on_create_document(doc) if @create_document_callback @create_document_callback.call doc end end |
.get_mapping(name) ⇒ Object
506 507 508 |
# File 'lib/handsoap/service.rb', line 506 def self.get_mapping(name) @mapping[name] if @mapping end |
.instance ⇒ Object
184 185 186 |
# File 'lib/handsoap/service.rb', line 184 def self.instance @@instance[self.to_s] ||= self.new end |
.logger=(io) ⇒ Object
149 150 151 |
# File 'lib/handsoap/service.rb', line 149 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
500 501 502 503 504 505 |
# File 'lib/handsoap/service.rb', line 500 def self.map_method(mapping) if @mapping.nil? @mapping = {} end @mapping.merge! mapping end |
.method_missing(method, *args, &block) ⇒ Object
187 188 189 190 191 192 193 |
# File 'lib/handsoap/service.rb', line 187 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
520 521 522 |
# File 'lib/handsoap/service.rb', line 520 def self.on_create_document(&block) @create_document_callback = block end |
.request_content_type ⇒ Object
177 178 179 |
# File 'lib/handsoap/service.rb', line 177 def self.request_content_type @request_content_type end |
.uri ⇒ Object
180 181 182 |
# File 'lib/handsoap/service.rb', line 180 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
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 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 |
# File 'lib/handsoap/service.rb', line 259 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| body.add action 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 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:
387 388 389 390 391 392 393 394 395 396 |
# File 'lib/handsoap/service.rb', line 387 def debug( = nil) #:nodoc: if @@logger if @@logger.puts() end if block_given? yield @@logger end end end |
#envelope_namespace ⇒ Object
194 195 196 |
# File 'lib/handsoap/service.rb', line 194 def envelope_namespace self.class.envelope_namespace end |
#http_driver_instance ⇒ Object
203 204 205 |
# File 'lib/handsoap/service.rb', line 203 def http_driver_instance Handsoap::Http.drivers[Handsoap.http_driver].new 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.
217 218 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 |
# File 'lib/handsoap/service.rb', line 217 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,header| if [:soap_header] iterate_hash(header, [:soap_header]) end body.add action if [:soap_body] iterate_hash(body, [:soap_body]) 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 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(element, hash) ⇒ Object
Used to iterate over a Hash, that can include Hash, Array or String/Float/Integer etc and insert it in the correct element.
328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/handsoap/service.rb', line 328 def iterate_hash(element, hash) hash.each do |name,value| if value.is_a?(Hash) element.add(name){|subelement| iterate_hash(subelement, value)} elsif value.is_a?(Array) value.each do |item| element.add(name, iterate_hash(element,item)) if item.is_a?(Hash) end else puts "#{name.to_s}: #{name.class.to_s} - #{value.to_s}:#{value.class.to_s}" element.add name, value.to_s end end end |
#make_envelope ⇒ Object
Creates a standard SOAP envelope and yields the Body
element.
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
# File 'lib/handsoap/service.rb', line 446 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
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 |
# File 'lib/handsoap/service.rb', line 398 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] 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
356 357 |
# File 'lib/handsoap/service.rb', line 356 def on_after_create_http_request(http_request) end |
#on_before_dispatch ⇒ Object
Hook that is called before the message is dispatched.
You can override this to provide filtering and logging.
351 352 |
# File 'lib/handsoap/service.rb', line 351 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).
346 347 |
# File 'lib/handsoap/service.rb', line 346 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
374 375 376 |
# File 'lib/handsoap/service.rb', line 374 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.
366 367 368 |
# File 'lib/handsoap/service.rb', line 366 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.
383 384 385 |
# File 'lib/handsoap/service.rb', line 383 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.
361 362 |
# File 'lib/handsoap/service.rb', line 361 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.
420 421 422 423 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 420 def parse_http_response(response) debug do |logger| logger.puts(response.inspect do |body| Handsoap.pretty_format_envelope(body).chomp end) end xml_document = parse_soap_response_document(response.primary_part.body) 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) return SoapResponse.new(xml_document, response) end |
#parse_soap_fault(document) ⇒ Object
XmlDocument -> [Fault | nil]
472 473 474 475 476 477 |
# File 'lib/handsoap/service.rb', line 472 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]
463 464 465 466 467 468 469 |
# File 'lib/handsoap/service.rb', line 463 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
197 198 199 |
# File 'lib/handsoap/service.rb', line 197 def request_content_type self.class.request_content_type end |
#uri ⇒ Object
200 201 202 |
# File 'lib/handsoap/service.rb', line 200 def uri self.class.uri end |