|
@@ -8,6 +8,7 @@ import (
|
|
|
"runtime"
|
|
"runtime"
|
|
|
"strconv"
|
|
"strconv"
|
|
|
"strings"
|
|
"strings"
|
|
|
|
|
+ "sync"
|
|
|
"time"
|
|
"time"
|
|
|
|
|
|
|
|
ssdp "github.com/koron/go-ssdp"
|
|
ssdp "github.com/koron/go-ssdp"
|
|
@@ -31,7 +32,10 @@ type SSDPHost struct {
|
|
|
advStarted bool
|
|
advStarted bool
|
|
|
SSDPTemplateFile string
|
|
SSDPTemplateFile string
|
|
|
Option *SSDPOption
|
|
Option *SSDPOption
|
|
|
|
|
+ aliveInterval time.Duration
|
|
|
quit chan bool
|
|
quit chan bool
|
|
|
|
|
+ done chan struct{}
|
|
|
|
|
+ closeOnce sync.Once
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func NewSSDPHost(outboundIP string, port int, templateFile string, option SSDPOption) (*SSDPHost, error) {
|
|
func NewSSDPHost(outboundIP string, port int, templateFile string, option SSDPOption) (*SSDPHost, error) {
|
|
@@ -65,6 +69,7 @@ func NewSSDPHost(outboundIP string, port int, templateFile string, option SSDPOp
|
|
|
return &SSDPHost{
|
|
return &SSDPHost{
|
|
|
ADV: ad,
|
|
ADV: ad,
|
|
|
advStarted: false,
|
|
advStarted: false,
|
|
|
|
|
+ aliveInterval: 5 * time.Second,
|
|
|
SSDPTemplateFile: templateFile,
|
|
SSDPTemplateFile: templateFile,
|
|
|
Option: &option,
|
|
Option: &option,
|
|
|
}, nil
|
|
}, nil
|
|
@@ -74,36 +79,46 @@ func (a *SSDPHost) Start() {
|
|
|
//Advertise ssdp
|
|
//Advertise ssdp
|
|
|
http.HandleFunc("/ssdp.xml", a.handleSSDP)
|
|
http.HandleFunc("/ssdp.xml", a.handleSSDP)
|
|
|
logger.PrintAndLog("Ssdp", "Starting SSDP Discovery Service: "+a.Option.URLBase, nil)
|
|
logger.PrintAndLog("Ssdp", "Starting SSDP Discovery Service: "+a.Option.URLBase, nil)
|
|
|
- var aliveTick <-chan time.Time
|
|
|
|
|
- aliveTick = time.Tick(time.Duration(5) * time.Second)
|
|
|
|
|
|
|
|
|
|
- quit := make(chan bool)
|
|
|
|
|
- a.quit = quit
|
|
|
|
|
|
|
+ a.quit = make(chan bool)
|
|
|
|
|
+ a.done = make(chan struct{})
|
|
|
a.advStarted = true
|
|
a.advStarted = true
|
|
|
- go func(ad *ssdp.Advertiser) {
|
|
|
|
|
|
|
+ go func(ad *ssdp.Advertiser, quit chan bool, done chan struct{}) {
|
|
|
|
|
+ defer close(done)
|
|
|
|
|
+ aliveTicker := time.NewTicker(a.aliveInterval)
|
|
|
|
|
+ defer aliveTicker.Stop()
|
|
|
for {
|
|
for {
|
|
|
select {
|
|
select {
|
|
|
- case <-aliveTick:
|
|
|
|
|
|
|
+ case <-aliveTicker.C:
|
|
|
if ad != nil {
|
|
if ad != nil {
|
|
|
ad.Alive()
|
|
ad.Alive()
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
case <-quit:
|
|
case <-quit:
|
|
|
- ad.Bye()
|
|
|
|
|
- ad.Close()
|
|
|
|
|
- break
|
|
|
|
|
|
|
+ //Return (not break) so no Alive() can run after the advertiser is closed
|
|
|
|
|
+ if ad != nil {
|
|
|
|
|
+ ad.Bye()
|
|
|
|
|
+ ad.Close()
|
|
|
|
|
+ }
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- }(a.ADV)
|
|
|
|
|
|
|
+ }(a.ADV, a.quit, a.done)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// Close sends the SSDP byebye message and stops the advertiser. It waits for
|
|
|
|
|
+// the alive loop to exit and is safe to call more than once.
|
|
|
func (a *SSDPHost) Close() {
|
|
func (a *SSDPHost) Close() {
|
|
|
- if a != nil {
|
|
|
|
|
|
|
+ if a == nil {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ a.closeOnce.Do(func() {
|
|
|
if a.advStarted {
|
|
if a.advStarted {
|
|
|
- a.quit <- true
|
|
|
|
|
|
|
+ close(a.quit)
|
|
|
|
|
+ <-a.done
|
|
|
|
|
+ } else if a.ADV != nil {
|
|
|
|
|
+ a.ADV.Close()
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Serve the xml file with the given properties
|
|
// Serve the xml file with the given properties
|