...

Source file src/github.com/docker/distribution/registry/handlers/catalog.go

Documentation: github.com/docker/distribution/registry/handlers

     1  package handlers
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net/http"
     8  	"net/url"
     9  	"strconv"
    10  
    11  	"github.com/docker/distribution/registry/api/errcode"
    12  	v2 "github.com/docker/distribution/registry/api/v2"
    13  	"github.com/docker/distribution/registry/storage/driver"
    14  
    15  	"github.com/gorilla/handlers"
    16  )
    17  
    18  const defaultReturnedEntries = 100
    19  
    20  func catalogDispatcher(ctx *Context, r *http.Request) http.Handler {
    21  	catalogHandler := &catalogHandler{
    22  		Context: ctx,
    23  	}
    24  
    25  	return handlers.MethodHandler{
    26  		"GET": http.HandlerFunc(catalogHandler.GetCatalog),
    27  	}
    28  }
    29  
    30  type catalogHandler struct {
    31  	*Context
    32  }
    33  
    34  type catalogAPIResponse struct {
    35  	Repositories []string `json:"repositories"`
    36  }
    37  
    38  func (ch *catalogHandler) GetCatalog(w http.ResponseWriter, r *http.Request) {
    39  	var moreEntries = true
    40  
    41  	q := r.URL.Query()
    42  	lastEntry := q.Get("last")
    43  
    44  	entries := defaultReturnedEntries
    45  	maximumConfiguredEntries := ch.App.Config.Catalog.MaxEntries
    46  
    47  	// parse n, if n unparseable, or negative assign it to defaultReturnedEntries
    48  	if n := q.Get("n"); n != "" {
    49  		parsedMax, err := strconv.Atoi(n)
    50  		if err == nil {
    51  			if parsedMax > maximumConfiguredEntries {
    52  				ch.Errors = append(ch.Errors, v2.ErrorCodePaginationNumberInvalid.WithDetail(map[string]int{"n": parsedMax}))
    53  				return
    54  			} else if parsedMax >= 0 {
    55  				entries = parsedMax
    56  			}
    57  		}
    58  	}
    59  
    60  	// then enforce entries to be between 0 & maximumConfiguredEntries
    61  	// max(0, min(entries, maximumConfiguredEntries))
    62  	if entries < 0 || entries > maximumConfiguredEntries {
    63  		entries = maximumConfiguredEntries
    64  	}
    65  
    66  	repos := make([]string, entries)
    67  	filled := 0
    68  
    69  	// entries is guaranteed to be >= 0 and < maximumConfiguredEntries
    70  	if entries == 0 {
    71  		moreEntries = false
    72  	} else {
    73  		returnedRepositories, err := ch.App.registry.Repositories(ch.Context, repos, lastEntry)
    74  		if err != nil {
    75  			_, pathNotFound := err.(driver.PathNotFoundError)
    76  			if err != io.EOF && !pathNotFound {
    77  				ch.Errors = append(ch.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
    78  				return
    79  			}
    80  			// err is either io.EOF or not PathNotFoundError
    81  			moreEntries = false
    82  		}
    83  		filled = returnedRepositories
    84  	}
    85  
    86  	w.Header().Set("Content-Type", "application/json; charset=utf-8")
    87  
    88  	// Add a link header if there are more entries to retrieve
    89  	if moreEntries {
    90  		lastEntry = repos[filled-1]
    91  		urlStr, err := createLinkEntry(r.URL.String(), entries, lastEntry)
    92  		if err != nil {
    93  			ch.Errors = append(ch.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
    94  			return
    95  		}
    96  		w.Header().Set("Link", urlStr)
    97  	}
    98  
    99  	enc := json.NewEncoder(w)
   100  	if err := enc.Encode(catalogAPIResponse{
   101  		Repositories: repos[0:filled],
   102  	}); err != nil {
   103  		ch.Errors = append(ch.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
   104  		return
   105  	}
   106  }
   107  
   108  // Use the original URL from the request to create a new URL for
   109  // the link header
   110  func createLinkEntry(origURL string, maxEntries int, lastEntry string) (string, error) {
   111  	calledURL, err := url.Parse(origURL)
   112  	if err != nil {
   113  		return "", err
   114  	}
   115  
   116  	v := url.Values{}
   117  	v.Add("n", strconv.Itoa(maxEntries))
   118  	v.Add("last", lastEntry)
   119  
   120  	calledURL.RawQuery = v.Encode()
   121  
   122  	calledURL.Fragment = ""
   123  	urlStr := fmt.Sprintf("<%s>; rel=\"next\"", calledURL.String())
   124  
   125  	return urlStr, nil
   126  }
   127  

View as plain text