···3333 //
3434 // The intended use-case for this flag is as an optimization for services which do not care about handles, but still want to use the `Directory` interface (instead of `ResolveDID`). For example, relay implementations, or services validating inter-service auth requests.
3535 SkipHandleVerification bool
3636+ // User-Agent header for HTTP requests. Optional (ignored if empty string).
3737+ UserAgent string
3638}
37393840var _ Directory = (*BaseDirectory)(nil)
···107109 return nil, fmt.Errorf("at-identifier neither a Handle nor a DID")
108110}
109111110110-func (d *BaseDirectory) Purge(ctx context.Context, a syntax.AtIdentifier) error {
112112+func (d *BaseDirectory) Purge(ctx context.Context, atid syntax.AtIdentifier) error {
111113 // BaseDirectory itself does not implement caching
112114 return nil
113115}
+3-3
atproto/identity/cache_directory.go
···250250 return nil, fmt.Errorf("at-identifier neither a Handle nor a DID")
251251}
252252253253-func (d *CacheDirectory) Purge(ctx context.Context, a syntax.AtIdentifier) error {
254254- handle, err := a.AsHandle()
253253+func (d *CacheDirectory) Purge(ctx context.Context, atid syntax.AtIdentifier) error {
254254+ handle, err := atid.AsHandle()
255255 if nil == err { // if not an error, is a handle
256256 handle = handle.Normalize()
257257 d.handleCache.Remove(handle)
258258 return nil
259259 }
260260- did, err := a.AsDID()
260260+ did, err := atid.AsDID()
261261 if nil == err { // if not an error, is a DID
262262 d.identityCache.Remove(did)
263263 return nil
···88 "time"
991010 "github.com/bluesky-social/indigo/atproto/syntax"
1111+1212+ "github.com/carlmjohnson/versioninfo"
1113)
12141315// Ergonomic interface for atproto identity lookup, by DID or handle.
···2729 Lookup(ctx context.Context, atid syntax.AtIdentifier) (*Identity, error)
28302931 // Flushes any cache of the indicated identifier. If directory is not using caching, can ignore this.
3030- Purge(ctx context.Context, i syntax.AtIdentifier) error
3232+ Purge(ctx context.Context, atid syntax.AtIdentifier) error
3133}
32343335// Indicates that handle resolution failed. A wrapped error may provide more context. This is only returned when looking up a handle, not when looking up a DID.
···8082 TryAuthoritativeDNS: true,
8183 // primary Bluesky PDS instance only supports HTTP resolution method
8284 SkipDNSDomainSuffixes: []string{".bsky.social"},
8585+ UserAgent: "indigo-identity/" + versioninfo.Short(),
8386 }
8487 cached := NewCacheDirectory(&base, 250_000, time.Hour*24, time.Minute*2, time.Minute*5)
8588 return &cached
+9-4
atproto/identity/handle.go
···131131 if err != nil {
132132 return "", fmt.Errorf("constructing HTTP request for handle resolution: %w", err)
133133 }
134134+ if d.UserAgent != "" {
135135+ req.Header.Set("User-Agent", d.UserAgent)
136136+ }
134137135138 resp, err := d.HTTPClient.Do(req)
136139 if err != nil {
···144147 return "", fmt.Errorf("%w: HTTP well-known request error: %w", ErrHandleResolutionFailed, err)
145148 }
146149 defer resp.Body.Close()
150150+ if resp.ContentLength > 2048 {
151151+ // NOTE: intentionally not draining body
152152+ return "", fmt.Errorf("%w: HTTP well-known body too large for %s", ErrHandleResolutionFailed, handle)
153153+ }
147154 if resp.StatusCode == http.StatusNotFound {
155155+ io.Copy(io.Discard, resp.Body)
148156 return "", fmt.Errorf("%w: HTTP 404 for %s", ErrHandleNotFound, handle)
149157 }
150158 if resp.StatusCode != http.StatusOK {
159159+ io.Copy(io.Discard, resp.Body)
151160 return "", fmt.Errorf("%w: HTTP well-known status %d for %s", ErrHandleResolutionFailed, resp.StatusCode, handle)
152152- }
153153-154154- if resp.ContentLength > 2048 {
155155- return "", fmt.Errorf("%w: HTTP well-known body too large for %s", ErrHandleResolutionFailed, handle)
156161 }
157162158163 b, err := io.ReadAll(io.LimitReader(resp.Body, 2048))