savonrb / savon

Heavy metal SOAP client
https://www.savonrb.com
MIT License
2.07k stars 616 forks source link

HTTPClient::KeepAliveDisconnected on ONVIF unsubscribe request #969

Open migalenkom opened 2 years ago

migalenkom commented 2 years ago

Bug report

I am generating SOAP requests to IP cameras to use ONVIF API. When generating a request through Savon it doesn't work. However, when generating via the official tool request passes OK.

Current behavior: Here is the request generated through Savon and I am getting HTTPClient::KeepAliveDisconnected

[2022-04-26 21:29:16 +0000] SOAP request: http://192.168.0.89/onvif/Subscription?Idx=77
[2022-04-26 21:29:16 +0000] Content-Type: application/soap+xml; charset=utf-8, SOAPAction: "http://docs.oasis-open.org/wsn/bw-2/PausableSubscriptionManager/UnsubscribeRequest", Content-Length: 1709
[2022-04-26 21:29:16 +0000] <?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wsdl="http://www.onvif.org/ver10/events/wsdl" xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope">
  <SOAP-ENV:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
    <wsa:Action>http://docs.oasis-open.org/wsn/bw-2/PausableSubscriptionManager/UnsubscribeRequest</wsa:Action>
    <wsa:To>http://192.168.0.89/onvif/Subscription?Idx=77</wsa:To>
    <wsa:MessageID xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">urn:uuid:b4742513-fd62-44b5-a9d9-3db005e1a330</wsa:MessageID>
    <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>admin</wsse:Username>
        <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">M2M0NGJhMGI1Y2ZjMWYxYTRjMTkyYTg1NmFjNzg4ZDliOGIzNjkyOA==</wsse:Nonce>
        <wsu:Created>2022-04-26T21:29:16Z</wsu:Created>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">h+WeEanGY2sFUhBbhcd5Oi1ZHhw=</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>2022-04-26T21:29:16Z</wsu:Created>
        <wsu:Expires>2022-04-26T21:30:16Z</wsu:Expires>
      </wsu:Timestamp>
    </wsse:Security>
  </SOAP-ENV:Header>
  <SOAP-ENV:Body>
    <wsdl:Unsubscribe/>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

[2022-04-26 21:29:16 +0000] HTTPI /none POST request to 192.168.0.89 (httpclient)
/data/deployer/onvif/vendor/bundle/ruby/3.1.0/gems/httpclient-2.8.3/lib/httpclient/session.rb:808:in `block in parse_header': HTTPClient::KeepAliveDisconnected:  (HTTPClient::KeepAliveDisconnected)
/data/deployer/onvif/vendor/bundle/ruby/3.1.0/gems/httpclient-2.8.3/lib/httpclient/session.rb:808:in `block in parse_header': HTTPClient::KeepAliveDisconnected:  `(HTTPClient::KeepAliveDisconnected)

Request generated via the official tool:


POST /onvif/Subscription?Idx=87 HTTP/1.1
Host: 192.168.0.89
Content-Type: application/soap+xml; charset=utf-8
Authorization: Digest username="admin",realm="Login to 2L05ABCPAA00246",qop="auth",algorithm=MD5,uri="/onvif/Subscription?Idx=87",nonce="b252aWYtZGlnZXN0OjQyOTc3MDM4Njcw",nc=00000001,cnonce="AA1E66694E8FDFD77C29FC359DAA0C15",opaque="",response="9a6aa57108955142ddb522698d20c274"
Content-Length: 713

<?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://docs.oasis-open.org/wsn/bw-2/SubscriptionManager/UnsubscribeRequest</a:Action>
    <a:MessageID>urn:uuid:bf921174-c408-48f7-97c8-f5f64ee3462e</a:MessageID>
    <a:ReplyTo>
      <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
    </a:ReplyTo>
    <a:To s:mustUnderstand="1">http://192.168.0.89/onvif/Subscription?Idx=87</a:To>
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Unsubscribe xmlns="http://docs.oasis-open.org/wsn/b-2" />
  </s:Body>
</s:Envelope>

Steps to reproduce current behavior:

require 'savon'

@ip.           = 'ip'
@user        = 'user'
@pass.      = 'pass'
@wsdl        = 'http://www.onvif.org/ver10/events/wsdl/event.wsdl'
@endpoint = 'http://192.168.0.89/onvif/event_service'

@soap_client = Savon.client do |c|
  c.wsdl wsdl
  c.endpoint @endpoint
  c.wsse_auth(user, pass, :digest)
  c.use_wsa_headers true
  c.wsse_timestamp true
  c.convert_request_keys_to :none
  c.env_namespace 'SOAP-ENV'
  c.open_timeout 60
  c.read_timeout 60
  c.soap_version 2
  c.headers 'Content-Type' => 'application/soap+xml; charset=utf-8'
  c.pretty_print_xml true
end
@soap_client.call(:create_pull_point_subscription)
...
@soap_client.call(:unsubscribe)

Expected behavior:

Generated request passes OK

System information:

olleolleolle commented 2 years ago

To me, by just looking at the thing, the top one seems to use the wsse type of authentication, which is a Web Service description of authentication. The lower one seems to have a Digest header only.

Could it be that the service authenticates requests in some other way than the wsse thing?

migalenkom commented 2 years ago

@olleolleolle Hi thanks for the suggestion. I tried different types of auth but ONVIF cameras support both wsse and digest so it should not be auth issue. The next thing that I tried is changing namespaces and that made a request to work. HTTPClient::KeepAliveDisconnected doesn't look like a correct error in this case.

The Official tool adds xmlns="http://docs.oasis-open.org/wsn/b-2" straight to the Unsubscribe tag.

<Unsubscribe xmlns="http://docs.oasis-open.org/wsn/b-2" />

Savon builds requests in a different way and adds a namespace wsdl:Unsubscribe with namespace declaration with wrong namespace xmlns:wsdl="http://www.onvif.org/ver10/events/wsdl"

 <wsdl:Unsubscribe/>

I did not manage to add xmlns="http://docs.oasis-open.org/wsn/b-2" or remove wsdl namespace from Unsubscribe tag but managed to override it namespaces: { 'xmlns:wsdl' => "http://docs.oasis-open.org/wsn/b-2" }

Is there a way I can build the same request with savon as the official tools offer?