mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-04 05:18:25 +00:00 
			
		
		
		
	Backport #12102 this was thought to be due to timeouts, however on closer look this appears to be due to the Close() function of the BlameReader hanging with a blocked stdout pipe. This PR fixes this Close function to: * Cancel the context of the cmd * Close the StdoutReader - ensuring that the output pipe is closed Further it makes the context of the `git blame` command a child of the request context - ensuring that even if Close() is not called, on cancellation of the Request the blame is command will also be cancelled. Fixes #11716 Closes #11727 Signed-off-by: Andrew Thornton <art27@cantab.net>
This commit is contained in:
		@@ -79,7 +79,9 @@ func (r *BlameReader) NextPart() (*BlamePart, error) {
 | 
				
			|||||||
// Close BlameReader - don't run NextPart after invoking that
 | 
					// Close BlameReader - don't run NextPart after invoking that
 | 
				
			||||||
func (r *BlameReader) Close() error {
 | 
					func (r *BlameReader) Close() error {
 | 
				
			||||||
	defer process.GetManager().Remove(r.pid)
 | 
						defer process.GetManager().Remove(r.pid)
 | 
				
			||||||
	defer r.cancel()
 | 
						r.cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_ = r.output.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := r.cmd.Wait(); err != nil {
 | 
						if err := r.cmd.Wait(); err != nil {
 | 
				
			||||||
		return fmt.Errorf("Wait: %v", err)
 | 
							return fmt.Errorf("Wait: %v", err)
 | 
				
			||||||
@@ -89,19 +91,19 @@ func (r *BlameReader) Close() error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CreateBlameReader creates reader for given repository, commit and file
 | 
					// CreateBlameReader creates reader for given repository, commit and file
 | 
				
			||||||
func CreateBlameReader(repoPath, commitID, file string) (*BlameReader, error) {
 | 
					func CreateBlameReader(ctx context.Context, repoPath, commitID, file string) (*BlameReader, error) {
 | 
				
			||||||
	gitRepo, err := OpenRepository(repoPath)
 | 
						gitRepo, err := OpenRepository(repoPath)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	gitRepo.Close()
 | 
						gitRepo.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return createBlameReader(repoPath, GitExecutable, "blame", commitID, "--porcelain", "--", file)
 | 
						return createBlameReader(ctx, repoPath, GitExecutable, "blame", commitID, "--porcelain", "--", file)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func createBlameReader(dir string, command ...string) (*BlameReader, error) {
 | 
					func createBlameReader(ctx context.Context, dir string, command ...string) (*BlameReader, error) {
 | 
				
			||||||
	// FIXME: graceful: This should have a timeout
 | 
						// Here we use the provided context - this should be tied to the request performing the blame so that it does not hang around.
 | 
				
			||||||
	ctx, cancel := context.WithCancel(DefaultContext)
 | 
						ctx, cancel := context.WithCancel(ctx)
 | 
				
			||||||
	cmd := exec.CommandContext(ctx, command[0], command[1:]...)
 | 
						cmd := exec.CommandContext(ctx, command[0], command[1:]...)
 | 
				
			||||||
	cmd.Dir = dir
 | 
						cmd.Dir = dir
 | 
				
			||||||
	cmd.Stderr = os.Stderr
 | 
						cmd.Stderr = os.Stderr
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@
 | 
				
			|||||||
package git
 | 
					package git
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -93,8 +94,10 @@ func TestReadingBlameOutput(t *testing.T) {
 | 
				
			|||||||
	if _, err = tempFile.WriteString(exampleBlame); err != nil {
 | 
						if _, err = tempFile.WriteString(exampleBlame); err != nil {
 | 
				
			||||||
		panic(err)
 | 
							panic(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						ctx, cancel := context.WithCancel(context.Background())
 | 
				
			||||||
 | 
						defer cancel()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	blameReader, err := createBlameReader("", "cat", tempFile.Name())
 | 
						blameReader, err := createBlameReader(ctx, "", "cat", tempFile.Name())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		panic(err)
 | 
							panic(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -141,7 +141,13 @@ func RefBlame(ctx *context.Context) {
 | 
				
			|||||||
	ctx.Data["FileSize"] = blob.Size()
 | 
						ctx.Data["FileSize"] = blob.Size()
 | 
				
			||||||
	ctx.Data["FileName"] = blob.Name()
 | 
						ctx.Data["FileName"] = blob.Name()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	blameReader, err := git.CreateBlameReader(models.RepoPath(userName, repoName), commitID, fileName)
 | 
						ctx.Data["NumLines"], err = blob.GetBlobLineCount()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							ctx.NotFound("GetBlobLineCount", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						blameReader, err := git.CreateBlameReader(ctx.Req.Context(), models.RepoPath(userName, repoName), commitID, fileName)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.NotFound("CreateBlameReader", err)
 | 
							ctx.NotFound("CreateBlameReader", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user