mirror of
https://github.com/kerberos-io/onvif.git
synced 2026-08-23 15:08:33 +00:00
Merge pull request #12 from kerberos-io/feature/enhance-ptz-failure-logging
feature/enhance-ptz-failure-logging
This commit is contained in:
@@ -1,10 +1,57 @@
|
||||
package networking
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSendSoapWithDigestReturnsServerError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) {
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
response, err := SendSoapWithDigest(server.Client(), server.URL, "<Envelope/>", "user", "password")
|
||||
if response == nil {
|
||||
t.Fatal("SendSoapWithDigest returned a nil response")
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if err == nil {
|
||||
t.Fatal("SendSoapWithDigest returned nil error for HTTP 500")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendSoapWithDigestReturnsServerErrorAfterAuthentication(t *testing.T) {
|
||||
requestCount := 0
|
||||
server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
||||
requestCount++
|
||||
if requestCount == 1 {
|
||||
writer.Header().Set("WWW-Authenticate", `Digest realm="AXIS", nonce="nonce", qop="auth", algorithm=MD5`)
|
||||
writer.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if !strings.HasPrefix(request.Header.Get("Authorization"), "Digest ") {
|
||||
t.Error("authenticated retry is missing Digest Authorization header")
|
||||
}
|
||||
writer.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
response, err := SendSoapWithDigest(server.Client(), server.URL, "<Envelope/>", "user", "password")
|
||||
if response == nil {
|
||||
t.Fatal("SendSoapWithDigest returned a nil response")
|
||||
}
|
||||
defer response.Body.Close()
|
||||
if err == nil {
|
||||
t.Fatal("SendSoapWithDigest returned nil error for authenticated HTTP 500")
|
||||
}
|
||||
if requestCount != 2 {
|
||||
t.Fatalf("request count = %d, want 2", requestCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseDigestChallenge(t *testing.T) {
|
||||
challenge := `Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41", algorithm=MD5`
|
||||
parts := parseDigestChallenge(challenge)
|
||||
|
||||
@@ -24,12 +24,14 @@ func SendSoap(httpClient *http.Client, endpoint, message string) (*http.Response
|
||||
return resp, errors.Annotate(err, "Post")
|
||||
}
|
||||
|
||||
// if resp.StatusCode is 4xx,5xx, return error
|
||||
if resp.StatusCode >= 400 && resp.StatusCode < 600 {
|
||||
return resp, errors.Errorf("Server error: %d: %s", resp.StatusCode, resp.Status)
|
||||
}
|
||||
return resp, responseError(resp)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
func responseError(resp *http.Response) error {
|
||||
if resp.StatusCode >= 400 && resp.StatusCode < 600 {
|
||||
return errors.Errorf("Server error: %d: %s", resp.StatusCode, resp.Status)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendSoapWithDigest sends a soap message and, when the device answers with an
|
||||
@@ -57,13 +59,13 @@ func SendSoapWithDigest(httpClient *http.Client, endpoint, message, username, pa
|
||||
|
||||
// Only escalate to HTTP digest when the device explicitly asks for it.
|
||||
if resp.StatusCode != http.StatusUnauthorized {
|
||||
return resp, nil
|
||||
return resp, responseError(resp)
|
||||
}
|
||||
|
||||
challenge := resp.Header.Get("WWW-Authenticate")
|
||||
if !strings.HasPrefix(strings.ToLower(strings.TrimSpace(challenge)), "digest") {
|
||||
// Not a digest challenge (e.g. Basic) - nothing more we can do here.
|
||||
return resp, nil
|
||||
return resp, responseError(resp)
|
||||
}
|
||||
|
||||
authorization := newDigestAuthorization(challenge, http.MethodPost, endpoint, username, password)
|
||||
@@ -86,7 +88,7 @@ func SendSoapWithDigest(httpClient *http.Client, endpoint, message, username, pa
|
||||
return resp, errors.Annotate(err, "Post with digest")
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
return resp, responseError(resp)
|
||||
}
|
||||
|
||||
// stripWSSecurityHeader removes the wsse:Security header block from a SOAP
|
||||
|
||||
25
ptz/types_test.go
Normal file
25
ptz/types_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
package ptz
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/kerberos-io/onvif/xsd/onvif"
|
||||
)
|
||||
|
||||
func TestContinuousMoveIncludesZeroPanTiltCoordinates(t *testing.T) {
|
||||
request := ContinuousMove{
|
||||
Velocity: onvif.PTZSpeedPanTilt{
|
||||
PanTilt: onvif.Vector2D{X: 0.5, Y: 0},
|
||||
},
|
||||
}
|
||||
|
||||
encoded, err := xml.Marshal(request)
|
||||
if err != nil {
|
||||
t.Fatalf("xml.Marshal() error = %v", err)
|
||||
}
|
||||
if !strings.Contains(string(encoded), `x="0.5" y="0"`) {
|
||||
t.Fatalf("ContinuousMove PanTilt = %s, want explicit x and y attributes", encoded)
|
||||
}
|
||||
}
|
||||
@@ -666,13 +666,13 @@ type PTZSpeedPanTilt struct {
|
||||
}
|
||||
|
||||
type Vector2D struct {
|
||||
X float64 `xml:"x,attr,omitempty"`
|
||||
Y float64 `xml:"y,attr,omitempty"`
|
||||
X float64 `xml:"x,attr"`
|
||||
Y float64 `xml:"y,attr"`
|
||||
Space *xsd.AnyURI `xml:"space,attr,omitempty"`
|
||||
}
|
||||
|
||||
type Vector1D struct {
|
||||
X float64 `xml:"x,attr,omitempty"`
|
||||
X float64 `xml:"x,attr"`
|
||||
Space *xsd.AnyURI `xml:"space,attr,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1176,7 +1176,7 @@ type PresetTour struct {
|
||||
Status PTZPresetTourStatus `xml:"Status"`
|
||||
AutoStart xsd.Boolean `xml:"AutoStart"`
|
||||
StartingCondition PTZPresetTourStartingCondition `xml:"StartingCondition"`
|
||||
TourSpot []PTZPresetTourSpot `xml:"TourSpot"`
|
||||
TourSpot []PTZPresetTourSpot `xml:"TourSpot"`
|
||||
Extension PTZPresetTourExtension `xml:"Extension"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user