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