service_client.go 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. package goupnp
  2. import (
  3. "context"
  4. "fmt"
  5. "net/url"
  6. "github.com/tailscale/goupnp/soap"
  7. )
  8. // ServiceClient is a SOAP client, root device and the service for the SOAP
  9. // client rolled into one value. The root device, location, and service are
  10. // intended to be informational. Location can be used to later recreate a
  11. // ServiceClient with NewServiceClientByURL if the service is still present;
  12. // bypassing the discovery process.
  13. type ServiceClient struct {
  14. SOAPClient *soap.SOAPClient
  15. RootDevice *RootDevice
  16. Location *url.URL
  17. Service *Service
  18. }
  19. // NewServiceClients discovers services, and returns clients for them. err will
  20. // report any error with the discovery process (blocking any device/service
  21. // discovery), errors reports errors on a per-root-device basis.
  22. func NewServiceClients(ctx context.Context, searchTarget string) (clients []ServiceClient, errors []error, err error) {
  23. var maybeRootDevices []MaybeRootDevice
  24. if maybeRootDevices, err = DiscoverDevices(ctx, searchTarget); err != nil {
  25. return
  26. }
  27. clients = make([]ServiceClient, 0, len(maybeRootDevices))
  28. for _, maybeRootDevice := range maybeRootDevices {
  29. if maybeRootDevice.Err != nil {
  30. errors = append(errors, maybeRootDevice.Err)
  31. continue
  32. }
  33. deviceClients, err := NewServiceClientsFromRootDevice(ctx, maybeRootDevice.Root, maybeRootDevice.Location, searchTarget)
  34. if err != nil {
  35. errors = append(errors, err)
  36. continue
  37. }
  38. clients = append(clients, deviceClients...)
  39. }
  40. return
  41. }
  42. // NewServiceClientsByURL creates client(s) for the given service URN, for a
  43. // root device at the given URL.
  44. func NewServiceClientsByURL(ctx context.Context, loc *url.URL, searchTarget string) ([]ServiceClient, error) {
  45. rootDevice, err := DeviceByURL(ctx, loc)
  46. if err != nil {
  47. return nil, err
  48. }
  49. return NewServiceClientsFromRootDevice(ctx, rootDevice, loc, searchTarget)
  50. }
  51. // NewServiceClientsFromDevice creates client(s) for the given service URN, in
  52. // a given root device. The loc parameter is simply assigned to the
  53. // Location attribute of the returned ServiceClient(s).
  54. func NewServiceClientsFromRootDevice(ctx context.Context, rootDevice *RootDevice, loc *url.URL, searchTarget string) ([]ServiceClient, error) {
  55. device := &rootDevice.Device
  56. srvs := device.FindService(ctx, searchTarget)
  57. if len(srvs) == 0 {
  58. return nil, fmt.Errorf("goupnp: service %q not found within device %q (UDN=%q)",
  59. searchTarget, device.FriendlyName, device.UDN)
  60. }
  61. clients := make([]ServiceClient, 0, len(srvs))
  62. for _, srv := range srvs {
  63. clients = append(clients, ServiceClient{
  64. SOAPClient: srv.NewSOAPClient(httpClient(ctx)),
  65. RootDevice: rootDevice,
  66. Location: loc,
  67. Service: srv,
  68. })
  69. }
  70. return clients, nil
  71. }
  72. // GetServiceClient returns the ServiceClient itself. This is provided so that the
  73. // service client attributes can be accessed via an interface method on a
  74. // wrapping type.
  75. func (client *ServiceClient) GetServiceClient() *ServiceClient {
  76. return client
  77. }