registry.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  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. "github.com/syncthing/syncthing/lib/sync"
  13. )
  14. var (
  15. Default = New()
  16. )
  17. type Registry struct {
  18. mut sync.Mutex
  19. available map[string][]interface{}
  20. }
  21. func New() *Registry {
  22. return &Registry{
  23. mut: sync.NewMutex(),
  24. available: make(map[string][]interface{}),
  25. }
  26. }
  27. func (r *Registry) Register(scheme string, item interface{}) {
  28. r.mut.Lock()
  29. defer r.mut.Unlock()
  30. r.available[scheme] = append(r.available[scheme], item)
  31. }
  32. func (r *Registry) Unregister(scheme string, item interface{}) {
  33. r.mut.Lock()
  34. defer r.mut.Unlock()
  35. candidates := r.available[scheme]
  36. for i, existingItem := range candidates {
  37. if existingItem == item {
  38. candidates[i] = candidates[len(candidates)-1]
  39. candidates[len(candidates)-1] = nil
  40. r.available[scheme] = candidates[:len(candidates)-1]
  41. break
  42. }
  43. }
  44. }
  45. // Get returns an item for a schema compatible with the given scheme.
  46. // If any item satisfies preferred, that has precedence over other items.
  47. func (r *Registry) Get(scheme string, preferred func(interface{}) bool) interface{} {
  48. r.mut.Lock()
  49. defer r.mut.Unlock()
  50. var (
  51. best interface{}
  52. bestPref bool
  53. bestScheme string
  54. )
  55. for availableScheme, items := range r.available {
  56. // quic:// should be considered ok for both quic4:// and quic6://
  57. if !strings.HasPrefix(scheme, availableScheme) {
  58. continue
  59. }
  60. for _, item := range items {
  61. better := best == nil
  62. pref := preferred(item)
  63. if !better {
  64. // In case of a tie, prefer "quic" to "quic[46]" etc.
  65. better = pref &&
  66. (!bestPref || len(availableScheme) < len(bestScheme))
  67. }
  68. if !better {
  69. continue
  70. }
  71. best, bestPref, bestScheme = item, pref, availableScheme
  72. }
  73. }
  74. return best
  75. }
  76. func Register(scheme string, item interface{}) {
  77. Default.Register(scheme, item)
  78. }
  79. func Unregister(scheme string, item interface{}) {
  80. Default.Unregister(scheme, item)
  81. }
  82. func Get(scheme string, preferred func(interface{}) bool) interface{} {
  83. return Default.Get(scheme, preferred)
  84. }