feat(stream): add NAT-PMP port mapping for remote downloads
Replace anacrolix/upnp with huin/goupnp + custom NAT-PMP (RFC 6886) implementation. NAT-PMP is tried first (faster, more compatible with TP-Link routers), with UPnP-IGD SOAP as fallback. Gateway detection reads /proc/net/route for accuracy. Includes unit tests with mock NAT-PMP server and permanent e2e tests (build tag manual).
This commit is contained in:
parent
819c727bf5
commit
aa6acbabc9
8 changed files with 1030 additions and 24 deletions
|
|
@ -32,6 +32,7 @@ type StreamServer struct {
|
|||
port int
|
||||
url string
|
||||
upnpMapping *UPnPMapping
|
||||
disableUPnP bool // for testing
|
||||
lastActivity atomic.Int64 // UnixNano of last HTTP request
|
||||
maxByteOffset atomic.Int64 // highest byte offset served (for watch progress estimation)
|
||||
totalFileSize int64 // total file size in bytes (set on Start)
|
||||
|
|
@ -154,8 +155,20 @@ func (ss *StreamServer) Start(ctx context.Context) (string, error) {
|
|||
}
|
||||
|
||||
ss.port = listener.Addr().(*net.TCPAddr).Port
|
||||
ss.url = fmt.Sprintf("http://%s:%d/stream", reachableIP(), ss.port)
|
||||
log.Printf("stream: serving on %s", ss.url)
|
||||
|
||||
// Try UPnP/NAT-PMP for public internet access (remote downloads)
|
||||
if !ss.disableUPnP {
|
||||
if mapping, err := SetupUPnP(ss.port); err == nil {
|
||||
ss.upnpMapping = mapping
|
||||
ss.url = fmt.Sprintf("http://%s:%d/stream", mapping.ExternalIP, mapping.ExternalPort)
|
||||
log.Printf("stream: UPnP success — public URL: %s", ss.url)
|
||||
} else {
|
||||
log.Printf("stream: UPnP unavailable (%v), falling back to LAN", err)
|
||||
ss.url = fmt.Sprintf("http://%s:%d/stream", reachableIP(), ss.port)
|
||||
}
|
||||
} else {
|
||||
ss.url = fmt.Sprintf("http://%s:%d/stream", reachableIP(), ss.port)
|
||||
}
|
||||
|
||||
ss.server = &http.Server{
|
||||
Handler: mux,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue