From 6f90d8a4ae594776c3b54a43151464763b2cb054 Mon Sep 17 00:00:00 2001 From: Hailey Date: Sat, 19 Jul 2025 09:30:44 -0700 Subject: [PATCH] refactor identity package --- identity/identity.go | 131 +++++++++++++++++++++++++------------------ identity/passport.go | 21 +++++-- 2 files changed, 91 insertions(+), 61 deletions(-) diff --git a/identity/identity.go b/identity/identity.go index 0a05ce9..ca945d7 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -13,81 +13,100 @@ import ( "github.com/bluesky-social/indigo/util" ) -func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) { - if cli == nil { - cli = util.RobustHTTPClient() - } - - var did string - - _, err := syntax.ParseHandle(handle) +func ResolveHandleFromTXT(ctx context.Context, handle string) (string, error) { + name := fmt.Sprintf("_atproto.%s", handle) + recs, err := net.LookupTXT(name) if err != nil { - return "", err + return "", fmt.Errorf("handle could not be resolved via txt: %w", err) } - recs, err := net.LookupTXT(fmt.Sprintf("_atproto.%s", handle)) - if err == nil { - for _, rec := range recs { - if strings.HasPrefix(rec, "did=") { - did = strings.Split(rec, "did=")[1] - break + for _, rec := range recs { + if strings.HasPrefix(rec, "did=") { + maybeDid := strings.Split(rec, "did=")[1] + if _, err := syntax.ParseDID(maybeDid); err == nil { + return maybeDid, nil } } - } else { - fmt.Printf("erorr getting txt records: %v\n", err) - } - - if did == "" { - req, err := http.NewRequestWithContext( - ctx, - "GET", - fmt.Sprintf("https://%s/.well-known/atproto-did", handle), - nil, - ) - if err != nil { - return "", nil - } + } - resp, err := http.DefaultClient.Do(req) - if err != nil { - return "", nil - } - defer resp.Body.Close() + return "", fmt.Errorf("handle could not be resolved via txt: no record found") +} - if resp.StatusCode != http.StatusOK { - io.Copy(io.Discard, resp.Body) - return "", fmt.Errorf("unable to resolve handle") - } +func ResolveHandleFromWellKnown(ctx context.Context, cli *http.Client, handle string) (string, error) { + ustr := fmt.Sprintf("https://%s/.well=known/atproto-did", handle) + req, err := http.NewRequestWithContext( + ctx, + "GET", + ustr, + nil, + ) + if err != nil { + return "", fmt.Errorf("handle could not be resolved via web: %w", err) + } - b, err := io.ReadAll(resp.Body) - if err != nil { - return "", err - } + resp, err := cli.Do(req) + if err != nil { + return "", fmt.Errorf("handle could not be resolved via web: %w", err) + } + defer resp.Body.Close() - maybeDid := string(b) + b, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("handle could not be resolved via web: %w", err) + } - if _, err := syntax.ParseDID(maybeDid); err != nil { - return "", fmt.Errorf("unable to resolve handle") - } + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("handle could not be resolved via web: invalid status code %d", resp.StatusCode) + } + + maybeDid := string(b) - did = maybeDid + if _, err := syntax.ParseDID(maybeDid); err != nil { + return "", fmt.Errorf("handle could not be resolved via web: invalid did in document") } - return did, nil + return maybeDid, nil } -func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) { +func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) { if cli == nil { cli = util.RobustHTTPClient() } - var ustr string + _, err := syntax.ParseHandle(handle) + if err != nil { + return "", err + } + + if maybeDidFromTxt, err := ResolveHandleFromTXT(ctx, handle); err == nil { + return maybeDidFromTxt, nil + } + + if maybeDidFromWeb, err := ResolveHandleFromWellKnown(ctx, cli, handle); err == nil { + return maybeDidFromWeb, nil + } + + return "", fmt.Errorf("handle could not be resolved") +} + +func DidToDocUrl(did string) (string, error) { if strings.HasPrefix(did, "did:plc:") { - ustr = fmt.Sprintf("https://plc.directory/%s", did) + return fmt.Sprintf("https://plc.directory/%s", did), nil } else if strings.HasPrefix(did, "did:web:") { - ustr = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")) + return fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")), nil } else { - return nil, fmt.Errorf("did was not a supported did type") + return "", fmt.Errorf("did was not a supported did type") + } +} + +func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) { + if cli == nil { + cli = util.RobustHTTPClient() + } + + ustr, err := DidToDocUrl(did) + if err != nil { + return nil, err } req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) @@ -95,7 +114,7 @@ func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, er return nil, err } - resp, err := http.DefaultClient.Do(req) + resp, err := cli.Do(req) if err != nil { return nil, err } @@ -103,7 +122,7 @@ func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, er if resp.StatusCode != 200 { io.Copy(io.Discard, resp.Body) - return nil, fmt.Errorf("could not find identity in plc registry") + return nil, fmt.Errorf("unable to find did doc at url. did: %s. url: %s", did, ustr) } var diddoc DidDoc @@ -127,7 +146,7 @@ func FetchDidData(ctx context.Context, cli *http.Client, did string) (*DidData, return nil, err } - resp, err := http.DefaultClient.Do(req) + resp, err := cli.Do(req) if err != nil { return nil, err } diff --git a/identity/passport.go b/identity/passport.go index fadbe6f..a8ab36b 100644 --- a/identity/passport.go +++ b/identity/passport.go @@ -19,7 +19,7 @@ type BackingCache interface { type Passport struct { h *http.Client bc BackingCache - lk sync.Mutex + mu sync.RWMutex } func NewPassport(h *http.Client, bc BackingCache) *Passport { @@ -30,7 +30,6 @@ func NewPassport(h *http.Client, bc BackingCache) *Passport { return &Passport{ h: h, bc: bc, - lk: sync.Mutex{}, } } @@ -38,21 +37,24 @@ func (p *Passport) FetchDoc(ctx context.Context, did string) (*DidDoc, error) { skipCache, _ := ctx.Value("skip-cache").(bool) if !skipCache { + p.mu.RLock() cached, ok := p.bc.GetDoc(did) + p.mu.RUnlock() + if ok { return cached, nil } } - p.lk.Lock() // this is pretty pathetic, and i should rethink this. but for now, fuck it - defer p.lk.Unlock() - + // TODO: should coalesce requests here doc, err := FetchDidDoc(ctx, p.h, did) if err != nil { return nil, err } + p.mu.Lock() p.bc.PutDoc(did, doc) + p.mu.Unlock() return doc, nil } @@ -61,7 +63,10 @@ func (p *Passport) ResolveHandle(ctx context.Context, handle string) (string, er skipCache, _ := ctx.Value("skip-cache").(bool) if !skipCache { + p.mu.RLock() cached, ok := p.bc.GetDid(handle) + p.mu.RUnlock() + if ok { return cached, nil } @@ -72,15 +77,21 @@ func (p *Passport) ResolveHandle(ctx context.Context, handle string) (string, er return "", err } + p.mu.Lock() p.bc.PutDid(handle, did) + p.mu.Unlock() return did, nil } func (p *Passport) BustDoc(ctx context.Context, did string) error { + p.mu.Lock() + defer p.mu.Unlock() return p.bc.BustDoc(did) } func (p *Passport) BustDid(ctx context.Context, handle string) error { + p.mu.Lock() + defer p.mu.Unlock() return p.bc.BustDid(handle) } -- 2.51.2