External httpClient support

This commit is contained in:
kikimor
2021-02-24 21:47:09 +05:00
parent 6a2c796805
commit 51ae55e8a0
6 changed files with 47 additions and 89 deletions

View File

@@ -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())
} }

View File

@@ -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)
``` ```

View File

@@ -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
} }

View File

@@ -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{}

View File

@@ -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())

View File

@@ -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))
}