Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] main from go-gitea:main #745

Merged
merged 3 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions models/issues/review.go
Original file line number Diff line number Diff line change
Expand Up @@ -930,17 +930,19 @@ func MarkConversation(ctx context.Context, comment *Comment, doer *user_model.Us
}

// CanMarkConversation Add or remove Conversation mark for a code comment permission check
// the PR writer , offfcial reviewer and poster can do it
// the PR writer , official reviewer and poster can do it
func CanMarkConversation(ctx context.Context, issue *Issue, doer *user_model.User) (permResult bool, err error) {
if doer == nil || issue == nil {
return false, fmt.Errorf("issue or doer is nil")
}

if err = issue.LoadRepo(ctx); err != nil {
return false, err
}
if issue.Repo.IsArchived {
return false, nil
}
if doer.ID != issue.PosterID {
if err = issue.LoadRepo(ctx); err != nil {
return false, err
}

p, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
if err != nil {
return false, err
Expand Down
13 changes: 10 additions & 3 deletions modules/httplib/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ func (r *Request) Param(key, value string) *Request {
return r
}

// Body adds request raw body.
// it supports string and []byte.
// Body adds request raw body. It supports string, []byte and io.Reader as body.
func (r *Request) Body(data any) *Request {
switch t := data.(type) {
case nil: // do nothing
case string:
bf := bytes.NewBufferString(t)
r.req.Body = io.NopCloser(bf)
Expand All @@ -111,6 +111,12 @@ func (r *Request) Body(data any) *Request {
bf := bytes.NewBuffer(t)
r.req.Body = io.NopCloser(bf)
r.req.ContentLength = int64(len(t))
case io.ReadCloser:
r.req.Body = t
case io.Reader:
r.req.Body = io.NopCloser(t)
default:
panic(fmt.Sprintf("unsupported request body type %T", t))
}
return r
}
Expand Down Expand Up @@ -141,7 +147,7 @@ func (r *Request) getResponse() (*http.Response, error) {
}
} else if r.req.Method == "POST" && r.req.Body == nil && len(paramBody) > 0 {
r.Header("Content-Type", "application/x-www-form-urlencoded")
r.Body(paramBody)
r.Body(paramBody) // string
}

var err error
Expand Down Expand Up @@ -185,6 +191,7 @@ func (r *Request) getResponse() (*http.Response, error) {
}

// Response executes request client gets response manually.
// Caller MUST close the response body if no error occurs
func (r *Request) Response() (*http.Response, error) {
return r.getResponse()
}
Expand Down
8 changes: 6 additions & 2 deletions modules/lfs/http_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,14 @@ func (c *HTTPClient) batch(ctx context.Context, operation string, objects []Poin

url := fmt.Sprintf("%s/objects/batch", c.endpoint)

// Original: In some lfs server implementations, they require the ref attribute. #32838
// `ref` is an "optional object describing the server ref that the objects belong to"
// but some (incorrect) lfs servers require it, so maybe adding an empty ref here doesn't break the correct ones.
// but some (incorrect) lfs servers like aliyun require it, so maybe adding an empty ref here doesn't break the correct ones.
// https://github.com/git-lfs/git-lfs/blob/a32a02b44bf8a511aa14f047627c49e1a7fd5021/docs/api/batch.md?plain=1#L37
request := &BatchRequest{operation, c.transferNames(), &Reference{}, objects}
//
// UPDATE: it can't use "empty ref" here because it breaks others like https://github.com/go-gitea/gitea/issues/33453
request := &BatchRequest{operation, c.transferNames(), nil, objects}

payload := new(bytes.Buffer)
err := json.NewEncoder(payload).Encode(request)
if err != nil {
Expand Down
40 changes: 19 additions & 21 deletions modules/lfstransfer/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package backend

import (
"bytes"
"context"
"encoding/base64"
"fmt"
Expand All @@ -29,7 +28,7 @@ var Capabilities = []string{
"locking",
}

var _ transfer.Backend = &GiteaBackend{}
var _ transfer.Backend = (*GiteaBackend)(nil)

// GiteaBackend is an adapter between git-lfs-transfer library and Gitea's internal LFS API
type GiteaBackend struct {
Expand Down Expand Up @@ -78,17 +77,17 @@ func (g *GiteaBackend) Batch(_ string, pointers []transfer.BatchItem, args trans
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
req := newInternalRequestLFS(g.ctx, url, http.MethodPost, headers, bodyBytes)
resp, err := req.Response()
if err != nil {
g.logger.Log("http request error", err)
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
g.logger.Log("http statuscode error", resp.StatusCode, statusCodeToErr(resp.StatusCode))
return nil, statusCodeToErr(resp.StatusCode)
}
defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body)
if err != nil {
g.logger.Log("http read error", err)
Expand Down Expand Up @@ -158,8 +157,7 @@ func (g *GiteaBackend) Batch(_ string, pointers []transfer.BatchItem, args trans
return pointers, nil
}

// Download implements transfer.Backend. The returned reader must be closed by the
// caller.
// Download implements transfer.Backend. The returned reader must be closed by the caller.
func (g *GiteaBackend) Download(oid string, args transfer.Args) (io.ReadCloser, int64, error) {
idMapStr, exists := args[argID]
if !exists {
Expand Down Expand Up @@ -187,25 +185,25 @@ func (g *GiteaBackend) Download(oid string, args transfer.Args) (io.ReadCloser,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeOctetStream,
}
req := newInternalRequest(g.ctx, url, http.MethodGet, headers, nil)
req := newInternalRequestLFS(g.ctx, url, http.MethodGet, headers, nil)
resp, err := req.Response()
if err != nil {
return nil, 0, err
return nil, 0, fmt.Errorf("failed to get response: %w", err)
}
// no need to close the body here by "defer resp.Body.Close()", see below
if resp.StatusCode != http.StatusOK {
return nil, 0, statusCodeToErr(resp.StatusCode)
}
defer resp.Body.Close()
respBytes, err := io.ReadAll(resp.Body)

respSize, err := strconv.ParseInt(resp.Header.Get("X-Gitea-LFS-Content-Length"), 10, 64)
if err != nil {
return nil, 0, err
return nil, 0, fmt.Errorf("failed to parse content length: %w", err)
}
respSize := int64(len(respBytes))
respBuf := io.NopCloser(bytes.NewBuffer(respBytes))
return respBuf, respSize, nil
// transfer.Backend will check io.Closer interface and close this Body reader
return resp.Body, respSize, nil
}

// StartUpload implements transfer.Backend.
// Upload implements transfer.Backend.
func (g *GiteaBackend) Upload(oid string, size int64, r io.Reader, args transfer.Args) error {
idMapStr, exists := args[argID]
if !exists {
Expand Down Expand Up @@ -234,15 +232,14 @@ func (g *GiteaBackend) Upload(oid string, size int64, r io.Reader, args transfer
headerContentType: mimeOctetStream,
headerContentLength: strconv.FormatInt(size, 10),
}
reqBytes, err := io.ReadAll(r)
if err != nil {
return err
}
req := newInternalRequest(g.ctx, url, http.MethodPut, headers, reqBytes)

req := newInternalRequestLFS(g.ctx, url, http.MethodPut, headers, nil)
req.Body(r)
resp, err := req.Response()
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return statusCodeToErr(resp.StatusCode)
}
Expand Down Expand Up @@ -284,11 +281,12 @@ func (g *GiteaBackend) Verify(oid string, size int64, args transfer.Args) (trans
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
req := newInternalRequestLFS(g.ctx, url, http.MethodPost, headers, bodyBytes)
resp, err := req.Response()
if err != nil {
return transfer.NewStatus(transfer.StatusInternalServerError), err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return transfer.NewStatus(uint32(resp.StatusCode), http.StatusText(resp.StatusCode)), statusCodeToErr(resp.StatusCode)
}
Expand Down
6 changes: 3 additions & 3 deletions modules/lfstransfer/backend/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (g *giteaLockBackend) Create(path, refname string) (transfer.Lock, error) {
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
req := newInternalRequestLFS(g.ctx, url, http.MethodPost, headers, bodyBytes)
resp, err := req.Response()
if err != nil {
g.logger.Log("http request error", err)
Expand Down Expand Up @@ -102,7 +102,7 @@ func (g *giteaLockBackend) Unlock(lock transfer.Lock) error {
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
req := newInternalRequest(g.ctx, url, http.MethodPost, headers, bodyBytes)
req := newInternalRequestLFS(g.ctx, url, http.MethodPost, headers, bodyBytes)
resp, err := req.Response()
if err != nil {
g.logger.Log("http request error", err)
Expand Down Expand Up @@ -185,7 +185,7 @@ func (g *giteaLockBackend) queryLocks(v url.Values) ([]transfer.Lock, string, er
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
req := newInternalRequest(g.ctx, url, http.MethodGet, headers, nil)
req := newInternalRequestLFS(g.ctx, url, http.MethodGet, headers, nil)
resp, err := req.Response()
if err != nil {
g.logger.Log("http request error", err)
Expand Down
63 changes: 13 additions & 50 deletions modules/lfstransfer/backend/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@ package backend

import (
"context"
"crypto/tls"
"fmt"
"net"
"io"
"net/http"
"time"

"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/proxyprotocol"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/private"

"github.com/charmbracelet/git-lfs-transfer/transfer"
)
Expand Down Expand Up @@ -89,53 +86,19 @@ func statusCodeToErr(code int) error {
}
}

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
},
})
}

func newInternalRequestLFS(ctx context.Context, url, method string, headers map[string]string, body any) *httplib.Request {
req := private.NewInternalRequest(ctx, url, method)
for k, v := range headers {
req.Header(k, v)
}

req.Body(body)

switch body := body.(type) {
case nil: // do nothing
case []byte:
req.Body(body) // []byte
case io.Reader:
req.Body(body) // io.Reader or io.ReadCloser
default:
panic(fmt.Sprintf("unsupported request body type %T", body))
}
return req
}
2 changes: 1 addition & 1 deletion modules/private/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type GenerateTokenRequest struct {
func GenerateActionsRunnerToken(ctx context.Context, scope string) (*ResponseText, ResponseExtra) {
reqURL := setting.LocalURL + "api/internal/actions/generate_actions_runner_token"

req := newInternalRequest(ctx, reqURL, "POST", GenerateTokenRequest{
req := newInternalRequestAPI(ctx, reqURL, "POST", GenerateTokenRequest{
Scope: scope,
})

Expand Down
10 changes: 5 additions & 5 deletions modules/private/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ type HookProcReceiveRefResult struct {
// HookPreReceive check whether the provided commits are allowed
func HookPreReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) ResponseExtra {
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName))
req := newInternalRequest(ctx, reqURL, "POST", opts)
req := newInternalRequestAPI(ctx, reqURL, "POST", opts)
req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second)
_, extra := requestJSONResp(req, &ResponseText{})
return extra
Expand All @@ -94,7 +94,7 @@ func HookPreReceive(ctx context.Context, ownerName, repoName string, opts HookOp
// HookPostReceive updates services and users
func HookPostReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (*HookPostReceiveResult, ResponseExtra) {
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/post-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName))
req := newInternalRequest(ctx, reqURL, "POST", opts)
req := newInternalRequestAPI(ctx, reqURL, "POST", opts)
req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second)
return requestJSONResp(req, &HookPostReceiveResult{})
}
Expand All @@ -103,7 +103,7 @@ func HookPostReceive(ctx context.Context, ownerName, repoName string, opts HookO
func HookProcReceive(ctx context.Context, ownerName, repoName string, opts HookOptions) (*HookProcReceiveResult, ResponseExtra) {
reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/proc-receive/%s/%s", url.PathEscape(ownerName), url.PathEscape(repoName))

req := newInternalRequest(ctx, reqURL, "POST", opts)
req := newInternalRequestAPI(ctx, reqURL, "POST", opts)
req.SetReadWriteTimeout(time.Duration(60+len(opts.OldCommitIDs)) * time.Second)
return requestJSONResp(req, &HookProcReceiveResult{})
}
Expand All @@ -115,15 +115,15 @@ func SetDefaultBranch(ctx context.Context, ownerName, repoName, branch string) R
url.PathEscape(repoName),
url.PathEscape(branch),
)
req := newInternalRequest(ctx, reqURL, "POST")
req := newInternalRequestAPI(ctx, reqURL, "POST")
_, extra := requestJSONResp(req, &ResponseText{})
return extra
}

// SSHLog sends ssh error log response
func SSHLog(ctx context.Context, isErr bool, msg string) error {
reqURL := setting.LocalURL + "api/internal/ssh/log"
req := newInternalRequest(ctx, reqURL, "POST", &SSHLogOption{IsError: isErr, Message: msg})
req := newInternalRequestAPI(ctx, reqURL, "POST", &SSHLogOption{IsError: isErr, Message: msg})
_, extra := requestJSONResp(req, &ResponseText{})
return extra.Error
}
8 changes: 6 additions & 2 deletions modules/private/internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func getClientIP() string {
return strings.Fields(sshConnEnv)[0]
}

func newInternalRequest(ctx context.Context, url, method string, body ...any) *httplib.Request {
func NewInternalRequest(ctx context.Context, url, method string) *httplib.Request {
if setting.InternalToken == "" {
log.Fatal(`The INTERNAL_TOKEN setting is missing from the configuration file: %q.
Ensure you are running in the correct environment or set the correct configuration file with -c.`, setting.CustomConf)
Expand Down Expand Up @@ -82,13 +82,17 @@ Ensure you are running in the correct environment or set the correct configurati
},
})
}
return req
}

func newInternalRequestAPI(ctx context.Context, url, method string, body ...any) *httplib.Request {
req := NewInternalRequest(ctx, url, method)
if len(body) == 1 {
req.Header("Content-Type", "application/json")
jsonBytes, _ := json.Marshal(body[0])
req.Body(jsonBytes)
} else if len(body) > 1 {
log.Fatal("Too many arguments for newInternalRequest")
log.Fatal("Too many arguments for newInternalRequestAPI")
}

req.SetTimeout(10*time.Second, 60*time.Second)
Expand Down
Loading
Loading