Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add "addressable" namespace + Action header support #409

Closed
michiels opened this issue Mar 18, 2013 · 19 comments
Closed

Add "addressable" namespace + Action header support #409

michiels opened this issue Mar 18, 2013 · 19 comments

Comments

@michiels
Copy link

I am working with a webservice that needs the Addressable namespace and <Action> header to be set for each request in the <Header>. Currently, you can define namespaces and soap_header as globals.

However, the contents of the <Action> element will change based on the action the request goes to, so it doesn't seem right to "hack around" this using the global options. It would be nice to have a global setting like use_addressable_headers: true or something to disable this for each request to be able to generate the following request XML:

Current code:

@client = Savon.client(
      wsdl: "https://server/webservice.wsdl",
      ssl_verify_mode: :none, soap_version: 2,
      soap_header: { "wsa:Action" => "http://tempuri.org/IWebService/GetStockInfo" },
      namespace_identifier: :tem,
      env_namespace: :soap,
      namespaces: namespaces)

    response = @client.call :get_stock_info message: { "tem:hash" => "api_key", "tem:itemcode" => "0220616200" }

New proposal code:

@client = Savon.client(
      wsdl: "https://server/webservice.wsdl",
      ssl_verify_mode: :none, soap_version: 2,
      use_addressable_headers: true,
      namespace_identifier: :tem,
      env_namespace: :soap)

response = @client.call :get_stock_info, soap_action:  "http://tempuri.org/IWebService/GetStockInfo", message: { "tem:hash" => "api_key", "tem:itemcode" => "0220616200" }

Request XML:

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:tem="http://tempuri.org/"   xmlns:soap="http://www.w3.org/2003/05/soap-envelope"
  xmlns:wsa="http://www.w3.org/2005/08/addressing">
<soap:Header>
  <wsa:Action>http://tempuri.org/IWebservice/GetStockInfo</wsa:Action>
</soap:Header>
<soap:Body>
  <tem:GetStockInfo>
    <tem:hash>api_key</tem:hash>
    <tem:itemcode>0220616200</tem:itemcode>
  </tem:GetStockInfo>
</soap:Body>
</soap:Envelope>
@rubiii
Copy link
Contributor

rubiii commented Mar 19, 2013

not sure about how this could work with the current header implementation,
but could you add your wsdl as a gist or pastie for reference?

@rubiii
Copy link
Contributor

rubiii commented Apr 19, 2013

closing this because it's getting stale. feel free to re-open when you can follow up.

@rubiii rubiii closed this as completed Apr 19, 2013
@locochris
Copy link

+1

the wsdl we're working with:

https://externalsuppliers.staging.serko.travel/SOAP/GenericProfile.svc?wsdl

requires:

client = Savon.client(
  wsdl: "https://externalsuppliers.staging.serko.travel/SOAP/GenericProfile.svc?wsdl",
  wsse_auth: ['username', 'password'],
  wsse_timestamp: true,
  ssl_verify_mode: :none,
  headers: {
    'Content-Type' => 'application/soap+xml; charset=utf-8'
  },
  soap_version: 2,
  namespaces: {
    'xmlns:a' => "http://www.w3.org/2005/08/addressing"
  },
  soap_header: %{
    <a:To env:mustUnderstand="1">https://externalsuppliers.staging.serko.travel/SOAP/GenericProfile.svc</a:To>
    <a:Action env:mustUnderstand="1">http://tempuri.org/IGenericProfile/Connectivity</a:Action>
  }
)

p client.operations
# => [:maintain, :connectivity]

response = client.call(:connectivity)

which generates:

<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://tempuri.org/" xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
  <env:Header>
    <a:To env:mustUnderstand="1">https://externalsuppliers.staging.serko.travel/SOAP/GenericProfile.svc</a:To>
    <a:Action env:mustUnderstand="1">http://tempuri.org/IGenericProfile/Connectivity</a:Action>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-1">
        <wsse:Username>username</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
      </wsse:UsernameToken>
      <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-2">
        <wsu:Created>2013-06-07T02:05:51Z</wsu:Created>
        <wsu:Expires>2013-06-07T02:06:51Z</wsu:Expires>
      </wsu:Timestamp>
    </wsse:Security>
  </env:Header>
  <env:Body>
    <tns:Connectivity/>
  </env:Body>
</env:Envelope>

... but of course this fails for the :maintain operation because the Action header is already set to http://tempuri.org/IGenericProfile/Connectivity

@rubiii
Copy link
Contributor

rubiii commented Jun 7, 2013

@locochris there is a workaround for this problem in version 2. you can access the global options via client.globals which allows you to change the soap header before each request. so you should be able to do something like this:

client.globals.header('...')
client.call(:connectivity)

hope that helps. thanks for providing a wsdl!

@rubiii rubiii mentioned this issue Jun 7, 2013
17 tasks
@locochris
Copy link

@rubiii ahhh ... cant' wait for v4.0 now!!

So will "Support WS-Policy/WS-Addressable" also help with my To header?

Please people send in your wsdl too, would ❤️ to see this awesome gem get even better!

@rubiii
Copy link
Contributor

rubiii commented Jun 7, 2013

@locochris yes, as far as i know, WS-Addressable specifies the Action and To headers.

@rubiii rubiii mentioned this issue Jun 29, 2013
19 tasks
@tjarratt tjarratt mentioned this issue Apr 22, 2014
19 tasks
@davispuh
Copy link

davispuh commented May 2, 2014

Any update for this? Looking for WS-Addressing (http://www.w3.org/2005/08/addressing)

@tjarratt
Copy link
Contributor

tjarratt commented May 3, 2014

@davispuh reading through this issue, it's not entirely clear to me what work remains to be done here. This issue feels very stale from my perspective (no one commented for almost a year after @rubiii identified a workaround).

Is there something concrete we could change in savon today that would help you with WS-Addressing? I'm not too familiar with that part of the spec, so framing the discussion around "I need to make a request that looks like this XML, but Savon can only generate XML like this" would be helpful from my perspective.

@davispuh
Copy link

davispuh commented May 3, 2014

An example of how addressable SOAP would look like

<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
   <s:Header>
      <a:Action s:mustUnderstand="1">http://tempuri.org/Interface/GetMethod</a:Action>
      <a:MessageID>urn:uuid:00000000-0000-0000-0000-000000000000</a:MessageID>
      <a:ReplyTo>
         <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
      </a:ReplyTo>
      <a:To s:mustUnderstand="1">https://services.example.com/endpoint.svc</a:To>
   </s:Header>
   <s:Body>
    <!-- usual SOAP -->
   </s:Body>
</s:Envelope>

Currently it can be made this way using Savon

endpoint = "https://services.example.com/endpoint.svc"
namespace = "http://tempuri.org/"
interface= "Interface"
actionMethod = 'GetMethod'

client = Savon.client(
  endpoint: endpoint,
  namespace: namespace ,
  convert_request_keys_to: :none,
  namespaces: { 'xmlns:a' => 'http://www.w3.org/2005/08/addressing' },
  soap_header: %{
      <a:Action s:mustUnderstand="1">#{namespace}#{interface}/#{actionMethod}</a:Action>
      <a:MessageID>urn:uuid:00000000-0000-0000-0000-000000000000</a:MessageID>
      <a:ReplyTo>
         <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
      </a:ReplyTo>
      <a:To s:mustUnderstand="1">#{endpoint}</a:To>
   }
)
response = client.call(:GetMethod, message: test: 'test' )
client.globals[:soap_header] = # set header again with updated a:Action so actionMethod is GetOtherMethod
# and also regenerate MessageID if it's used.
response = client.call(:GetOtherMethod, message: test: 'test' )

Which is a bit ugly. Firstly have to setup SOAP headers and worse is that have to do it each time, because Action must match call method.

It's used my Microsoft Web Services and C# handles it all automatically. The only changes needed to Savon would be to allow to use Action and To (MessageID, ReplyTo are optional) fields from WS-Addressing in some nice way. Basically MessageID is unique identification for message (anyURI) but typically set as urn:uuid:UUID.

alexanderk23 added a commit to alexanderk23/savon that referenced this issue May 3, 2014
@alexanderk23
Copy link
Contributor

Please take a look at commit 1d2e2dd. You may find it useful, but Wasabi still needs to be fixed because it doesn't know yet how to build correct default action name if soapAction is not set explicitly.

@davispuh
Copy link

davispuh commented May 3, 2014

cool, thanks a loads :) this really makes it pretty.

client = Savon.client(
  endpoint: endpoint,
  namespace: namespace,
  use_wsa_headers: true,
  convert_request_keys_to: :none
)
responseA = client.call(:GetMethod, soap_action: "#{namespace}#{interface}/GetMethod", message: m)
responseB = client.call(:GetOtherMethod, soap_action: "#{namespace}#{interface}/GetOtherMethod", message: m)

@alexanderk23
Copy link
Contributor

@davispuh no problem :) I'll do a clean PR if @tjarratt decide that it's appropriate.
Also, there is an issue #391 which seems to be a duplicate of this one.

@tjarratt
Copy link
Contributor

tjarratt commented May 5, 2014

Thank you @davispuh and @alexanderk23 for enlightening me. I'd be more than happy to merge a PR for this functionality.

Is there a reason why :use_wsa_headers defaults to false? I can't find a line in the spec that says you shouldn't include these headers, and my assumption is that most servers ignore them when they are sent.

@alexanderk23
Copy link
Contributor

I agree that having these headers included by default should be safe (can't prove it for sure, but from the specs it seems to be true as long as we're not explicitly require the endpoint to understand these headers by specifying env:mustUnderstand="1" attribute). The other thing is that a lot of tests will fail in that case.

@tjarratt
Copy link
Contributor

tjarratt commented May 6, 2014

For now this behavior will be opt in, but I plan on making this the default soon.

@alexanderk23
Copy link
Contributor

It seems that there could be more elegant solution of turning this on/off automagically based on the WS-Addressing policy specified in WSDL, but I'm not yet sure how to implement it.

@tjarratt
Copy link
Contributor

tjarratt commented May 9, 2014

That sounds like an even better approach, although I suspect some users will still need the ability to toggle the feature off (because not all SOAP servers are compliant with the spec).

@bezborodow
Copy link

The :use_wsa_headers option seems to generate WS-Addressing headers with a mix of two namespaces:

Both of the above seem to be deprecated (but are still in use) in favour of:

I must admit that I do not know the history behind the evolution of the WS-Addressing namespaces, but in any case, it would seem to me that the namespaces should be configurable?

@bezborodow
Copy link

Please see also my comment here: 423b67a

I can open a new issue if this is a bug/feature worth considering separately?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

7 participants