fix onvif client

This commit is contained in:
seydx
2025-02-10 20:21:25 +01:00
parent ad61662cc4
commit d0ac99fc69
+68 -14
View File
@@ -3,9 +3,10 @@ package onvif
import ( import (
"bytes" "bytes"
"errors" "errors"
"fmt"
"html" "html"
"io" "io"
"net/http" "net"
"net/url" "net/url"
"regexp" "regexp"
"strings" "strings"
@@ -37,13 +38,22 @@ func NewClient(rawURL string) (*Client, error) {
client.deviceURL = baseURL + u.Path client.deviceURL = baseURL + u.Path
} }
// Set default media URL before trying to get capabilities
client.mediaURL = baseURL + "/onvif/media_service"
client.imaginURL = baseURL + "/onvif/imaging_service"
b, err := client.DeviceRequest(DeviceGetCapabilities) b, err := client.DeviceRequest(DeviceGetCapabilities)
if err != nil { if err != nil {
return nil, err return nil, err
} }
client.mediaURL = FindTagValue(b, "Media.+?XAddr") // Update URLs if found in capabilities
client.imaginURL = FindTagValue(b, "Imaging.+?XAddr") if mediaAddr := FindTagValue(b, "Media.+?XAddr"); mediaAddr != "" {
client.mediaURL = mediaAddr
}
if imagingAddr := FindTagValue(b, "Imaging.+?XAddr"); imagingAddr != "" {
client.imaginURL = imagingAddr
}
return client, nil return client, nil
} }
@@ -172,26 +182,70 @@ func (c *Client) MediaRequest(operation string) ([]byte, error) {
return c.Request(c.mediaURL, operation) return c.Request(c.mediaURL, operation)
} }
func (c *Client) Request(url, body string) ([]byte, error) { func (c *Client) Request(rawUrl, body string) ([]byte, error) {
if url == "" { if rawUrl == "" {
return nil, errors.New("onvif: unsupported service") return nil, errors.New("onvif: unsupported service")
} }
e := NewEnvelopeWithUser(c.url.User) e := NewEnvelopeWithUser(c.url.User)
e.Append(body) e.Append(body)
client := &http.Client{Timeout: time.Second * 5000} u, err := url.Parse(rawUrl)
res, err := client.Post(url, `application/soap+xml;charset=utf-8`, bytes.NewReader(e.Bytes()))
if err != nil { if err != nil {
return nil, err return nil, err
} }
// need to close body with eny response status // Ensure we have a port
b, err := io.ReadAll(res.Body) host := u.Host
if !strings.Contains(host, ":") {
if err == nil && res.StatusCode != http.StatusOK { host = host + ":80"
err = errors.New("onvif: " + res.Status + " for " + url)
} }
return b, err // Connect with timeout
} conn, err := net.DialTimeout("tcp", host, 5*time.Second)
if err != nil {
return nil, err
}
defer conn.Close()
// Send request
httpReq := fmt.Sprintf("POST %s HTTP/1.1\r\n"+
"Host: %s\r\n"+
"Content-Type: application/soap+xml;charset=utf-8\r\n"+
"Content-Length: %d\r\n"+
"Connection: close\r\n"+
"\r\n%s", u.Path, u.Host, len(e.Bytes()), e.Bytes())
if _, err = conn.Write([]byte(httpReq)); err != nil {
return nil, err
}
// Read full response first
var fullResponse []byte
buf := make([]byte, 4096)
for {
n, err := conn.Read(buf)
if n > 0 {
fullResponse = append(fullResponse, buf[:n]...)
}
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
}
// Look for XML in complete response
if idx := bytes.Index(fullResponse, []byte("<?xml")); idx >= 0 {
return fullResponse[idx:], nil
}
// No XML found - might be an error response
if idx := bytes.Index(fullResponse, []byte("\r\n\r\n")); idx >= 0 {
// Return body after headers
return fullResponse[idx+4:], nil
}
return fullResponse, nil
}