From 4a8d3882759a653952623664566718810d55cd98 Mon Sep 17 00:00:00 2001 From: MURAOKA Taro Date: Sat, 21 Oct 2023 23:20:25 +0900 Subject: [PATCH] replace with optional function pattern --- .gitignore | 2 + advertise.go | 32 +++----------- announce.go | 16 +++++-- examples/advertise/advertise.go | 8 ++-- examples/alive/alive.go | 8 ++-- examples/bye/bye.go | 8 ++-- examples/monitor/monitor.go | 13 +++--- examples/search/search.go | 9 ++-- monitor.go | 8 +++- option.go | 77 +++++++++++++++++++++++++++++++++ search.go | 8 +++- search_test.go | 2 +- ssdp.go | 34 --------------- 13 files changed, 141 insertions(+), 84 deletions(-) create mode 100644 option.go diff --git a/.gitignore b/.gitignore index 24a4254..679e605 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.exe +*~ +default.pgo tags tmp/ diff --git a/advertise.go b/advertise.go index 4f44353..6178e1a 100644 --- a/advertise.go +++ b/advertise.go @@ -39,12 +39,16 @@ type Advertiser struct { // Advertise starts advertisement of service. // location should be a string or a ssdp.LocationProvider. -func Advertise(st, usn string, location interface{}, server string, maxAge int, opts ...AdvertiserOption) (*Advertiser, error) { +func Advertise(st, usn string, location interface{}, server string, maxAge int, opts ...Option) (*Advertiser, error) { locProv, err := toLocationProvider(location) if err != nil { return nil, err } - conn, err := multicast.Listen(multicast.RecvAddrResolver, defaultConnOpts()...) + cfg, err := opts2config(opts) + if err != nil { + return nil, err + } + conn, err := multicast.Listen(multicast.RecvAddrResolver, cfg.multicastConfig.options()...) if err != nil { return nil, err } @@ -57,9 +61,7 @@ func Advertise(st, usn string, location interface{}, server string, maxAge int, maxAge: maxAge, conn: conn, ch: make(chan *message), - } - for _, o := range opts { - o.apply(a) + addHost: cfg.advertiseConfig.addHost, } a.wg.Add(2) a.wgS.Add(1) @@ -202,23 +204,3 @@ func (a *Advertiser) Bye() error { ssdplog.Printf("sent bye") return nil } - -// AdvertiserOption configures for specific behavior of Advertiser. -type AdvertiserOption interface { - apply(*Advertiser) -} - -type advertiserOptionFunc func(*Advertiser) - -func (af advertiserOptionFunc) apply(a *Advertiser) { - af(a) -} - -// AdvertiserOptionAddHost returns as AdvertiserOption that add HOST header -// response for M-SEARCH request. This is added to support SmartThings. -// See https://github.com/koron/go-ssdp/issues/30 for details. -func AdvertiserOptionAddHost() AdvertiserOption { - return advertiserOptionFunc(func(a *Advertiser) { - a.addHost = true - }) -} diff --git a/announce.go b/announce.go index 12890a7..c3c2f9a 100644 --- a/announce.go +++ b/announce.go @@ -10,13 +10,17 @@ import ( // AnnounceAlive sends ssdp:alive message. // location should be a string or a ssdp.LocationProvider. -func AnnounceAlive(nt, usn string, location interface{}, server string, maxAge int, localAddr string) error { +func AnnounceAlive(nt, usn string, location interface{}, server string, maxAge int, localAddr string, opts ...Option) error { locProv, err := toLocationProvider(location) if err != nil { return err } + cfg, err := opts2config(opts) + if err != nil { + return err + } // dial multicast UDP packet. - conn, err := multicast.Listen(&multicast.AddrResolver{Addr: localAddr}, defaultConnOpts()...) + conn, err := multicast.Listen(&multicast.AddrResolver{Addr: localAddr}, cfg.multicastConfig.options()...) if err != nil { return err } @@ -75,9 +79,13 @@ func buildAlive(raddr net.Addr, nt, usn, location, server string, maxAge int) [] } // AnnounceBye sends ssdp:byebye message. -func AnnounceBye(nt, usn, localAddr string) error { +func AnnounceBye(nt, usn, localAddr string, opts...Option) error { + cfg, err := opts2config(opts) + if err != nil { + return err + } // dial multicast UDP packet. - conn, err := multicast.Listen(&multicast.AddrResolver{Addr: localAddr}, defaultConnOpts()...) + conn, err := multicast.Listen(&multicast.AddrResolver{Addr: localAddr}, cfg.multicastConfig.options()...) if err != nil { return err } diff --git a/examples/advertise/advertise.go b/examples/advertise/advertise.go index 328dae9..d53b8ca 100644 --- a/examples/advertise/advertise.go +++ b/examples/advertise/advertise.go @@ -29,14 +29,16 @@ func main() { if *v { ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags) } + + var opts []ssdp.Option if *ttl > 0 { - ssdp.SetMulticastTTL(*ttl) + opts = append(opts, ssdp.TTL(*ttl)) } if *sysIf { - ssdp.SetMulticastSystemAssignedInterface(true) + opts = append(opts, ssdp.OnlySystemInterface()) } - ad, err := ssdp.Advertise(*st, *usn, *loc, *srv, *maxAge) + ad, err := ssdp.Advertise(*st, *usn, *loc, *srv, *maxAge, opts...) if err != nil { log.Fatal(err) } diff --git a/examples/alive/alive.go b/examples/alive/alive.go index da93583..a25b595 100644 --- a/examples/alive/alive.go +++ b/examples/alive/alive.go @@ -28,14 +28,16 @@ func main() { if *v { ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags) } + + var opts []ssdp.Option if *ttl > 0 { - ssdp.SetMulticastTTL(*ttl) + opts = append(opts, ssdp.TTL(*ttl)) } if *sysIf { - ssdp.SetMulticastSystemAssignedInterface(true) + opts = append(opts, ssdp.OnlySystemInterface()) } - err := ssdp.AnnounceAlive(*nt, *usn, *loc, *srv, *maxAge, *laddr) + err := ssdp.AnnounceAlive(*nt, *usn, *loc, *srv, *maxAge, *laddr, opts...) if err != nil { log.Fatal(err) } diff --git a/examples/bye/bye.go b/examples/bye/bye.go index 1063da2..e8a49a5 100644 --- a/examples/bye/bye.go +++ b/examples/bye/bye.go @@ -25,14 +25,16 @@ func main() { if *v { ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags) } + + var opts []ssdp.Option if *ttl > 0 { - ssdp.SetMulticastTTL(*ttl) + opts = append(opts, ssdp.TTL(*ttl)) } if *sysIf { - ssdp.SetMulticastSystemAssignedInterface(true) + opts = append(opts, ssdp.OnlySystemInterface()) } - err := ssdp.AnnounceBye(*nt, *usn, *laddr) + err := ssdp.AnnounceBye(*nt, *usn, *laddr, opts...) if err != nil { log.Fatal(err) } diff --git a/examples/monitor/monitor.go b/examples/monitor/monitor.go index 2997417..9a5d60e 100644 --- a/examples/monitor/monitor.go +++ b/examples/monitor/monitor.go @@ -25,17 +25,20 @@ func main() { if *v { ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags) } + + var opts []ssdp.Option if *ttl > 0 { - ssdp.SetMulticastTTL(*ttl) + opts = append(opts, ssdp.TTL(*ttl)) } if *sysIf { - ssdp.SetMulticastSystemAssignedInterface(true) + opts = append(opts, ssdp.OnlySystemInterface()) } m := &ssdp.Monitor{ - Alive: onAlive, - Bye: onBye, - Search: onSearch, + Alive: onAlive, + Bye: onBye, + Search: onSearch, + Options: opts, } if err := m.Start(); err != nil { log.Fatal(err) diff --git a/examples/search/search.go b/examples/search/search.go index 700e848..6757570 100644 --- a/examples/search/search.go +++ b/examples/search/search.go @@ -25,13 +25,16 @@ func main() { if *v { ssdp.Logger = log.New(os.Stderr, "[SSDP] ", log.LstdFlags) } + + var opts []ssdp.Option if *ttl > 0 { - ssdp.SetMulticastTTL(*ttl) + opts = append(opts, ssdp.TTL(*ttl)) } if *sysIf { - ssdp.SetMulticastSystemAssignedInterface(true) + opts = append(opts, ssdp.OnlySystemInterface()) } - list, err := ssdp.Search(*t, *w, *l) + + list, err := ssdp.Search(*t, *w, *l, opts...) if err != nil { log.Fatal(err) } diff --git a/monitor.go b/monitor.go index 2613bc7..a6803b7 100644 --- a/monitor.go +++ b/monitor.go @@ -20,13 +20,19 @@ type Monitor struct { Bye ByeHandler Search SearchHandler + Options []Option + conn *multicast.Conn wg sync.WaitGroup } // Start starts to monitor SSDP messages. func (m *Monitor) Start() error { - conn, err := multicast.Listen(multicast.RecvAddrResolver, defaultConnOpts()...) + cfg, err := opts2config(m.Options) + if err != nil { + return err + } + conn, err := multicast.Listen(multicast.RecvAddrResolver, cfg.multicastConfig.options()...) if err != nil { return err } diff --git a/option.go b/option.go new file mode 100644 index 0000000..41a5040 --- /dev/null +++ b/option.go @@ -0,0 +1,77 @@ +package ssdp + +import "github.com/koron/go-ssdp/internal/multicast" + +type config struct { + multicastConfig + advertiseConfig +} + +func opts2config(opts []Option) (cfg config, err error) { + for _, o := range opts { + err := o.apply(&cfg) + if err != nil { + return config{}, err + } + } + return cfg, nil +} + +type multicastConfig struct { + ttl int + sysIf bool +} + +func (mc multicastConfig) options() (opts []multicast.ConnOption) { + if mc.ttl > 0 { + opts = append(opts, multicast.ConnTTL(mc.ttl)) + } + if mc.sysIf { + opts = append(opts, multicast.ConnSystemAssginedInterface()) + } + return opts +} + +type advertiseConfig struct { + addHost bool +} + +// Option is option set for SSDP API. +type Option interface { + apply(c *config) error +} + +type optionFunc func(*config) error + +func (of optionFunc) apply(c *config) error { + return of(c) +} + +// TTL returns as Option that set TTL for multicast packets. +func TTL(ttl int) Option { + return optionFunc(func(c *config) error { + c.ttl = ttl + return nil + }) +} + +// OnlySystemInterface returns as Option that using only a system assigned +// multicast interface. +func OnlySystemInterface() Option { + return optionFunc(func(c *config) error { + c.sysIf = true + return nil + }) +} + +// AdvertiseHost returns as Option that add HOST header to response for +// M-SEARCH requests. +// This option works with Advertise() function only. +// This is added to support SmartThings. +// See https://github.com/koron/go-ssdp/issues/30 for details. +func AdvertiseHost() Option { + return optionFunc(func(c *config) error { + c.addHost = true + return nil + }) +} diff --git a/search.go b/search.go index 449f1b8..f13b5ef 100644 --- a/search.go +++ b/search.go @@ -69,9 +69,13 @@ const ( ) // Search searches services by SSDP. -func Search(searchType string, waitSec int, localAddr string) ([]Service, error) { +func Search(searchType string, waitSec int, localAddr string, opts ...Option) ([]Service, error) { + cfg, err := opts2config(opts) + if err != nil { + return nil, err + } // dial multicast UDP packet. - conn, err := multicast.Listen(&multicast.AddrResolver{Addr: localAddr}, defaultConnOpts()...) + conn, err := multicast.Listen(&multicast.AddrResolver{Addr: localAddr}, cfg.multicastConfig.options()...) if err != nil { return nil, err } diff --git a/search_test.go b/search_test.go index e897245..29442dd 100644 --- a/search_test.go +++ b/search_test.go @@ -177,7 +177,7 @@ func TestSearch_ServiceRawHeader(t *testing.T) { } func TestSearch_AdvetiserWithHost(t *testing.T) { - a, err := Advertise("test:search+advertiserwithhost", "usn:search+advertiserwithhost", "location:search+advertiserwithhost", "server:search+advertiserwithhost", 600, AdvertiserOptionAddHost()) + a, err := Advertise("test:search+advertiserwithhost", "usn:search+advertiserwithhost", "location:search+advertiserwithhost", "server:search+advertiserwithhost", 600, AdvertiseHost()) if err != nil { t.Fatalf("failed to Advertise: %s", err) } diff --git a/ssdp.go b/ssdp.go index 0022853..5b875c0 100644 --- a/ssdp.go +++ b/ssdp.go @@ -35,37 +35,3 @@ func SetMulticastRecvAddrIPv4(addr string) error { func SetMulticastSendAddrIPv4(addr string) error { return multicast.SetSendAddrIPv4(addr) } - -func defaultConnOpts() []multicast.ConnOption { - var opts []multicast.ConnOption - if multicastTTL > 0 { - opts = append(opts, multicast.ConnTTL(multicastTTL)) - } - if multicastSystemAssignedInterface { - opts = append(opts, multicast.ConnSystemAssginedInterface()) - } - return opts -} - -var multicastTTL int - -// SetMulticastTTL sets default TTL of SSDP's UDP packets. -// 0 default, 1 or greater set TTL. -func SetMulticastTTL(ttl int) { - multicastTTL = ttl -} - -var multicastSystemAssignedInterface bool - -// SetMulticastSystemAssignedInterface updates state whether using the system -// assigned multicast interface or provided interfaces. -// Default is "false", it uses provided interface (see Interfaces also). -func SetMulticastSystemAssignedInterface(enable bool) { - multicastSystemAssignedInterface = enable -} - -// GetMulticastSystemAssignedInterface returns state using the system assigned -// multicast interface or provided interfaces. -func GetMulticastSystemAssignedInterface() bool { - return multicastSystemAssignedInterface -}