registry.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  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. "sort"
  12. "strings"
  13. "github.com/syncthing/syncthing/lib/sync"
  14. )
  15. var (
  16. Default = New()
  17. )
  18. type Registry struct {
  19. mut sync.Mutex
  20. available map[string][]interface{}
  21. }
  22. func New() *Registry {
  23. return &Registry{
  24. mut: sync.NewMutex(),
  25. available: make(map[string][]interface{}),
  26. }
  27. }
  28. func (r *Registry) Register(scheme string, item interface{}) {
  29. r.mut.Lock()
  30. defer r.mut.Unlock()
  31. r.available[scheme] = append(r.available[scheme], item)
  32. }
  33. func (r *Registry) Unregister(scheme string, item interface{}) {
  34. r.mut.Lock()
  35. defer r.mut.Unlock()
  36. candidates := r.available[scheme]
  37. for i, existingItem := range candidates {
  38. if existingItem == item {
  39. copy(candidates[i:], candidates[i+1:])
  40. candidates[len(candidates)-1] = nil
  41. r.available[scheme] = candidates[:len(candidates)-1]
  42. break
  43. }
  44. }
  45. }
  46. func (r *Registry) Get(scheme string, less func(i, j interface{}) bool) interface{} {
  47. r.mut.Lock()
  48. defer r.mut.Unlock()
  49. candidates := make([]interface{}, 0)
  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. candidates = append(candidates, items...)
  54. }
  55. }
  56. if len(candidates) == 0 {
  57. return nil
  58. }
  59. sort.Slice(candidates, func(i, j int) bool {
  60. return less(candidates[i], candidates[j])
  61. })
  62. return candidates[0]
  63. }
  64. func Register(scheme string, item interface{}) {
  65. Default.Register(scheme, item)
  66. }
  67. func Unregister(scheme string, item interface{}) {
  68. Default.Unregister(scheme, item)
  69. }
  70. func Get(scheme string, less func(i, j interface{}) bool) interface{} {
  71. return Default.Get(scheme, less)
  72. }