Monorepo for Tangled
at master 126 lines 3.2 kB view raw
1package idresolver 2 3import ( 4 "context" 5 "net" 6 "net/http" 7 "sync" 8 "time" 9 10 "github.com/bluesky-social/indigo/atproto/identity" 11 "github.com/bluesky-social/indigo/atproto/identity/redisdir" 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "github.com/carlmjohnson/versioninfo" 14) 15 16type Resolver struct { 17 directory identity.Directory 18 base *identity.BaseDirectory 19} 20 21func BaseDirectory(plcUrl string) *identity.BaseDirectory { 22 base := identity.BaseDirectory{ 23 PLCURL: plcUrl, 24 HTTPClient: http.Client{ 25 Timeout: time.Second * 10, 26 Transport: &http.Transport{ 27 // would want this around 100ms for services doing lots of handle resolution. Impacts PLC connections as well, but not too bad. 28 IdleConnTimeout: time.Millisecond * 1000, 29 MaxIdleConns: 100, 30 }, 31 }, 32 Resolver: net.Resolver{ 33 Dial: func(ctx context.Context, network, address string) (net.Conn, error) { 34 d := net.Dialer{Timeout: time.Second * 3} 35 return d.DialContext(ctx, network, address) 36 }, 37 }, 38 TryAuthoritativeDNS: true, 39 // primary Bluesky PDS instance only supports HTTP resolution method 40 SkipDNSDomainSuffixes: []string{".bsky.social"}, 41 UserAgent: "indigo-identity/" + versioninfo.Short(), 42 } 43 return &base 44} 45 46func DefaultResolver(plcUrl string) *Resolver { 47 base := BaseDirectory(plcUrl) 48 cached := identity.NewCacheDirectory(base, 250_000, time.Hour*24, time.Minute*2, time.Minute*5) 49 return &Resolver{ 50 directory: cached, 51 base: base, 52 } 53} 54 55func RedisDirectory(base *identity.BaseDirectory, url string) (identity.Directory, error) { 56 hitTTL := time.Hour * 24 57 errTTL := time.Second * 30 58 invalidHandleTTL := time.Minute * 5 59 return redisdir.NewRedisDirectory(base, url, hitTTL, errTTL, invalidHandleTTL, 10000) 60} 61 62func RedisResolver(redisUrl, plcUrl string) (*Resolver, error) { 63 base := BaseDirectory(plcUrl) 64 directory, err := RedisDirectory(base, redisUrl) 65 if err != nil { 66 return nil, err 67 } 68 return &Resolver{ 69 directory: directory, 70 base: base, 71 }, nil 72} 73 74func (r *Resolver) ResolveHandle(ctx context.Context, handle syntax.Handle) (syntax.DID, error) { 75 return r.base.ResolveHandle(ctx, handle) 76} 77 78func (r *Resolver) ResolveIdent(ctx context.Context, arg string) (*identity.Identity, error) { 79 id, err := syntax.ParseAtIdentifier(arg) 80 if err != nil { 81 return nil, err 82 } 83 84 return r.directory.Lookup(ctx, id) 85} 86 87func (r *Resolver) ResolveIdents(ctx context.Context, idents []string) []*identity.Identity { 88 results := make([]*identity.Identity, len(idents)) 89 var wg sync.WaitGroup 90 91 done := make(chan struct{}) 92 defer close(done) 93 94 for idx, ident := range idents { 95 wg.Add(1) 96 go func(index int, id string) { 97 defer wg.Done() 98 99 select { 100 case <-ctx.Done(): 101 results[index] = nil 102 case <-done: 103 results[index] = nil 104 default: 105 identity, _ := r.ResolveIdent(ctx, id) 106 results[index] = identity 107 } 108 }(idx, ident) 109 } 110 111 wg.Wait() 112 return results 113} 114 115func (r *Resolver) InvalidateIdent(ctx context.Context, arg string) error { 116 id, err := syntax.ParseAtIdentifier(arg) 117 if err != nil { 118 return err 119 } 120 121 return r.directory.Purge(ctx, id) 122} 123 124func (r *Resolver) Directory() identity.Directory { 125 return r.directory 126}