forked from hailey.at/cocoon
An atproto PDS written in Go

refactor identity package (#22)

authored by hailey.at and committed by GitHub 2face30c fead6747

Changed files
+89 -59
identity
+73 -54
identity/identity.go
··· 13 13 "github.com/bluesky-social/indigo/util" 14 14 ) 15 15 16 - func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) { 17 - if cli == nil { 18 - cli = util.RobustHTTPClient() 19 - } 20 - 21 - var did string 22 - 23 - _, err := syntax.ParseHandle(handle) 16 + func ResolveHandleFromTXT(ctx context.Context, handle string) (string, error) { 17 + name := fmt.Sprintf("_atproto.%s", handle) 18 + recs, err := net.LookupTXT(name) 24 19 if err != nil { 25 - return "", err 20 + return "", fmt.Errorf("handle could not be resolved via txt: %w", err) 26 21 } 27 22 28 - recs, err := net.LookupTXT(fmt.Sprintf("_atproto.%s", handle)) 29 - if err == nil { 30 - for _, rec := range recs { 31 - if strings.HasPrefix(rec, "did=") { 32 - did = strings.Split(rec, "did=")[1] 33 - break 23 + for _, rec := range recs { 24 + if strings.HasPrefix(rec, "did=") { 25 + maybeDid := strings.Split(rec, "did=")[1] 26 + if _, err := syntax.ParseDID(maybeDid); err == nil { 27 + return maybeDid, nil 34 28 } 35 29 } 36 - } else { 37 - fmt.Printf("erorr getting txt records: %v\n", err) 38 30 } 39 31 40 - if did == "" { 41 - req, err := http.NewRequestWithContext( 42 - ctx, 43 - "GET", 44 - fmt.Sprintf("https://%s/.well-known/atproto-did", handle), 45 - nil, 46 - ) 47 - if err != nil { 48 - return "", nil 49 - } 32 + return "", fmt.Errorf("handle could not be resolved via txt: no record found") 33 + } 50 34 51 - resp, err := http.DefaultClient.Do(req) 52 - if err != nil { 53 - return "", nil 54 - } 55 - defer resp.Body.Close() 35 + func ResolveHandleFromWellKnown(ctx context.Context, cli *http.Client, handle string) (string, error) { 36 + ustr := fmt.Sprintf("https://%s/.well=known/atproto-did", handle) 37 + req, err := http.NewRequestWithContext( 38 + ctx, 39 + "GET", 40 + ustr, 41 + nil, 42 + ) 43 + if err != nil { 44 + return "", fmt.Errorf("handle could not be resolved via web: %w", err) 45 + } 56 46 57 - if resp.StatusCode != http.StatusOK { 58 - io.Copy(io.Discard, resp.Body) 59 - return "", fmt.Errorf("unable to resolve handle") 60 - } 47 + resp, err := cli.Do(req) 48 + if err != nil { 49 + return "", fmt.Errorf("handle could not be resolved via web: %w", err) 50 + } 51 + defer resp.Body.Close() 61 52 62 - b, err := io.ReadAll(resp.Body) 63 - if err != nil { 64 - return "", err 65 - } 53 + b, err := io.ReadAll(resp.Body) 54 + if err != nil { 55 + return "", fmt.Errorf("handle could not be resolved via web: %w", err) 56 + } 66 57 67 - maybeDid := string(b) 58 + if resp.StatusCode != http.StatusOK { 59 + return "", fmt.Errorf("handle could not be resolved via web: invalid status code %d", resp.StatusCode) 60 + } 68 61 69 - if _, err := syntax.ParseDID(maybeDid); err != nil { 70 - return "", fmt.Errorf("unable to resolve handle") 71 - } 62 + maybeDid := string(b) 72 63 73 - did = maybeDid 64 + if _, err := syntax.ParseDID(maybeDid); err != nil { 65 + return "", fmt.Errorf("handle could not be resolved via web: invalid did in document") 74 66 } 75 67 76 - return did, nil 68 + return maybeDid, nil 77 69 } 78 70 79 - func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) { 71 + func ResolveHandle(ctx context.Context, cli *http.Client, handle string) (string, error) { 80 72 if cli == nil { 81 73 cli = util.RobustHTTPClient() 82 74 } 83 75 84 - var ustr string 76 + _, err := syntax.ParseHandle(handle) 77 + if err != nil { 78 + return "", err 79 + } 80 + 81 + if maybeDidFromTxt, err := ResolveHandleFromTXT(ctx, handle); err == nil { 82 + return maybeDidFromTxt, nil 83 + } 84 + 85 + if maybeDidFromWeb, err := ResolveHandleFromWellKnown(ctx, cli, handle); err == nil { 86 + return maybeDidFromWeb, nil 87 + } 88 + 89 + return "", fmt.Errorf("handle could not be resolved") 90 + } 91 + 92 + func DidToDocUrl(did string) (string, error) { 85 93 if strings.HasPrefix(did, "did:plc:") { 86 - ustr = fmt.Sprintf("https://plc.directory/%s", did) 94 + return fmt.Sprintf("https://plc.directory/%s", did), nil 87 95 } else if strings.HasPrefix(did, "did:web:") { 88 - ustr = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")) 96 + return fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")), nil 89 97 } else { 90 - return nil, fmt.Errorf("did was not a supported did type") 98 + return "", fmt.Errorf("did was not a supported did type") 99 + } 100 + } 101 + 102 + func FetchDidDoc(ctx context.Context, cli *http.Client, did string) (*DidDoc, error) { 103 + if cli == nil { 104 + cli = util.RobustHTTPClient() 105 + } 106 + 107 + ustr, err := DidToDocUrl(did) 108 + if err != nil { 109 + return nil, err 91 110 } 92 111 93 112 req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) ··· 95 114 return nil, err 96 115 } 97 116 98 - resp, err := http.DefaultClient.Do(req) 117 + resp, err := cli.Do(req) 99 118 if err != nil { 100 119 return nil, err 101 120 } ··· 103 122 104 123 if resp.StatusCode != 200 { 105 124 io.Copy(io.Discard, resp.Body) 106 - return nil, fmt.Errorf("could not find identity in plc registry") 125 + return nil, fmt.Errorf("unable to find did doc at url. did: %s. url: %s", did, ustr) 107 126 } 108 127 109 128 var diddoc DidDoc ··· 127 146 return nil, err 128 147 } 129 148 130 - resp, err := http.DefaultClient.Do(req) 149 + resp, err := cli.Do(req) 131 150 if err != nil { 132 151 return nil, err 133 152 }
+16 -5
identity/passport.go
··· 19 19 type Passport struct { 20 20 h *http.Client 21 21 bc BackingCache 22 - lk sync.Mutex 22 + mu sync.RWMutex 23 23 } 24 24 25 25 func NewPassport(h *http.Client, bc BackingCache) *Passport { ··· 30 30 return &Passport{ 31 31 h: h, 32 32 bc: bc, 33 - lk: sync.Mutex{}, 34 33 } 35 34 } 36 35 ··· 38 37 skipCache, _ := ctx.Value("skip-cache").(bool) 39 38 40 39 if !skipCache { 40 + p.mu.RLock() 41 41 cached, ok := p.bc.GetDoc(did) 42 + p.mu.RUnlock() 43 + 42 44 if ok { 43 45 return cached, nil 44 46 } 45 47 } 46 48 47 - p.lk.Lock() // this is pretty pathetic, and i should rethink this. but for now, fuck it 48 - defer p.lk.Unlock() 49 - 49 + // TODO: should coalesce requests here 50 50 doc, err := FetchDidDoc(ctx, p.h, did) 51 51 if err != nil { 52 52 return nil, err 53 53 } 54 54 55 + p.mu.Lock() 55 56 p.bc.PutDoc(did, doc) 57 + p.mu.Unlock() 56 58 57 59 return doc, nil 58 60 } ··· 61 63 skipCache, _ := ctx.Value("skip-cache").(bool) 62 64 63 65 if !skipCache { 66 + p.mu.RLock() 64 67 cached, ok := p.bc.GetDid(handle) 68 + p.mu.RUnlock() 69 + 65 70 if ok { 66 71 return cached, nil 67 72 } ··· 72 77 return "", err 73 78 } 74 79 80 + p.mu.Lock() 75 81 p.bc.PutDid(handle, did) 82 + p.mu.Unlock() 76 83 77 84 return did, nil 78 85 } 79 86 80 87 func (p *Passport) BustDoc(ctx context.Context, did string) error { 88 + p.mu.Lock() 89 + defer p.mu.Unlock() 81 90 return p.bc.BustDoc(did) 82 91 } 83 92 84 93 func (p *Passport) BustDid(ctx context.Context, handle string) error { 94 + p.mu.Lock() 95 + defer p.mu.Unlock() 85 96 return p.bc.BustDid(handle) 86 97 }