@@ -3,8 +3,11 @@ package handlers
33import (
44 "context"
55 "encoding/json"
6+ "fmt"
67 "net/http"
8+ "net/url"
79 "path"
10+ "strconv"
811
912 "github.com/distribution/distribution/v3"
1013 dcontext "github.com/distribution/distribution/v3/context"
@@ -20,6 +23,12 @@ import (
2023 v1 "github.com/opencontainers/image-spec/specs-go/v1"
2124)
2225
26+ const (
27+ pageSizeMin = 1
28+ pageSizeDefault = 10
29+ pageSizeMax = 100
30+ )
31+
2332// referrersDispatcher takes the request context and builds the
2433// appropriate handler for handling referrers requests.
2534func referrersDispatcher (ctx * Context , r * http.Request ) http.Handler {
@@ -59,13 +68,30 @@ func (h *referrersHandler) GetReferrers(w http.ResponseWriter, r *http.Request)
5968 return
6069 }
6170
71+ // extract the artifactType filter.
6272 var annotations map [string ]string
6373 var artifactTypeFilter string
6474 if artifactTypeFilter = r .URL .Query ().Get ("artifactType" ); artifactTypeFilter != "" {
6575 annotations = map [string ]string {
6676 v1 .AnnotationReferrersFiltersApplied : "artifactType" ,
6777 }
6878 }
79+
80+ // extract the page size info. Users define page size by using the n
81+ // query parameter. The default page size is 10.
82+ pageSize , err := strconv .Atoi (r .URL .Query ().Get ("n" ))
83+ if err != nil || pageSize < pageSizeMin || pageSize > pageSizeMax {
84+ pageSize = pageSizeDefault
85+ }
86+
87+ // extract the page number info.
88+ pageNumber , err := strconv .Atoi (r .URL .Query ().Get ("p" ))
89+ if err != nil || pageNumber < 0 {
90+ pageNumber = 0
91+ }
92+
93+ // currently, pagination would call generateReferrersList multiple times
94+ // and this is not efficient. This implementation is for testing purpose.
6995 referrers , err := h .generateReferrersList (h , h .Digest , artifactTypeFilter )
7096 if err != nil {
7197 if _ , ok := err .(distribution.ErrManifestUnknownRevision ); ok {
@@ -76,8 +102,16 @@ func (h *referrersHandler) GetReferrers(w http.ResponseWriter, r *http.Request)
76102 return
77103 }
78104
79- if referrers == nil {
105+ startIndex := pageNumber * pageSize
106+
107+ if referrers == nil || startIndex >= len (referrers ) {
80108 referrers = []v1.Descriptor {}
109+ } else if len (referrers )- startIndex <= pageSize {
110+ // only 1 page of results left
111+ referrers = referrers [startIndex :]
112+ } else {
113+ referrers = referrers [startIndex : startIndex + pageSize ]
114+ w .Header ().Set ("Link" , generateLinkHeader (h .Repository .Named ().Name (), h .Digest .String (), artifactTypeFilter , pageSize , pageNumber + 1 ))
81115 }
82116
83117 response := v1.Index {
@@ -195,6 +229,19 @@ func readlink(ctx context.Context, path string, stDriver driver.StorageDriver) (
195229 return digest .Parse (string (content ))
196230}
197231
232+ func generateLinkHeader (repoName , subjectDigest , artifactType string , pageSize int , pageNumber int ) string {
233+ linkURL := fmt .Sprintf ("/v2/%s/referrers/%s" , repoName , subjectDigest )
234+ v := url.Values {}
235+ v .Add ("p" , strconv .Itoa (pageNumber ))
236+ if artifactType != "" {
237+ v .Add ("artifactType" , artifactType )
238+ }
239+ if pageSize > 0 {
240+ v .Add ("n" , strconv .Itoa (pageSize ))
241+ }
242+ return fmt .Sprintf ("<%s?%s>; rel=\" next\" " , linkURL , v .Encode ())
243+ }
244+
198245func generateReferrerFromArtifact (ctx context.Context ,
199246 blobStatter distribution.BlobStatter ,
200247 referrerDigest digest.Digest ,
0 commit comments