mirror of
https://github.com/kerberos-io/onvif.git
synced 2026-08-23 15:08:33 +00:00
AXIS encodes pull-point subscription identity in
<wsa:ReferenceParameters> inside the CreatePullPointSubscription
response — a generic /onvif/services endpoint plus a
<dom0:SubscriptionId> child — rather than a per-subscription URL.
We were discarding the ReferenceParameters and POSTing to the
generic endpoint, which AXIS rejected with ter:InvalidArgs on every
PullMessages/Renew/Unsubscribe.
Per WS-Addressing 1.0 §3.1 each reference parameter MUST be echoed
as a SOAP Header block carrying wsa:IsReferenceParameter="true".
- subscriptionRef now carries Address + the verbatim
ReferenceParameters inner XML extracted from the create response.
- buildRefParamsHeader walks the children, adds the attribute, and
produces the SOAP Header content.
- pullMessages, renewPullPoint, unsubscribePullPoint switch from
SendSoap to a new SendSoapWithHeader path on the caller interface.
- onvif.Device gains SendSoapWithHeader as a thin variant of
SendSoap (existing SendSoap is now a one-liner delegating to it
with empty header content, so all external callers are unaffected).
Verified end-to-end against an AXIS camera at 192.168.1.10: pulls
now stream the full topic tree (VMD, Object Analytics, IO, storage,
hardware-failure topics) instead of looping on ter:InvalidArgs.
74 lines
2.4 KiB
Go
74 lines
2.4 KiB
Go
package onvif
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestDevice_SetDeviceInfoFromScopes(t *testing.T) {
|
|
const (
|
|
name = "DeviceName"
|
|
hardware = "M9000"
|
|
)
|
|
scopes := []string{
|
|
"onvif://www.onvif.org/Profile/Streaming",
|
|
"onvif://www.onvif.org/SomethingElse/value",
|
|
"onvif://www.onvif.org/name/" + name,
|
|
"onvif://www.onvif.org/hardware/" + hardware,
|
|
}
|
|
device := Device{}
|
|
device.SetDeviceInfoFromScopes(scopes)
|
|
assert.Equal(t, device.info.Name, name)
|
|
assert.Equal(t, device.info.Model, hardware)
|
|
}
|
|
|
|
// TestDevice_SendSoapWithHeader_InjectsHeaderXML verifies that the
|
|
// supplied header XML lands inside the SOAP <Header> element of the
|
|
// outgoing request. AXIS-style WS-Addressing reference parameter
|
|
// echoing depends on this — without it the camera returns
|
|
// ter:InvalidArgs on every PullMessages.
|
|
func TestDevice_SendSoapWithHeader_InjectsHeaderXML(t *testing.T) {
|
|
const headerXML = `<dom0:SubscriptionId xmlns:dom0="http://www.axis.com/2009/event" wsa:IsReferenceParameter="true">297</dom0:SubscriptionId>`
|
|
const bodyXML = `<tev:PullMessages xmlns:tev="http://www.onvif.org/ver10/events/wsdl"><tev:Timeout>PT5S</tev:Timeout><tev:MessageLimit>32</tev:MessageLimit></tev:PullMessages>`
|
|
|
|
var captured string
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
b, _ := io.ReadAll(r.Body)
|
|
captured = string(b)
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
|
|
dev := Device{
|
|
params: DeviceParams{
|
|
Xaddr: strings.TrimPrefix(srv.URL, "http://"),
|
|
HttpClient: srv.Client(),
|
|
},
|
|
}
|
|
resp, err := dev.SendSoapWithHeader(srv.URL, bodyXML, headerXML)
|
|
require.NoError(t, err)
|
|
if resp != nil && resp.Body != nil {
|
|
resp.Body.Close()
|
|
}
|
|
|
|
headerStart := strings.Index(captured, "Header>")
|
|
headerEnd := strings.Index(captured, "</")
|
|
require.NotEqual(t, -1, headerStart, "envelope must contain <Header>; got: %s", captured)
|
|
assert.Greater(t, headerEnd, headerStart, "envelope must close the header")
|
|
|
|
headerSlice := captured[headerStart:strings.Index(captured, "Body>")]
|
|
assert.Contains(t, headerSlice, "SubscriptionId",
|
|
"injected header element must land inside SOAP <Header>")
|
|
assert.Contains(t, headerSlice, "297")
|
|
|
|
bodySlice := captured[strings.Index(captured, "Body>"):]
|
|
assert.Contains(t, bodySlice, "PullMessages",
|
|
"body content must land inside SOAP <Body>")
|
|
}
|