forked from
tangled.org/core
Monorepo for Tangled
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}