mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-07 00:50:27 +00:00
fix(http): close prefetch response when temporary-file setup fails
This commit is contained in:
1 parent
5342be16d9
commit
e27c7dddb4
2 files changed
+85
-10
No files matched your search
@@ -512,6 +512,17 @@ func (f *Fetcher) asyncPrefetch() {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Close the response even if temporary-file setup fails or shutdown
|
||||
// rejects a late prefetch. Otherwise the server can remain blocked writing.
|
||||
f.resolveRespLock.Lock()
|
||||
if f.resolveResp != nil {
|
||||
f.resolveResp.Body.Close()
|
||||
f.resolveResp = nil
|
||||
}
|
||||
f.resolveRespLock.Unlock()
|
||||
}()
|
||||
|
||||
// Create temporary file for prefetch data
|
||||
tempDir := ""
|
||||
if f.ctl != nil && f.ctl.TempDir != "" {
|
||||
@@ -537,16 +548,6 @@ func (f *Fetcher) asyncPrefetch() {
|
||||
f.prefetchFile = tmpFile
|
||||
f.prefetchFilePath = tmpFile.Name()
|
||||
|
||||
defer func() {
|
||||
// Close response body when prefetch stops
|
||||
f.resolveRespLock.Lock()
|
||||
if f.resolveResp != nil {
|
||||
f.resolveResp.Body.Close()
|
||||
f.resolveResp = nil
|
||||
}
|
||||
f.resolveRespLock.Unlock()
|
||||
}()
|
||||
|
||||
buf := make([]byte, 32*1024) // 32KB buffer
|
||||
reader := f.responseReader(resp)
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/GopeedLab/gopeed/internal/controller"
|
||||
"github.com/GopeedLab/gopeed/internal/tempfiles"
|
||||
)
|
||||
|
||||
type prefetchSetupBody struct {
|
||||
io.Reader
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (b *prefetchSetupBody) Close() error {
|
||||
b.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestPrefetchSetupFailureClosesResponse(t *testing.T) {
|
||||
for _, closedScope := range []bool{false, true} {
|
||||
name := "invalid_temp_directory"
|
||||
if closedScope {
|
||||
name = "downloader_already_closed"
|
||||
}
|
||||
t.Run(name, func(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
scope := &tempfiles.Scope{}
|
||||
dir := root
|
||||
if closedScope {
|
||||
if err := scope.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
dir = filepath.Join(root, "file")
|
||||
if err := os.WriteFile(dir, nil, 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
body := &prefetchSetupBody{Reader: strings.NewReader("unused response")}
|
||||
f := &Fetcher{
|
||||
ctl: &controller.Controller{TempDir: dir, TempFiles: scope},
|
||||
resolveResp: &http.Response{Body: body},
|
||||
}
|
||||
f.asyncPrefetch()
|
||||
if f.prefetchErr == nil {
|
||||
t.Fatal("expected temporary-file initialization to fail")
|
||||
}
|
||||
if !body.closed {
|
||||
t.Error("prefetch setup failure left the response body open")
|
||||
}
|
||||
if f.resolveResp != nil {
|
||||
t.Error("prefetch retained its failed resolve response")
|
||||
}
|
||||
if !f.prefetchDone.Load() {
|
||||
t.Error("prefetch did not report completion")
|
||||
}
|
||||
if closedScope {
|
||||
entries, err := os.ReadDir(root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(entries) != 0 {
|
||||
t.Errorf("late prefetch left temporary files: %v", entries)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user