registry.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. // Copyright (C) 2019 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. // Registry tracks connections/addresses on which we are listening on, to allow us to pick a connection/address that
  7. // has a NAT port mapping. This also makes our outgoing port stable and same as incoming port which should allow
  8. // better probability of punching through.
  9. package registry
  10. import (
  11. "strings"
  12. "sync"
  13. "github.com/syncthing/syncthing/lib/sliceutil"
  14. )
  15. type Registry struct {
  16. mut sync.Mutex
  17. available map[string][]interface{}
  18. }
  19. func New() *Registry {
  20. return &Registry{
  21. available: make(map[string][]interface{}),
  22. }
  23. }
  24. func (r *Registry) Register(scheme string, item interface{}) {
  25. r.mut.Lock()
  26. defer r.mut.Unlock()
  27. r.available[scheme] = append(r.available[scheme], item)
  28. }
  29. func (r *Registry) Unregister(scheme string, item interface{}) {
  30. r.mut.Lock()
  31. defer r.mut.Unlock()
  32. candidates := r.available[scheme]
  33. for i, existingItem := range candidates {
  34. if existingItem == item {
  35. r.available[scheme] = sliceutil.RemoveAndZero(candidates, i)
  36. break
  37. }
  38. }
  39. }
  40. // Get returns an item for a schema compatible with the given scheme.
  41. // If any item satisfies preferred, that has precedence over other items.
  42. func (r *Registry) Get(scheme string, preferred func(interface{}) bool) interface{} {
  43. r.mut.Lock()
  44. defer r.mut.Unlock()
  45. var (
  46. best interface{}
  47. bestPref bool
  48. bestScheme string
  49. )
  50. for availableScheme, items := range r.available {
  51. // quic:// should be considered ok for both quic4:// and quic6://
  52. if !strings.HasPrefix(scheme, availableScheme) {
  53. continue
  54. }
  55. for _, item := range items {
  56. better := best == nil
  57. pref := preferred(item)
  58. if !better {
  59. // In case of a tie, prefer "quic" to "quic[46]" etc.
  60. better = pref &&
  61. (!bestPref || len(availableScheme) < len(bestScheme))
  62. }
  63. if !better {
  64. continue
  65. }
  66. best, bestPref, bestScheme = item, pref, availableScheme
  67. }
  68. }
  69. return best
  70. }