registry.go 2.1 KB

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