Summary
UpgradeScheme in NvtSession.fs unconditionally promotes sub-service URLs (media, events, PTZ, etc.) from HTTP to HTTPS whenever the device service connection is HTTPS — even when those URLs were explicitly returned by GetCapabilities / GetServices on a different port. This breaks any camera that intentionally serves its device endpoint over HTTPS but its media endpoint over plain HTTP.
Root Cause
The device service is the authoritative source for sub-service URLs. When GetCapabilities returns http://camera:80/onvif/Media, that is the correct address for the media service — the camera is telling us it is HTTP-only on port 80. UpgradeScheme must not override this.
Current code in NvtSession.fs:
if deviceUri.Scheme = https && url.Scheme = http then
b.Scheme <- https
if b.Port = 80 then b.Port <- 443
b.Uri
This unconditionally upgrades any HTTP sub-service URL to HTTPS when the device URI is HTTPS. If the camera returns http://camera:80/onvif/Media as the media xAddr, it becomes https://camera:443/onvif/Media. The camera only serves /onvif/Media on HTTP:80 — the HTTPS endpoint at :443 returns a TCP RST or CommunicationException.
Reproduction Cases
Case 1 — Milesight camera (192.168.1.190):
- Device service:
https://192.168.1.190:443/onvif/device_service
GetCapabilities returns: XAddr = http://192.168.1.190:80/onvif/Media
UpgradeScheme maps to: https://192.168.1.190:443/onvif/Media
- Result:
GetProfiles → connection refused (port 443 does not serve /onvif/Media)
Integration test failure:
System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 192.168.1.190:443
Case 2 — Field camera 10.102.10.7 (confirmed in production logs):
[UpgradeScheme] deviceUri=https://10.102.10.7/onvif/device_service
input=http://10.102.10.7/onvif/Media → output=https://10.102.10.7/onvif/Media ← WRONG
[withMedia2HttpFallback] non-retryable exception (CommunicationException): propagating
[withMedia1HttpFallback] non-retryable exception (CommunicationException): propagating
Both Media1 and Media2 fail. HTTP fallback also fails because port 80 refuses the upgraded HTTPS connection.
Fix
UpgradeScheme should only upgrade the scheme of a sub-service URL if the sub-service URL's port matches the device service port — meaning the camera misconfigured the scheme but is pointing at the same endpoint. If the ports differ, the camera is intentionally advertising a different endpoint and we must use the URL verbatim.
// Only upgrade if sub-service port matches device service port
// (camera advertised wrong scheme but same endpoint)
if deviceUri.Scheme = https && url.Scheme = http
&& (url.Port = deviceUri.Port || (url.IsDefaultPort && deviceUri.Port = 443)) then
b.Scheme <- https
b.Uri
else
url // trust the URL as returned by GetCapabilities/GetServices
Also fix HttpsIntegrationTests.cs:65 which hardcodes :443 in ServicePointManager.FindServicePoint — should use _httpsPort.
Impact
Cameras with split-scheme deployments (HTTPS device service, HTTP media service) fail completely — CommunicationException on both Media1 and Media2, no streaming possible.
Affects: Milesight cameras (Sprint 6), field cameras 10.102.10.7 and variants.
Summary
UpgradeSchemeinNvtSession.fsunconditionally promotes sub-service URLs (media, events, PTZ, etc.) from HTTP to HTTPS whenever the device service connection is HTTPS — even when those URLs were explicitly returned byGetCapabilities/GetServiceson a different port. This breaks any camera that intentionally serves its device endpoint over HTTPS but its media endpoint over plain HTTP.Root Cause
The device service is the authoritative source for sub-service URLs. When
GetCapabilitiesreturnshttp://camera:80/onvif/Media, that is the correct address for the media service — the camera is telling us it is HTTP-only on port 80.UpgradeSchememust not override this.Current code in
NvtSession.fs:This unconditionally upgrades any HTTP sub-service URL to HTTPS when the device URI is HTTPS. If the camera returns
http://camera:80/onvif/Mediaas the media xAddr, it becomeshttps://camera:443/onvif/Media. The camera only serves/onvif/Mediaon HTTP:80 — the HTTPS endpoint at :443 returns a TCP RST orCommunicationException.Reproduction Cases
Case 1 — Milesight camera (192.168.1.190):
https://192.168.1.190:443/onvif/device_serviceGetCapabilitiesreturns:XAddr = http://192.168.1.190:80/onvif/MediaUpgradeSchememaps to:https://192.168.1.190:443/onvif/MediaGetProfiles→ connection refused (port 443 does not serve/onvif/Media)Integration test failure:
Case 2 — Field camera 10.102.10.7 (confirmed in production logs):
Both Media1 and Media2 fail. HTTP fallback also fails because port 80 refuses the upgraded HTTPS connection.
Fix
UpgradeSchemeshould only upgrade the scheme of a sub-service URL if the sub-service URL's port matches the device service port — meaning the camera misconfigured the scheme but is pointing at the same endpoint. If the ports differ, the camera is intentionally advertising a different endpoint and we must use the URL verbatim.Also fix
HttpsIntegrationTests.cs:65which hardcodes:443inServicePointManager.FindServicePoint— should use_httpsPort.Impact
Cameras with split-scheme deployments (HTTPS device service, HTTP media service) fail completely —
CommunicationExceptionon both Media1 and Media2, no streaming possible.Affects: Milesight cameras (Sprint 6), field cameras 10.102.10.7 and variants.