1package resolver
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 RedisDirectory(url string) (identity.Directory, error) {
46 return redisdir.NewRedisDirectory(BaseDirectory(), url, time.Hour*24, time.Hour*1, time.Hour*1, 10000)
47}
48
49func DefaultResolver() *Resolver {
50 return &Resolver{
51 directory: identity.DefaultDirectory(),
52 }
53}
54
55func RedisResolver(url string) (*Resolver, error) {
56 directory, err := RedisDirectory(url)
57 if err != nil {
58 return nil, err
59 }
60 return &Resolver{
61 directory: directory,
62 }, nil
63}
64
65func (r *Resolver) ResolveIdent(ctx context.Context, arg string) (*identity.Identity, error) {
66 id, err := syntax.ParseAtIdentifier(arg)
67 if err != nil {
68 return nil, err
69 }
70
71 return r.directory.Lookup(ctx, *id)
72}
73
74func (r *Resolver) ResolveIdents(ctx context.Context, idents []string) []*identity.Identity {
75 results := make([]*identity.Identity, len(idents))
76 var wg sync.WaitGroup
77
78 done := make(chan struct{})
79 defer close(done)
80
81 for idx, ident := range idents {
82 wg.Add(1)
83 go func(index int, id string) {
84 defer wg.Done()
85
86 select {
87 case <-ctx.Done():
88 results[index] = nil
89 case <-done:
90 results[index] = nil
91 default:
92 identity, _ := r.ResolveIdent(ctx, id)
93 results[index] = identity
94 }
95 }(idx, ident)
96 }
97
98 wg.Wait()
99 return results
100}