mirror of
https://github.com/kerberos-io/onvif.git
synced 2025-10-05 15:56:50 +08:00
External httpClient support
This commit is contained in:
99
Device.go
99
Device.go
@@ -76,13 +76,18 @@ type DeviceInfo struct {
|
|||||||
//struct represents an abstract ONVIF device.
|
//struct represents an abstract ONVIF device.
|
||||||
//It contains methods, which helps to communicate with ONVIF device
|
//It contains methods, which helps to communicate with ONVIF device
|
||||||
type Device struct {
|
type Device struct {
|
||||||
xaddr string
|
params DeviceParams
|
||||||
login string
|
|
||||||
password string
|
|
||||||
endpoints map[string]string
|
endpoints map[string]string
|
||||||
info DeviceInfo
|
info DeviceInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DeviceParams struct {
|
||||||
|
Xaddr string
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
HttpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
//GetServices return available endpoints
|
//GetServices return available endpoints
|
||||||
func (dev *Device) GetServices() map[string]string {
|
func (dev *Device) GetServices() map[string]string {
|
||||||
return dev.endpoints
|
return dev.endpoints
|
||||||
@@ -93,7 +98,6 @@ func (dev *Device) GetDeviceInfo() DeviceInfo {
|
|||||||
return dev.info
|
return dev.info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func readResponse(resp *http.Response) string {
|
func readResponse(resp *http.Response) string {
|
||||||
b, err := ioutil.ReadAll(resp.Body)
|
b, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -109,52 +113,47 @@ func GetAvailableDevicesAtSpecificEthernetInterface(interfaceName string) []Devi
|
|||||||
*/
|
*/
|
||||||
devices := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:" + NVT.String()}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"})
|
devices := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:" + NVT.String()}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"})
|
||||||
nvtDevices := make([]Device, 0)
|
nvtDevices := make([]Device, 0)
|
||||||
////fmt.Println(devices)
|
|
||||||
for _, j := range devices {
|
for _, j := range devices {
|
||||||
doc := etree.NewDocument()
|
doc := etree.NewDocument()
|
||||||
if err := doc.ReadFromString(j); err != nil {
|
if err := doc.ReadFromString(j); err != nil {
|
||||||
fmt.Errorf("%s", err.Error())
|
fmt.Errorf("%s", err.Error())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
////fmt.Println(j)
|
|
||||||
endpoints := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs")
|
endpoints := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs")
|
||||||
for _, xaddr := range endpoints {
|
for _, xaddr := range endpoints {
|
||||||
//fmt.Println(xaddr.Tag,strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2] )
|
|
||||||
xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2]
|
xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2]
|
||||||
fmt.Println(xaddr)
|
fmt.Println(xaddr)
|
||||||
c := 0
|
c := 0
|
||||||
|
|
||||||
for c = 0; c < len(nvtDevices); c++ {
|
for c = 0; c < len(nvtDevices); c++ {
|
||||||
if nvtDevices[c].xaddr == xaddr {
|
if nvtDevices[c].params.Xaddr == xaddr {
|
||||||
fmt.Println(nvtDevices[c].xaddr, "==", xaddr)
|
fmt.Println(nvtDevices[c].params.Xaddr, "==", xaddr)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if c < len(nvtDevices) {
|
if c < len(nvtDevices) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
dev, err := NewDevice(strings.Split(xaddr, " ")[0])
|
|
||||||
//fmt.Println(dev)
|
dev, err := NewDevice(DeviceParams{Xaddr: strings.Split(xaddr, " ")[0]})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error", xaddr)
|
fmt.Println("Error", xaddr)
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
////fmt.Println(dev)
|
|
||||||
nvtDevices = append(nvtDevices, *dev)
|
nvtDevices = append(nvtDevices, *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
////fmt.Println(j)
|
|
||||||
//nvtDevices[i] = NewDevice()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nvtDevices
|
return nvtDevices
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dev *Device) getSupportedServices(resp *http.Response) {
|
func (dev *Device) getSupportedServices(resp *http.Response) {
|
||||||
//resp, err := dev.CallMethod(device.GetCapabilities{Category:"All"})
|
|
||||||
//if err != nil {
|
|
||||||
// log.Println(err.Error())
|
|
||||||
//return
|
|
||||||
//} else {
|
|
||||||
doc := etree.NewDocument()
|
doc := etree.NewDocument()
|
||||||
|
|
||||||
data, _ := ioutil.ReadAll(resp.Body)
|
data, _ := ioutil.ReadAll(resp.Body)
|
||||||
@@ -165,28 +164,27 @@ func (dev *Device) getSupportedServices(resp *http.Response) {
|
|||||||
}
|
}
|
||||||
services := doc.FindElements("./Envelope/Body/GetCapabilitiesResponse/Capabilities/*/XAddr")
|
services := doc.FindElements("./Envelope/Body/GetCapabilitiesResponse/Capabilities/*/XAddr")
|
||||||
for _, j := range services {
|
for _, j := range services {
|
||||||
////fmt.Println(j.Text())
|
|
||||||
////fmt.Println(j.Parent().Tag)
|
|
||||||
dev.addEndpoint(j.Parent().Tag, j.Text())
|
dev.addEndpoint(j.Parent().Tag, j.Text())
|
||||||
}
|
}
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//NewDevice function construct a ONVIF Device entity
|
//NewDevice function construct a ONVIF Device entity
|
||||||
func NewDevice(xaddr string) (*Device, error) {
|
func NewDevice(params DeviceParams) (*Device, error) {
|
||||||
dev := new(Device)
|
dev := new(Device)
|
||||||
dev.xaddr = xaddr
|
dev.params = params
|
||||||
dev.endpoints = make(map[string]string)
|
dev.endpoints = make(map[string]string)
|
||||||
dev.addEndpoint("Device", "http://"+xaddr+"/onvif/device_service")
|
dev.addEndpoint("Device", "http://"+dev.params.Xaddr+"/onvif/device_service")
|
||||||
|
|
||||||
|
if dev.params.HttpClient == nil {
|
||||||
|
dev.params.HttpClient = new(http.Client)
|
||||||
|
}
|
||||||
|
|
||||||
getCapabilities := device.GetCapabilities{Category: "All"}
|
getCapabilities := device.GetCapabilities{Category: "All"}
|
||||||
|
|
||||||
resp, err := dev.CallMethod(getCapabilities)
|
resp, err := dev.CallMethod(getCapabilities)
|
||||||
//fmt.Println(resp.Request.Host)
|
|
||||||
//fmt.Println(readResponse(resp))
|
|
||||||
if err != nil || resp.StatusCode != http.StatusOK {
|
if err != nil || resp.StatusCode != http.StatusOK {
|
||||||
//panic(errors.New("camera is not available at " + xaddr + " or it does not support ONVIF services"))
|
return nil, errors.New("camera is not available at " + dev.params.Xaddr + " or it does not support ONVIF services")
|
||||||
return nil, errors.New("camera is not available at " + xaddr + " or it does not support ONVIF services")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dev.getSupportedServices(resp)
|
dev.getSupportedServices(resp)
|
||||||
@@ -194,28 +192,18 @@ func NewDevice(xaddr string) (*Device, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dev *Device) addEndpoint(Key, Value string) {
|
func (dev *Device) addEndpoint(Key, Value string) {
|
||||||
|
|
||||||
//use lowCaseKey
|
//use lowCaseKey
|
||||||
//make key having ability to handle Mixed Case for Different vendor devcie (e.g. Events EVENTS, events)
|
//make key having ability to handle Mixed Case for Different vendor devcie (e.g. Events EVENTS, events)
|
||||||
lowCaseKey := strings.ToLower(Key)
|
lowCaseKey := strings.ToLower(Key)
|
||||||
dev.endpoints[lowCaseKey] = Value
|
dev.endpoints[lowCaseKey] = Value
|
||||||
}
|
}
|
||||||
|
|
||||||
//Authenticate function authenticate client in the ONVIF device.
|
|
||||||
//Function takes <username> and <password> params.
|
|
||||||
//You should use this function to allow authorized requests to the ONVIF Device
|
|
||||||
//To change auth data call this function again.
|
|
||||||
func (dev *Device) Authenticate(username, password string) {
|
|
||||||
dev.login = username
|
|
||||||
dev.password = password
|
|
||||||
}
|
|
||||||
|
|
||||||
//GetEndpoint returns specific ONVIF service endpoint address
|
//GetEndpoint returns specific ONVIF service endpoint address
|
||||||
func (dev *Device) GetEndpoint(name string) string {
|
func (dev *Device) GetEndpoint(name string) string {
|
||||||
return dev.endpoints[name]
|
return dev.endpoints[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildMethodSOAP(msg string) (gosoap.SoapMessage, error) {
|
func (dev Device) buildMethodSOAP(msg string) (gosoap.SoapMessage, error) {
|
||||||
doc := etree.NewDocument()
|
doc := etree.NewDocument()
|
||||||
if err := doc.ReadFromString(msg); err != nil {
|
if err := doc.ReadFromString(msg); err != nil {
|
||||||
//log.Println("Got error")
|
//log.Println("Got error")
|
||||||
@@ -226,7 +214,6 @@ func buildMethodSOAP(msg string) (gosoap.SoapMessage, error) {
|
|||||||
|
|
||||||
soap := gosoap.NewEmptySOAP()
|
soap := gosoap.NewEmptySOAP()
|
||||||
soap.AddBodyContent(element)
|
soap.AddBodyContent(element)
|
||||||
//soap.AddRootNamespace("onvif", "http://www.onvif.org/ver10/device/wsdl")
|
|
||||||
|
|
||||||
return soap, nil
|
return soap, nil
|
||||||
}
|
}
|
||||||
@@ -267,41 +254,23 @@ func (dev Device) CallMethod(method interface{}) (*http.Response, error) {
|
|||||||
|
|
||||||
//CallMethod functions call an method, defined <method> struct with authentication data
|
//CallMethod functions call an method, defined <method> struct with authentication data
|
||||||
func (dev Device) callMethodDo(endpoint string, method interface{}) (*http.Response, error) {
|
func (dev Device) callMethodDo(endpoint string, method interface{}) (*http.Response, error) {
|
||||||
/*
|
|
||||||
Converting <method> struct to xml string representation
|
|
||||||
*/
|
|
||||||
output, err := xml.MarshalIndent(method, " ", " ")
|
output, err := xml.MarshalIndent(method, " ", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//log.Printf("error: %v\n", err.Error())
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
//fmt.Println(gosoap.SoapMessage(string(output)).StringIndent())
|
|
||||||
/*
|
soap, err := dev.buildMethodSOAP(string(output))
|
||||||
Build an SOAP request with <method>
|
|
||||||
*/
|
|
||||||
soap, err := buildMethodSOAP(string(output))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//log.Printf("error: %v\n", err.Error())
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Println(soap.StringIndent())
|
|
||||||
/*
|
|
||||||
Adding namespaces and WS-Security headers
|
|
||||||
*/
|
|
||||||
soap.AddRootNamespaces(Xlmns)
|
soap.AddRootNamespaces(Xlmns)
|
||||||
|
|
||||||
//fmt.Println(soap.StringIndent())
|
|
||||||
//Header handling
|
|
||||||
soap.AddAction()
|
soap.AddAction()
|
||||||
|
|
||||||
//Auth Handling
|
//Auth Handling
|
||||||
if dev.login != "" && dev.password != "" {
|
if dev.params.Username != "" && dev.params.Password != "" {
|
||||||
soap.AddWSSecurity(dev.login, dev.password)
|
soap.AddWSSecurity(dev.params.Username, dev.params.Password)
|
||||||
}
|
}
|
||||||
//fmt.Println(soap.StringIndent())
|
|
||||||
/*
|
return networking.SendSoap(dev.params.HttpClient, endpoint, soap.String())
|
||||||
Sending request and returns the response
|
|
||||||
*/
|
|
||||||
return networking.SendSoap(endpoint, soap.String())
|
|
||||||
}
|
}
|
||||||
|
@@ -38,7 +38,7 @@ The following services are implemented:
|
|||||||
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:
|
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:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
dev, err := onvif.NewDevice("192.168.13.42:1234")
|
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.***
|
*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.***
|
||||||
@@ -48,8 +48,7 @@ dev, err := onvif.NewDevice("192.168.13.42:1234")
|
|||||||
If any function of the ONVIF services requires authentication, you must use the `Authenticate` method.
|
If any function of the ONVIF services requires authentication, you must use the `Authenticate` method.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
device := onvif.NewDevice("192.168.13.42:1234")
|
device := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234", Username: "username", Password: password})
|
||||||
device.Authenticate("username", "password")
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Defining Data Types
|
#### Defining Data Types
|
||||||
@@ -94,7 +93,7 @@ To perform any function of one of the ONVIF services whose structure has been de
|
|||||||
|
|
||||||
```go
|
```go
|
||||||
createUsers := device.CreateUsers{User: onvif.User{Username:"admin", Password:"qwerty", UserLevel:"User"}}
|
createUsers := device.CreateUsers{User: onvif.User{Username:"admin", Password:"qwerty", UserLevel:"User"}}
|
||||||
device := onvif.NewDevice("192.168.13.42:1234")
|
device := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234", Username: "username", Password: password})
|
||||||
device.Authenticate("username", "password")
|
device.Authenticate("username", "password")
|
||||||
resp, err := dev.CallMethod(createUsers)
|
resp, err := dev.CallMethod(createUsers)
|
||||||
```
|
```
|
||||||
|
@@ -147,7 +147,7 @@ func callNecessaryMethod(serviceName, methodName, acceptedData, username, passwo
|
|||||||
soap.AddRootNamespaces(onvif.Xlmns)
|
soap.AddRootNamespaces(onvif.Xlmns)
|
||||||
soap.AddWSSecurity(username, password)
|
soap.AddWSSecurity(username, password)
|
||||||
|
|
||||||
servResp, err := networking.SendSoap(endpoint, soap.String())
|
servResp, err := networking.SendSoap(new(http.Client), endpoint, soap.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ func callNecessaryMethod(serviceName, methodName, acceptedData, username, passwo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getEndpoint(service, xaddr string) (string, error) {
|
func getEndpoint(service, xaddr string) (string, error) {
|
||||||
dev, err := onvif.NewDevice(xaddr)
|
dev, err := onvif.NewDevice(onvif.DeviceParams{Xaddr: xaddr})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@@ -27,12 +27,15 @@ func readResponse(resp *http.Response) string {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
//Getting an camera instance
|
//Getting an camera instance
|
||||||
dev, err := goonvif.NewDevice("192.168.13.14:80")
|
dev, err := goonvif.NewDevice(goonvif.DeviceParams{
|
||||||
|
Xaddr: "192.168.13.14:80",
|
||||||
|
Username: login,
|
||||||
|
Password: password,
|
||||||
|
HttpClient: new(http.Client),
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
//Authorization
|
|
||||||
dev.Authenticate(login, password)
|
|
||||||
|
|
||||||
//Preparing commands
|
//Preparing commands
|
||||||
systemDateAndTyme := device.GetSystemDateAndTime{}
|
systemDateAndTyme := device.GetSystemDateAndTime{}
|
||||||
|
@@ -25,11 +25,10 @@ func TestGetAvailableDevicesAtSpecificEthernetInterface(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func client() {
|
func client() {
|
||||||
dev, err := onvif.NewDevice("192.168.3.10")
|
dev, err := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.3.10", Username: "admin", Password: "zsyy12345"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
dev.Authenticate("admin", "zsyy12345")
|
|
||||||
|
|
||||||
log.Printf("output %+v", dev.GetServices())
|
log.Printf("output %+v", dev.GetServices())
|
||||||
|
|
||||||
|
@@ -3,13 +3,10 @@ package networking
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SendSoap send soap message
|
// SendSoap send soap message
|
||||||
func SendSoap(endpoint, message string) (*http.Response, error) {
|
func SendSoap(httpClient *http.Client, endpoint, message string) (*http.Response, error) {
|
||||||
httpClient := new(http.Client)
|
|
||||||
|
|
||||||
resp, err := httpClient.Post(endpoint, "application/soap+xml; charset=utf-8", bytes.NewBufferString(message))
|
resp, err := httpClient.Post(endpoint, "application/soap+xml; charset=utf-8", bytes.NewBufferString(message))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return resp, err
|
return resp, err
|
||||||
@@ -17,12 +14,3 @@ func SendSoap(endpoint, message string) (*http.Response, error) {
|
|||||||
|
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendSoapWithTimeout send soap message with timeOut
|
|
||||||
func SendSoapWithTimeout(endpoint string, message []byte, timeout time.Duration) (*http.Response, error) {
|
|
||||||
httpClient := &http.Client{
|
|
||||||
Timeout: timeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
return httpClient.Post(endpoint, "application/soap+xml; charset=utf-8", bytes.NewReader(message))
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user