99 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			99 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Go
		
	
	
| package handlers
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"net/url"
 | |
| 	"strconv"
 | |
| 
 | |
| 	"github.com/distribution/distribution/v3/registry/api/errcode"
 | |
| 	"github.com/distribution/distribution/v3/registry/storage/driver"
 | |
| 	"github.com/gorilla/handlers"
 | |
| )
 | |
| 
 | |
| const maximumReturnedEntries = 100
 | |
| 
 | |
| func catalogDispatcher(ctx *Context, r *http.Request) http.Handler {
 | |
| 	catalogHandler := &catalogHandler{
 | |
| 		Context: ctx,
 | |
| 	}
 | |
| 
 | |
| 	return handlers.MethodHandler{
 | |
| 		"GET": http.HandlerFunc(catalogHandler.GetCatalog),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| type catalogHandler struct {
 | |
| 	*Context
 | |
| }
 | |
| 
 | |
| type catalogAPIResponse struct {
 | |
| 	Repositories []string `json:"repositories"`
 | |
| }
 | |
| 
 | |
| func (ch *catalogHandler) GetCatalog(w http.ResponseWriter, r *http.Request) {
 | |
| 	var moreEntries = true
 | |
| 
 | |
| 	q := r.URL.Query()
 | |
| 	lastEntry := q.Get("last")
 | |
| 	maxEntries, err := strconv.Atoi(q.Get("n"))
 | |
| 	if err != nil || maxEntries < 0 {
 | |
| 		maxEntries = maximumReturnedEntries
 | |
| 	}
 | |
| 
 | |
| 	repos := make([]string, maxEntries)
 | |
| 
 | |
| 	filled, err := ch.App.registry.Repositories(ch.Context, repos, lastEntry)
 | |
| 	_, pathNotFound := err.(driver.PathNotFoundError)
 | |
| 
 | |
| 	if err == io.EOF || pathNotFound {
 | |
| 		moreEntries = false
 | |
| 	} else if err != nil {
 | |
| 		ch.Errors = append(ch.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	w.Header().Set("Content-Type", "application/json")
 | |
| 
 | |
| 	// Add a link header if there are more entries to retrieve
 | |
| 	if moreEntries {
 | |
| 		lastEntry = repos[len(repos)-1]
 | |
| 		urlStr, err := createLinkEntry(r.URL.String(), maxEntries, lastEntry)
 | |
| 		if err != nil {
 | |
| 			ch.Errors = append(ch.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
 | |
| 			return
 | |
| 		}
 | |
| 		w.Header().Set("Link", urlStr)
 | |
| 	}
 | |
| 
 | |
| 	enc := json.NewEncoder(w)
 | |
| 	if err := enc.Encode(catalogAPIResponse{
 | |
| 		Repositories: repos[0:filled],
 | |
| 	}); err != nil {
 | |
| 		ch.Errors = append(ch.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
 | |
| 		return
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Use the original URL from the request to create a new URL for
 | |
| // the link header
 | |
| func createLinkEntry(origURL string, maxEntries int, lastEntry string) (string, error) {
 | |
| 	calledURL, err := url.Parse(origURL)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 
 | |
| 	v := url.Values{}
 | |
| 	v.Add("n", strconv.Itoa(maxEntries))
 | |
| 	v.Add("last", lastEntry)
 | |
| 
 | |
| 	calledURL.RawQuery = v.Encode()
 | |
| 
 | |
| 	calledURL.Fragment = ""
 | |
| 	urlStr := fmt.Sprintf("<%s>; rel=\"next\"", calledURL.String())
 | |
| 
 | |
| 	return urlStr, nil
 | |
| }
 |