[full-ci] chore: bump reva to v2.43.0 (#2630)
This commit is contained in:
+147
-21
@@ -7,7 +7,6 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
@@ -24,6 +23,33 @@ import (
|
||||
"github.com/go-git/go-git/v5/utils/ioutil"
|
||||
)
|
||||
|
||||
type contextKey int
|
||||
|
||||
const initialRequestKey contextKey = iota
|
||||
|
||||
// RedirectPolicy controls how the HTTP transport follows redirects.
|
||||
//
|
||||
// The values mirror Git's http.followRedirects config:
|
||||
// "true" follows redirects for all requests, "false" treats redirects as
|
||||
// errors, and "initial" follows redirects only for the initial
|
||||
// /info/refs discovery request. The zero value defaults to "initial".
|
||||
type RedirectPolicy string
|
||||
|
||||
const (
|
||||
FollowInitialRedirects RedirectPolicy = "initial"
|
||||
FollowRedirects RedirectPolicy = "true"
|
||||
NoFollowRedirects RedirectPolicy = "false"
|
||||
)
|
||||
|
||||
func withInitialRequest(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, initialRequestKey, true)
|
||||
}
|
||||
|
||||
func isInitialRequest(req *http.Request) bool {
|
||||
v, _ := req.Context().Value(initialRequestKey).(bool)
|
||||
return v
|
||||
}
|
||||
|
||||
// it requires a bytes.Buffer, because we need to know the length
|
||||
func applyHeadersToRequest(req *http.Request, content *bytes.Buffer, host string, requestType string) {
|
||||
req.Header.Add("User-Agent", capability.DefaultAgent())
|
||||
@@ -54,12 +80,15 @@ func advertisedReferences(ctx context.Context, s *session, serviceName string) (
|
||||
|
||||
s.ApplyAuthToRequest(req)
|
||||
applyHeadersToRequest(req, nil, s.endpoint.Host, serviceName)
|
||||
res, err := s.client.Do(req.WithContext(ctx))
|
||||
res, err := s.client.Do(req.WithContext(withInitialRequest(ctx)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.ModifyEndpointIfRedirect(res)
|
||||
if err := s.ModifyEndpointIfRedirect(res); err != nil {
|
||||
_ = res.Body.Close()
|
||||
return nil, err
|
||||
}
|
||||
defer ioutil.CheckClose(res.Body, &err)
|
||||
|
||||
if err = NewErr(res); err != nil {
|
||||
@@ -96,6 +125,7 @@ type client struct {
|
||||
client *http.Client
|
||||
transports *lru.Cache
|
||||
mutex sync.RWMutex
|
||||
follow RedirectPolicy
|
||||
}
|
||||
|
||||
// ClientOptions holds user configurable options for the client.
|
||||
@@ -106,6 +136,11 @@ type ClientOptions struct {
|
||||
// size, will result in the least recently used transport getting deleted
|
||||
// before the provided transport is added to the cache.
|
||||
CacheMaxEntries int
|
||||
|
||||
// RedirectPolicy controls redirect handling. Supported values are
|
||||
// "true", "false", and "initial". The zero value defaults to
|
||||
// "initial", matching Git's http.followRedirects default.
|
||||
RedirectPolicy RedirectPolicy
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -150,12 +185,16 @@ func NewClientWithOptions(c *http.Client, opts *ClientOptions) transport.Transpo
|
||||
}
|
||||
cl := &client{
|
||||
client: c,
|
||||
follow: FollowInitialRedirects,
|
||||
}
|
||||
|
||||
if opts != nil {
|
||||
if opts.CacheMaxEntries > 0 {
|
||||
cl.transports = lru.New(opts.CacheMaxEntries)
|
||||
}
|
||||
if opts.RedirectPolicy != "" {
|
||||
cl.follow = opts.RedirectPolicy
|
||||
}
|
||||
}
|
||||
return cl
|
||||
}
|
||||
@@ -289,14 +328,9 @@ func newSession(c *client, ep *transport.Endpoint, auth transport.AuthMethod) (*
|
||||
}
|
||||
}
|
||||
|
||||
httpClient = &http.Client{
|
||||
Transport: transport,
|
||||
CheckRedirect: c.client.CheckRedirect,
|
||||
Jar: c.client.Jar,
|
||||
Timeout: c.client.Timeout,
|
||||
}
|
||||
httpClient = c.cloneHTTPClient(transport)
|
||||
} else {
|
||||
httpClient = c.client
|
||||
httpClient = c.cloneHTTPClient(c.client.Transport)
|
||||
}
|
||||
|
||||
s := &session{
|
||||
@@ -324,30 +358,122 @@ func (s *session) ApplyAuthToRequest(req *http.Request) {
|
||||
s.auth.SetAuth(req)
|
||||
}
|
||||
|
||||
func (s *session) ModifyEndpointIfRedirect(res *http.Response) {
|
||||
func (s *session) ModifyEndpointIfRedirect(res *http.Response) error {
|
||||
if res.Request == nil {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
if s.endpoint == nil {
|
||||
return fmt.Errorf("http redirect: nil endpoint")
|
||||
}
|
||||
|
||||
r := res.Request
|
||||
if !strings.HasSuffix(r.URL.Path, infoRefsPath) {
|
||||
return
|
||||
return fmt.Errorf("http redirect: target %q does not end with %s", r.URL.Path, infoRefsPath)
|
||||
}
|
||||
if r.URL.Scheme != "http" && r.URL.Scheme != "https" {
|
||||
return fmt.Errorf("http redirect: unsupported scheme %q", r.URL.Scheme)
|
||||
}
|
||||
if r.URL.Scheme != s.endpoint.Protocol &&
|
||||
!(s.endpoint.Protocol == "http" && r.URL.Scheme == "https") {
|
||||
return fmt.Errorf("http redirect: changes scheme from %q to %q", s.endpoint.Protocol, r.URL.Scheme)
|
||||
}
|
||||
|
||||
h, p, err := net.SplitHostPort(r.URL.Host)
|
||||
host := endpointHost(r.URL.Hostname())
|
||||
port, err := endpointPort(r.URL.Port())
|
||||
if err != nil {
|
||||
h = r.URL.Host
|
||||
return err
|
||||
}
|
||||
if p != "" {
|
||||
port, err := strconv.Atoi(p)
|
||||
if err == nil {
|
||||
s.endpoint.Port = port
|
||||
}
|
||||
|
||||
if host != s.endpoint.Host || effectivePort(r.URL.Scheme, port) != effectivePort(s.endpoint.Protocol, s.endpoint.Port) {
|
||||
s.endpoint.User = ""
|
||||
s.endpoint.Password = ""
|
||||
s.auth = nil
|
||||
}
|
||||
s.endpoint.Host = h
|
||||
|
||||
s.endpoint.Host = host
|
||||
s.endpoint.Port = port
|
||||
|
||||
s.endpoint.Protocol = r.URL.Scheme
|
||||
s.endpoint.Path = r.URL.Path[:len(r.URL.Path)-len(infoRefsPath)]
|
||||
return nil
|
||||
}
|
||||
|
||||
func endpointHost(host string) string {
|
||||
if strings.Contains(host, ":") {
|
||||
return "[" + host + "]"
|
||||
}
|
||||
|
||||
return host
|
||||
}
|
||||
|
||||
func endpointPort(port string) (int, error) {
|
||||
if port == "" {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
parsed, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("http redirect: invalid port %q", port)
|
||||
}
|
||||
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func effectivePort(scheme string, port int) int {
|
||||
if port != 0 {
|
||||
return port
|
||||
}
|
||||
|
||||
switch strings.ToLower(scheme) {
|
||||
case "http":
|
||||
return 80
|
||||
case "https":
|
||||
return 443
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) cloneHTTPClient(transport http.RoundTripper) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: transport,
|
||||
CheckRedirect: wrapCheckRedirect(c.follow, c.client.CheckRedirect),
|
||||
Jar: c.client.Jar,
|
||||
Timeout: c.client.Timeout,
|
||||
}
|
||||
}
|
||||
|
||||
func wrapCheckRedirect(policy RedirectPolicy, next func(*http.Request, []*http.Request) error) func(*http.Request, []*http.Request) error {
|
||||
return func(req *http.Request, via []*http.Request) error {
|
||||
if err := checkRedirect(req, via, policy); err != nil {
|
||||
return err
|
||||
}
|
||||
if next != nil {
|
||||
return next(req, via)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func checkRedirect(req *http.Request, via []*http.Request, policy RedirectPolicy) error {
|
||||
switch policy {
|
||||
case FollowRedirects:
|
||||
case NoFollowRedirects:
|
||||
return fmt.Errorf("http redirect: redirects disabled to %s", req.URL)
|
||||
case "", FollowInitialRedirects:
|
||||
if !isInitialRequest(req) {
|
||||
return fmt.Errorf("http redirect: redirect on non-initial request to %s", req.URL)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("http redirect: invalid redirect policy %q", policy)
|
||||
}
|
||||
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
|
||||
return fmt.Errorf("http redirect: unsupported scheme %q", req.URL.Scheme)
|
||||
}
|
||||
if len(via) >= 10 {
|
||||
return fmt.Errorf("http redirect: too many redirects")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*session) Close() error {
|
||||
|
||||
+45
-12
@@ -132,30 +132,64 @@ func (w *PackWriter) clean() error {
|
||||
|
||||
func (w *PackWriter) save() error {
|
||||
base := w.fs.Join(objectsPath, packPath, fmt.Sprintf("pack-%s", w.checksum))
|
||||
idx, err := w.fs.Create(fmt.Sprintf("%s.idx", base))
|
||||
|
||||
// Pack files are content addressable. Each file is checked
|
||||
// individually — if it already exists on disk, skip creating it.
|
||||
idxPath := fmt.Sprintf("%s.idx", base)
|
||||
exists, err := fileExists(w.fs, idxPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
idx, err := w.fs.Create(idxPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := w.encodeIdx(idx); err != nil {
|
||||
_ = idx.Close()
|
||||
return err
|
||||
}
|
||||
if err := w.encodeIdx(idx); err != nil {
|
||||
_ = idx.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := idx.Close(); err != nil {
|
||||
return err
|
||||
if err := idx.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
fixPermissions(w.fs, idxPath)
|
||||
}
|
||||
fixPermissions(w.fs, fmt.Sprintf("%s.idx", base))
|
||||
|
||||
packPath := fmt.Sprintf("%s.pack", base)
|
||||
if err := w.fs.Rename(w.fw.Name(), packPath); err != nil {
|
||||
exists, err = fileExists(w.fs, packPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fixPermissions(w.fs, packPath)
|
||||
if !exists {
|
||||
if err := w.fs.Rename(w.fw.Name(), packPath); err != nil {
|
||||
return err
|
||||
}
|
||||
fixPermissions(w.fs, packPath)
|
||||
} else {
|
||||
// Pack already exists, clean up the temp file.
|
||||
return w.clean()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fileExists checks whether path already exists as a regular file.
|
||||
// It returns (true, nil) for an existing regular file, (false, nil) when the
|
||||
// path does not exist, and (false, err) if the path exists but is not a
|
||||
// regular file (e.g. a directory or symlink).
|
||||
func fileExists(fs billy.Filesystem, path string) (bool, error) {
|
||||
fi, err := fs.Lstat(path)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
if !fi.Mode().IsRegular() {
|
||||
return false, fmt.Errorf("unexpected file type for %q: %s", path, fi.Mode().Type())
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (w *PackWriter) encodeIdx(writer io.Writer) error {
|
||||
idx, err := w.writer.Index()
|
||||
if err != nil {
|
||||
@@ -235,7 +269,6 @@ func (s *syncedReader) sleep() {
|
||||
atomic.StoreUint32(&s.blocked, 1)
|
||||
<-s.news
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *syncedReader) Seek(offset int64, whence int) (int64, error) {
|
||||
@@ -293,7 +326,7 @@ func (w *ObjectWriter) save() error {
|
||||
// Loose objects are content addressable, if they already exist
|
||||
// we can safely delete the temporary file and short-circuit the
|
||||
// operation.
|
||||
if _, err := w.fs.Stat(file); err == nil || os.IsExist(err) {
|
||||
if _, err := w.fs.Lstat(file); err == nil || os.IsExist(err) {
|
||||
return w.fs.Remove(w.f.Name())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user