Comments (4)
I have sketched some changes here, to see.
gupnp.localIPv4MCastAddrs
is made publicssdp.SSDPRawSearch
is modified to de duplicate answers using the client local address as additional uniq identifier parameter. It receives an additional parameterclient httpu.ClientInterface
.httpu.HTTPUClient
is modified to add a new FieldAddr string
goupnp.DiscoverDevicesCtx
and others, are modified to take in aclient httpu.ClientInterface
httpu.HTTPUClient.Do
is modified to set the response header keyhttpu.LocalAddressHeader
to the relatedhttpuClient.Addr
instead ofhttpuClient.conn.LocalAddr
Overall, in this form it is breaking the API. At some point i though that it probably should exist a(nother) Client
API onto which functions defined within goupnp.go
are defined with more flexibility. Than some default client is defined and those package functions redirect their call to that default client.
Edit: looking further,
- Discovery services functions defined within
goupnp/service.go
should also exist within that aforementioned client. - the auto generated code would have to make public those definitions that maps a generic client to a dedicated service client. https://github.com/huin/goupnp/blob/main/dcps/internetgateway1/internetgateway1.go#L109
- Alternatively, maybe, all those dedicated clients like
WANEthernetLinkConfig1
,LANHostConfigManagement1
etc could share a small API likeinterface { setGenericClient(goupnp.ServiceClient}
and he packagegoupnp
provides aMultiServiceClient []GenericClientSetter
API to distribute calls to individual underlyingsetGenericClient
instances, whichever their upnp implementations is. (After further reading, i figured we could definefunc (client *ServiceClient) setGenericClient(n *ServiceClient){*client=*n}
, thus, all dedicated upnp implementations would benefit from it. I don't usually write this kind of method, but that should do it, I guess) - that
MultiServiceClient
Could also provide some filtering/mapping methods to group services byLocationURL
/USN
/Device ID
/Local Address
. That would be useful to work when dealing with multi homed setup. - So that "client" is something about the discovery process, guessing.
I dont share the sketch because it contains lots of useless dup code that would not exist if i had used a fork directly, i worked around. Although, here is the resulting equivalent invocation of the API, to figure how different that would be
addrs := []string{"0.0.0.0"}
addrs = append(addrs, upnp.SomeLocalIPv4MCastAddrs()...)
log.Println(addrs)
client, err := upnp.NewHTTPUMultiClient(addrs...)
if err != nil {
return err
}
rootDevices, err := upnp.DiscoverDevicesCtx(context.Background(), client, ssdp.UPNPRootDevice)
if err != nil {
return err
}
for _, rtDevice := range rootDevices {
log.Println(rtDevice.LocalAddr)
log.Println(rtDevice.Location)
log.Println(rtDevice.Root.SpecVersion)
log.Println(rtDevice.Root.Device.String())
}
from goupnp.
I ended up crafting many things.
there is a new discovery client like
client := goupnp.Discovery{
Client: goupnp.AutoDiscover("0.0.0.0"),
}
A new kind to lookup services and provide concrete implementations such as lookup.ServiceTable map[string]func(goupnp.MultiServiceClient) ServiceImplementations
svcTable := lookup.ServiceTable{
internetgateway2.URN_WANIPConnection_1: func(services goupnp.MultiServiceClient) (out lookup.ServiceImplementations) {
for _, v := range internetgateway2.NewWANIPConnection1MultiClients(services) {
out = append(out, v)
}
return
},
Then it is possible to resolve services to implementation with a call like
searchTargets := svcTable.Targets() // internetgateway2.URN_WANIPConnection_1, internetgateway2.URN_WANIPConnection_2 etc....
services, _, err := client.NewServiceClientsCtx(context.Background(), searchTargets...)
if err != nil {
return err
}
// do some filterings...
// hostwhatever := services.Filter(goupnp.ServiceHost("192..."))
impls := svcTable.Implement(services)
// impls is interface []lookup.ServiceImplementation, which in turn is one of ` internetgateway2.NewWANIPConnection1MultiClients` , ` internetgateway2.NewWANIPConnection2MultiClients` etc...
for _, impl := range impls {
fmt.Printf("%T\n", impl)
// need type assert
// impl.(portmapping.PortMapper).AddPortMapping()
}
The Template is modified to instantiate DCP client for a MultiServiceClient
, such as
func NewDeviceProtection1MultiClients(mutiClients goupnp.MultiServiceClient) []*DeviceProtection1 {
services := mutiClients.Filter(goupnp.ServiceType(URN_DeviceProtection_1)) // The service clients are filtered to keep only those that corresponds to the related service definition
clients := make([]*DeviceProtection1, len(services))
for i := range services {
clients[i] = &DeviceProtection1{services[i]}
}
return clients
}
The MultiServiceClient
is a list filter that works on the flat list of all services for every local addr.
It defines, func (m MultiServiceClient) Filter(f ServiceClientFilter, andAlso ...ServiceClientFilter) MultiServiceClient
and type ServiceClientFilter func(ServiceClient) bool
. With some dedicated helpers such as ServiceType(ts ...string) ServiceClientFilter
.
Doing so, i was able to define an appropriate IGW service table
var IGW = lookup.ServiceTable{
internetgateway1.URN_WANIPConnection_1: func(services goupnp.MultiServiceClient) (out lookup.ServiceImplementations) {
for _, v := range internetgateway1.NewWANIPConnection1MultiClients(services) {
out = append(out, v)
}
return
},
internetgateway2.URN_WANIPConnection_2: func(services goupnp.MultiServiceClient) (out lookup.ServiceImplementations) {
for _, v := range internetgateway2.NewWANIPConnection2MultiClients(services) {
out = append(out, v)
}
return
},
}
And its accompanying Lookup
function to deal with virtual any addresses.
var out []PortMapperClient
searchTargets := IGW.Targets()
services, _, err := client.NewServiceClientsCtx(context.Background(), searchTargets...)
if err != nil {
return out, err
}
impls := IGW.ExpandAnyAddr(services)
for _, impl := range impls {
switch x := impl.(type) {
case lookup.AnyAddrServiceImplementation:
out = append(out, PortMapperClient{
PortMapper: x.ServiceImplementation.(PortMapper),
ServiceImplementation: impl,
})
case PortMapper:
out = append(out, PortMapperClient{
PortMapper: x,
ServiceImplementation: impl,
})
default:
log.Printf("not a port mapper %T", x)
}
}
in the end it can fetch all clients, resolve the virtual address thing and get the right local address to use for the call to AddPortMapping
when it encounters anyAddr.
for _, impl := range impls {
switch x := impl.(type) {
case lookup.AnyAddrServiceImplementation:
fmt.Printf("%v should port map using %v\n", x.ServiceImplementation.LocalAddr(), impl.LocalAddr())
}
_, ok := impl.(portmapping.PortMapper)
fmt.Printf("%T PortMapper=%v\n", impl, ok)
printService(*impl.GetServiceClient())
}
anyway.. Much.... much changes !
from goupnp.
Sorry for the slow reply, I tend to only deal with this repo on weekends.
A few questions/comments:
-
MultiServiceClient
- is this something that benefits from being in this library, or might be better placed in a more specific library or application codebase? That is: is its utility general and not special purpose? -
httpu.HTTPUClient
:-
having a new address field (presumably affecting which address+interface it talks on) - or perhaps receiving the local address as a parameter - this seems possible without breaking compatibility, by either adding a new
New*
function, or better yet: using functional options. This pattern could be applied elsewhere. -
httpu.HTTPUClient.Do
is modified to set the response header keyhttpu.LocalAddressHeader
to the relatedhttpuClient.Addr
instead ofhttpuClient.conn.LocalAddr
What if instead the
HTTPClient.conn
field was created based on the given local address? And then set the header based on that (which should be effectively the same, for specific addresses?)
-
-
ssdp.SSDPRawSearch
:-
taking a client parameter - it already receives an HTTPU client, perhaps we simply need to broaden it to accepting
ClientInterface
instead of a concrete type? -
Changing the dedupe behaviour - also possible to change without breaking compatibility with functional options. Perhaps provide a function that maps from result to deduplication key? (with the default being the current behaviour).
All that said - isn't this achievable by users of the library calling
SSDPRawSearch
multiple times (optionally concurrently)?
-
-
goupnp.DiscoverDevicesCtx
and others, are modified to take in a clienthttpu.ClientInterface
Seems reasonable - functional options for compatibility?
from goupnp.
hi, no worries for the late reply, thanks for the inputs.
About functional options, it surely could do few things, although, this is still breaking the API signature. I dont think this is right for someone that built up some APIs that relies on interfaces, or functions type def.
About httpuClient
, the reason I did that is that if you do net.Listen("0.0.0.0").Addr()
you dont exactly get back 0.0.0.0
, but its ipv6 equivalent notation. It is anything but obvious. So that seem just right to re use the strict input.
About ssdpRawSearch
, bad comment on my part, sorry, indeed it already receives one, it just happens I had to rename the variable because it was conflicting with the newly imported httpu
package, and misread that. Although, again, such change, take n an hypothetical ClientInterface
would break signature APIs.
About MultiServiceClient
, I have introduced that new type in order to manipulate found clients before they are instantiated, filtering and select against the multiple upnp gateways. Otherwise, such API leaks into all generated types (
Although, this is definitely an intentional major shift in regard to the current API design.
Overall, besides the specific implementation details change, like the dedup mechanism, which could be made configurable, one way or another, it is a lot of changes, despite my attempts at being conservative (i tried = ). I feel mixed about it regarding the shift in the design api. Maybe i kept that away and for an eventual V2 this is considered.
from goupnp.
Related Issues (20)
- Unable to find devices most of the time HOT 7
- It's not possible to query ssdp:all HOT 1
- Incorrect err (nil) returned incase of timeout in httpu HOT 6
- Discover UPnP as not active if UPnP not ON in device HOT 4
- Show devices connected to discovered UPnP enabled device. HOT 2
- Why can't i find this upnp device (http://192.168.61.1:5000/rootDesc.xml)? HOT 5
- GetTotalBytesSent() can't parse value, if it's negative HOT 3
- Fail on http post request to router. HOT 12
- httpu multicast fails on multiple interfaces HOT 7
- Please provide example of AV dlna device implementation (audio) HOT 2
- Could you specify some examples? HOT 10
- Making charset dependency optional HOT 6
- SSDPRawSearch: With the same USN, ipv4Response was replaced by ipv6Response, which is not effected for ipv4only client. HOT 1
- Contexts on HTTP calls HOT 6
- httpu: error while parsing response HOT 4
- Any plan to add SSDP Server Support ? HOT 1
- get the IP associated with the interface used to successfully discover the upnp setup ? HOT 26
- feat: context everywhere and non-blocking results HOT 2
- update the guide HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from goupnp.