An atproto PDS written in Go

refactor identity package (#22)

Changed files
+89 -59
identity
+73 -54
identity/identity.go
···
"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
-
}
+
return "", fmt.Errorf("handle could not be resolved via txt: no record found")
+
}
-
resp, err := http.DefaultClient.Do(req)
-
if err != nil {
-
return "", nil
-
}
-
defer resp.Body.Close()
+
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)
+
}
-
if resp.StatusCode != http.StatusOK {
-
io.Copy(io.Discard, resp.Body)
-
return "", fmt.Errorf("unable to resolve handle")
-
}
+
resp, err := cli.Do(req)
+
if err != nil {
+
return "", fmt.Errorf("handle could not be resolved via web: %w", err)
+
}
+
defer resp.Body.Close()
-
b, err := io.ReadAll(resp.Body)
-
if err != nil {
-
return "", err
-
}
+
b, err := io.ReadAll(resp.Body)
+
if err != nil {
+
return "", fmt.Errorf("handle could not be resolved via web: %w", err)
+
}
-
maybeDid := string(b)
+
if resp.StatusCode != http.StatusOK {
+
return "", fmt.Errorf("handle could not be resolved via web: invalid status code %d", resp.StatusCode)
+
}
-
if _, err := syntax.ParseDID(maybeDid); err != nil {
-
return "", fmt.Errorf("unable to resolve handle")
-
}
+
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)
···
return nil, err
}
-
resp, err := http.DefaultClient.Do(req)
+
resp, err := cli.Do(req)
if err != nil {
return nil, err
}
···
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
···
return nil, err
}
-
resp, err := http.DefaultClient.Do(req)
+
resp, err := cli.Do(req)
if err != nil {
return nil, err
}
+16 -5
identity/passport.go
···
type Passport struct {
h *http.Client
bc BackingCache
-
lk sync.Mutex
+
mu sync.RWMutex
}
func NewPassport(h *http.Client, bc BackingCache) *Passport {
···
return &Passport{
h: h,
bc: bc,
-
lk: sync.Mutex{},
}
}
···
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
}
···
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
}
···
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)
}