mirror of
https://github.com/go-gitea/gitea
synced 2024-12-23 00:54:28 +00:00
580e21dd2e
Gitea instance keeps reporting a lot of errors like "LFS SSH transfer connection denied, pure SSH protocol is disabled". When starting debugging the problem, there are more problems found. Try to address most of them: * avoid unnecessary server side error logs (change `fail()` to not log them) * figure out the broken tests/user2/lfs.git (added comments) * avoid `migratePushMirrors` failure when a repository doesn't exist (ignore them) * avoid "Authorization" (internal&lfs) header conflicts, remove the tricky "swapAuth" and use "X-Gitea-Internal-Auth" * make internal token comparing constant time (it wasn't a serous problem because in a real world it's nearly impossible to timing-attack the token, but good to fix and backport) * avoid duplicate routers (introduce AddOwnerRepoGitLFSRoutes) * avoid "internal (private)" routes using session/web context (they should use private context) * fix incorrect "path" usages (use "filepath") * fix incorrect mocked route point handling (need to check func nil correctly) * split some tests from "git general tests" to "git misc tests" (to keep "git_general_test.go" simple) Still no correct result for Git LFS SSH tests. So the code is kept there (`tests/integration/git_lfs_ssh_test.go`) and a FIXME explains the details.
142 lines
3.1 KiB
Go
142 lines
3.1 KiB
Go
// Copyright 2024 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package backend
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
|
|
"code.gitea.io/gitea/modules/httplib"
|
|
"code.gitea.io/gitea/modules/proxyprotocol"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
|
|
"github.com/charmbracelet/git-lfs-transfer/transfer"
|
|
)
|
|
|
|
// HTTP headers
|
|
const (
|
|
headerAccept = "Accept"
|
|
headerAuthorization = "Authorization"
|
|
headerGiteaInternalAuth = "X-Gitea-Internal-Auth"
|
|
headerContentType = "Content-Type"
|
|
headerContentLength = "Content-Length"
|
|
)
|
|
|
|
// MIME types
|
|
const (
|
|
mimeGitLFS = "application/vnd.git-lfs+json"
|
|
mimeOctetStream = "application/octet-stream"
|
|
)
|
|
|
|
// SSH protocol action keys
|
|
const (
|
|
actionDownload = "download"
|
|
actionUpload = "upload"
|
|
actionVerify = "verify"
|
|
)
|
|
|
|
// SSH protocol argument keys
|
|
const (
|
|
argCursor = "cursor"
|
|
argExpiresAt = "expires-at"
|
|
argID = "id"
|
|
argLimit = "limit"
|
|
argPath = "path"
|
|
argRefname = "refname"
|
|
argToken = "token"
|
|
argTransfer = "transfer"
|
|
)
|
|
|
|
// Default username constants
|
|
const (
|
|
userSelf = "(self)"
|
|
userUnknown = "(unknown)"
|
|
)
|
|
|
|
// Operations enum
|
|
const (
|
|
opNone = iota
|
|
opDownload
|
|
opUpload
|
|
)
|
|
|
|
var opMap = map[string]int{
|
|
"download": opDownload,
|
|
"upload": opUpload,
|
|
}
|
|
|
|
var ErrMissingID = fmt.Errorf("%w: missing id arg", transfer.ErrMissingData)
|
|
|
|
func statusCodeToErr(code int) error {
|
|
switch code {
|
|
case http.StatusBadRequest:
|
|
return transfer.ErrParseError
|
|
case http.StatusConflict:
|
|
return transfer.ErrConflict
|
|
case http.StatusForbidden:
|
|
return transfer.ErrForbidden
|
|
case http.StatusNotFound:
|
|
return transfer.ErrNotFound
|
|
case http.StatusUnauthorized:
|
|
return transfer.ErrUnauthorized
|
|
default:
|
|
return fmt.Errorf("server returned status %v: %v", code, http.StatusText(code))
|
|
}
|
|
}
|
|
|
|
func newInternalRequest(ctx context.Context, url, method string, headers map[string]string, body []byte) *httplib.Request {
|
|
req := httplib.NewRequest(url, method).
|
|
SetContext(ctx).
|
|
SetTimeout(10*time.Second, 60*time.Second).
|
|
SetTLSClientConfig(&tls.Config{
|
|
InsecureSkipVerify: true,
|
|
})
|
|
|
|
if setting.Protocol == setting.HTTPUnix {
|
|
req.SetTransport(&http.Transport{
|
|
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
|
var d net.Dialer
|
|
conn, err := d.DialContext(ctx, "unix", setting.HTTPAddr)
|
|
if err != nil {
|
|
return conn, err
|
|
}
|
|
if setting.LocalUseProxyProtocol {
|
|
if err = proxyprotocol.WriteLocalHeader(conn); err != nil {
|
|
_ = conn.Close()
|
|
return nil, err
|
|
}
|
|
}
|
|
return conn, err
|
|
},
|
|
})
|
|
} else if setting.LocalUseProxyProtocol {
|
|
req.SetTransport(&http.Transport{
|
|
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
|
|
var d net.Dialer
|
|
conn, err := d.DialContext(ctx, network, address)
|
|
if err != nil {
|
|
return conn, err
|
|
}
|
|
if err = proxyprotocol.WriteLocalHeader(conn); err != nil {
|
|
_ = conn.Close()
|
|
return nil, err
|
|
}
|
|
return conn, err
|
|
},
|
|
})
|
|
}
|
|
|
|
for k, v := range headers {
|
|
req.Header(k, v)
|
|
}
|
|
|
|
req.Body(body)
|
|
|
|
return req
|
|
}
|