this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

identity: BaseDirectory more of a struct; can be zero

+69 -31
+8 -9
atproto/identity/base_directory.go
··· 3 3 import ( 4 4 "context" 5 5 "fmt" 6 + "net" 7 + "net/http" 6 8 7 9 "github.com/bluesky-social/indigo/atproto/syntax" 8 10 ) 9 11 12 + // The zero value ('BaseDirectory{}') is a usable Directory. 10 13 type BaseDirectory struct { 14 + // if non-empty, this string should have URL method, hostname, and optional port; it should not have a path or trailing slash 11 15 PLCURL string 16 + // HTTP client used for did:web, did:plc, and HTTP (well-known) handle resolution 17 + HTTPClient http.Client 18 + // DNS resolver used for DNS handle resolution. Calling code can use a custom Dialer to query against a specific DNS server, or re-implement the interface for even more control over the resolution process 19 + Resolver net.Resolver 12 20 } 13 21 14 22 var _ Directory = (*BaseDirectory)(nil) 15 - 16 - func NewBaseDirectory(plcURL string) BaseDirectory { 17 - if plcURL == "" { 18 - plcURL = DefaultPLCURL 19 - } 20 - return BaseDirectory{ 21 - PLCURL: plcURL, 22 - } 23 - } 24 23 25 24 func (d *BaseDirectory) LookupHandle(ctx context.Context, h syntax.Handle) (*Identity, error) { 26 25 did, err := d.ResolveHandle(ctx, h)
+3 -3
atproto/identity/cmd/atp-id/main.go
··· 50 50 } 51 51 fmt.Printf("valid at-identifier syntax: %s\n", id) 52 52 53 - ndir := identity.NewBaseDirectory("https://plc.directory") 53 + ndir := identity.BaseDirectory{} 54 54 55 55 acc, err := ndir.Lookup(ctx, *id) 56 56 if err != nil { ··· 73 73 } 74 74 fmt.Printf("valid handle syntax: %s\n", handle) 75 75 76 - d := identity.NewBaseDirectory(identity.DefaultPLCURL) 76 + d := identity.BaseDirectory{} 77 77 did, err := d.ResolveHandle(ctx, handle) 78 78 if err != nil { 79 79 return err ··· 97 97 } 98 98 fmt.Printf("valid DID syntax: %s\n", did) 99 99 100 - d := identity.NewBaseDirectory(identity.DefaultPLCURL) 100 + d := identity.BaseDirectory{} 101 101 doc, err := d.ResolveDID(ctx, did) 102 102 if err != nil { 103 103 return err
+6 -3
atproto/identity/did.go
··· 37 37 case "web": 38 38 return d.ResolveDIDWeb(ctx, did) 39 39 case "plc": 40 - return d.ResolveDIDPLC(ctx, DefaultPLCURL, did) 40 + return d.ResolveDIDPLC(ctx, did) 41 41 default: 42 42 return nil, fmt.Errorf("DID method not supported: %s", did.Method()) 43 43 } ··· 83 83 return &doc, nil 84 84 } 85 85 86 - // plcURL should have URL method, hostname, and optional port; it should not have a path or trailing slash 87 - func (d *BaseDirectory) ResolveDIDPLC(ctx context.Context, plcURL string, did syntax.DID) (*DIDDocument, error) { 86 + func (d *BaseDirectory) ResolveDIDPLC(ctx context.Context, did syntax.DID) (*DIDDocument, error) { 88 87 if did.Method() != "plc" { 89 88 return nil, fmt.Errorf("expected a did:plc, got: %s", did) 90 89 } 91 90 91 + plcURL := d.PLCURL 92 + if plcURL == "" { 93 + plcURL = DefaultPLCURL 94 + } 92 95 resp, err := http.Get(plcURL + "/" + did.String()) 93 96 if err != nil { 94 97 return nil, fmt.Errorf("failed did:plc directory resolution: %w", err)
+34
atproto/identity/did_test.go
··· 44 44 assert.Equal("atproto.com", hdl.String()) 45 45 } 46 46 } 47 + 48 + func TestDIDDocFeedGenParse(t *testing.T) { 49 + assert := assert.New(t) 50 + f, err := os.Open("testdata/did_web_doc.json") 51 + if err != nil { 52 + t.Fatal(err) 53 + } 54 + defer f.Close() 55 + 56 + docBytes, err := io.ReadAll(f) 57 + if err != nil { 58 + t.Fatal(err) 59 + } 60 + 61 + var doc DIDDocument 62 + err = json.Unmarshal(docBytes, &doc) 63 + assert.NoError(err) 64 + 65 + id := ParseIdentity(&doc) 66 + 67 + assert.Equal("did:web:discover.bsky.social", id.DID.String()) 68 + assert.Equal([]string{}, id.AlsoKnownAs) 69 + pk, err := id.PublicKey() 70 + assert.Error(err) 71 + assert.Equal(ErrKeyNotFound, err) 72 + assert.Nil(pk) 73 + assert.Equal("", id.PDSEndpoint()) 74 + hdl, err := id.DeclaredHandle() 75 + assert.Error(err) 76 + assert.Empty(hdl) 77 + svc, ok := id.Services["bsky_fg"] 78 + assert.True(ok) 79 + assert.Equal("https://discover.bsky.social", svc.URL) 80 + }
+4 -10
atproto/identity/handle.go
··· 12 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 13 ) 14 14 15 - // Does not cross-verify, just does the handle resolution step. 15 + // Does not cross-verify, only does the handle resolution step. 16 16 func (d *BaseDirectory) ResolveHandleDNS(ctx context.Context, handle syntax.Handle) (syntax.DID, error) { 17 - // TODO: timeout 18 - // TODO: mechanism to control NDS better; context? separate method? 19 - 20 - res, err := net.LookupTXT("_atproto." + handle.String()) 17 + res, err := d.Resolver.LookupTXT(ctx, "_atproto."+handle.String()) 21 18 // look for NXDOMAIN 22 19 var dnsErr *net.DNSError 23 20 if errors.As(err, &dnsErr) { ··· 43 40 } 44 41 45 42 func (d *BaseDirectory) ResolveHandleWellKnown(ctx context.Context, handle syntax.Handle) (syntax.DID, error) { 46 - // TODO: timeout 47 - // TODO: could pull a client or transport from context? 48 - c := http.DefaultClient 49 - 50 43 req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://%s/.well-known/atproto-did", handle), nil) 51 44 if err != nil { 52 45 return "", err 53 46 } 54 47 55 - resp, err := c.Do(req) 48 + resp, err := d.HTTPClient.Do(req) 56 49 if err != nil { 57 50 // look for NXDOMAIN 58 51 var dnsErr *net.DNSError ··· 80 73 } 81 74 82 75 func (d *BaseDirectory) ResolveHandle(ctx context.Context, handle syntax.Handle) (syntax.DID, error) { 76 + // TODO: *could* do resolution in parallel, but expecting that sequential is sufficient to start 83 77 did, dnsErr := d.ResolveHandleDNS(ctx, handle) 84 78 if dnsErr == nil { 85 79 return did, nil
+12 -4
atproto/identity/identity.go
··· 8 8 "net/http" 9 9 "net/url" 10 10 "strings" 11 + "time" 11 12 12 13 "github.com/bluesky-social/indigo/atproto/crypto" 13 14 "github.com/bluesky-social/indigo/atproto/syntax" ··· 44 45 45 46 var DefaultPLCURL = "https://plc.directory" 46 47 47 - // Returns a reasonable default Directory implementation for most use cases 48 + // Returns a reasonable Directory implementation for applications 48 49 func DefaultDirectory() Directory { 49 50 base := BaseDirectory{ 50 - PLCURL: DefaultPLCURL, 51 - HTTPClient: http.DefaultClient, 52 - Resolver: net.DefaultResolver, 51 + PLCURL: DefaultPLCURL, 52 + HTTPClient: http.Client{ 53 + Timeout: time.Second * 15, 54 + }, 55 + Resolver: net.Resolver{ 56 + Dial: func(ctx context.Context, network, address string) (net.Conn, error) { 57 + d := net.Dialer{Timeout: time.Second * 5} 58 + return d.DialContext(ctx, network, address) 59 + }, 60 + }, 53 61 } 54 62 cached := NewCacheDirectory(&base) 55 63 return &cached
+2 -2
atproto/identity/live_test.go
··· 51 51 52 52 func TestBaseDirectory(t *testing.T) { 53 53 // XXX: t.Skip("skipping live network test") 54 - d := NewBaseDirectory(DefaultPLCURL) 54 + d := BaseDirectory{} 55 55 testDirectoryLive(t, &d) 56 56 } 57 57 58 58 func TestCacheDirectory(t *testing.T) { 59 59 // XXX: t.Skip("skipping live network test") 60 - inner := NewBaseDirectory(DefaultPLCURL) 60 + inner := BaseDirectory{} 61 61 d := NewCacheDirectory(&inner) 62 62 for i := 0; i < 3; i = i + 1 { 63 63 testDirectoryLive(t, &d)