Sebastian Norling 93620f04a3 fix(event/stream): production-grade SOAP and lifecycle hardening
Addresses the five ship-blocker findings from the second review:

1. Bounded body read (review F1 / R-HIGH)
   readClose now wraps resp.Body with io.LimitReader(10 MiB). A
   hostile or buggy camera streaming an unbounded body cannot OOM
   the agent. Legitimate PullMessages payloads are <200KB even with
   dense analytics.

2. SOAP Fault detection (review F3)
   unmarshalNode now scans for SOAP 1.1 faultstring and SOAP 1.2
   Reason/Text BEFORE the missing-element error path. Auth failures
   ('not authorized'), InvalidFilterFault and expired-subscription
   faults now surface their reason text instead of collapsing to
   the unhelpful 'response missing PullMessagesResponse element'.
   This is the difference between a debuggable error and a hidden
   one when a customer's credentials change.

3. Absolute Renew TerminationTime (review F1 wire-correctness)
   renewPullPoint now sends an RFC3339 UTC datetime
   ('2026-05-21T10:30:00Z') instead of a relative xsd:duration
   ('PT60S'). WS-BaseNotification §6.1.1 accepts both, but older
   Hikvision, some Dahua and Bosch firmwares only accept the
   absolute form — the library's own type comment even flags this
   ('BUG(r) Bad AbsoluteOrRelativeTimeType type').

4. Bounded Close (review P0)
   Close now wraps Unsubscribe in a 5s timeout. Previously a
   TCP-accepted-but-never-replying camera would wedge Close
   indefinitely; now Close returns with a timeout error and the
   subscription expires on its own at InitialTermination.

5. Explicit channel-close ordering after wg.Wait
   The run goroutine previously relied on defer-LIFO to guarantee
   renew exits before close(errors). Future maintainers extending
   run() could invert that order silently. Closes are now explicit
   sequential statements after wg.Wait() so the invariant is
   local, not order-of-defers magic.

Also expands wsnt:UtcTime parsing in decode.go to cover the four
formats observed across vendor firmwares: RFC3339 with sub-seconds,
compact offsets ('+0200', Geovision/Dahua), and naked timestamps
without timezone (older Hikvision; per spec UTC is implied).

Caller interface gains a doc comment noting it must be safe for
concurrent use, documenting the contract Stream depends on (*onvif.
Device satisfies it via http.Client).

Tests added: SOAP 1.1 and 1.2 fault extraction, fault surfacing
through unmarshalNode, Renew absolute-datetime assertion,
Close-with-blocked-Unsubscribe returning within the timeout. -race
clean.
2026-05-21 14:54:05 +02:00
2025-01-19 10:19:22 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
add
2023-12-25 21:31:14 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2025-01-19 09:58:53 +01:00
2023-12-18 20:16:21 +01:00
2023-12-18 20:16:21 +01:00
2023-12-25 21:26:55 +01:00
2023-12-18 20:16:21 +01:00
2021-01-31 16:01:46 +08:00
2023-12-25 21:26:55 +01:00
2023-12-18 20:16:21 +01:00

Onvif library

Simple management of onvif IP-devices cameras. onvif is an implementation of ONVIF protocol for managing onvif IP devices. The purpose of this library is convenient and easy management of IP cameras and other devices that support ONVIF standard.

Overview

This repository is forked from: use-go/onvif

Supported services

The following services are implemented:

  • Device
  • Media
  • PTZ
  • Event
  • Discovery

Using

General concept

  1. Connecting to the device
  2. Authentication (if necessary)
  3. Defining Data Types
  4. Carrying out the required method

Connecting to the device

If there is a device on the network at the address 192.168.13.42, and its ONVIF services use the 1234 port, then you can connect to the device in the following way:

dev, err := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234"})

The ONVIF port may differ depending on the device , to find out which port to use, you can go to the web interface of the device. Usually this is 80 port.

Authentication

If any function of the ONVIF services requires authentication, you must use the Authenticate method.

device := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234", Username: "username", Password: password})

Defining Data Types

Each ONVIF service in this library has its own package, in which all data types of this service are defined, and the package name is identical to the service name and begins with a capital letter. onvif defines the structures for each function of each ONVIF service supported by this library. Define the data type of the GetCapabilities function of the Device service. This is done as follows:

capabilities := device.GetCapabilities{Category:"All"}

Why does the GetCapabilities structure have the Category field and why is the value of this field All?

The figure below shows the documentation for the GetCapabilities. It can be seen that the function takes one Category parameter and its value should be one of the following: 'All', 'Analytics',' Device ',' Events', 'Imaging', 'Media' or 'PTZ'`.

Device GetCapabilities

An example of defining the data type of GetServiceCapabilities function in PTZ:

ptzCapabilities := ptz.GetServiceCapabilities{}

The figure below shows that GetServiceCapabilities does not accept any arguments.

PTZ GetServiceCapabilities

Common data types are in the xsd/onvif package. The types of data (structures) that can be shared by all services are defined in the onvif package.

An example of how to define the data type of the CreateUsers function in Devicemgmt:

createUsers := device.CreateUsers{User: onvif.User{Username:"admin", Password:"qwerty", UserLevel:"User"}}

The figure below shows that ,in this example, the CreateUsers structure field must be a User whose data type is the User structure containing the Username, Password, UserLevel, and optional Extension fields. The User structure is in the onvif package.

Device CreateUsers

Carrying out the required method

To perform any function of one of the ONVIF services whose structure has been defined, you must use the CallMethod of the device object.

createUsers := device.CreateUsers{User: onvif.User{Username:"admin", Password:"qwerty", UserLevel:"User"}}
device := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234", Username: "username", Password: password})
device.Authenticate("username", "password")
resp, err := dev.CallMethod(createUsers)

Development

See here

Description
pull-mirror of github.com/kerberos-io/onvif
Readme MIT 2.2 MiB
Languages
Go 99%
Python 0.9%