first commit

This commit is contained in:
2026-06-19 12:32:14 +09:00
commit eb8d9b9460
36 changed files with 6502 additions and 0 deletions

88
httpdl/single_test.go Normal file
View File

@@ -0,0 +1,88 @@
package httpdl
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"path/filepath"
"testing"
"github.com/hanbok/got/download"
)
// TestSingleShortBody verifies that a single-stream transfer whose body ends
// before the declared Content-Length is reported as an error (a retryable short
// read) rather than a false success (item [1]). A proxy that closes early must
// not leave the download marked Complete.
func TestSingleShortBody(t *testing.T) {
const declared = 1000
// Hijack the connection so we can declare a long Content-Length but write
// only a few bytes and then close — exactly the early-close a re-chunking
// proxy produces. httptest's normal writer would otherwise flag the mismatch
// on its own terms; hijacking gives us full control of the wire bytes.
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
hj, ok := w.(http.Hijacker)
if !ok {
t.Errorf("server does not support hijacking")
return
}
conn, _, err := hj.Hijack()
if err != nil {
t.Errorf("hijack: %v", err)
return
}
defer conn.Close()
fmt.Fprintf(conn, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n", declared)
conn.Write([]byte("short")) // only 5 of the promised 1000 bytes
// closing the connection now is the early EOF
}))
defer srv.Close()
dir := t.TempDir()
out := filepath.Join(dir, "file.bin")
d := New([]string{srv.URL + "/file.bin"}, Config{
Dir: dir,
Out: "file.bin",
MaxConnPerServer: 1,
Tries: 1, // a single attempt so the retryable short read surfaces, not loops
UserAgent: "got-test",
})
err := d.single(context.Background(), out)
if err == nil {
t.Fatal("single() returned nil for a body shorter than Content-Length; want an error")
}
if d.Stat().Status == download.Complete {
t.Fatalf("status = Complete after a short body; want not Complete")
}
}
// TestSingleCompleteFullBody is the positive control: a body that matches its
// Content-Length succeeds. This guards against the short-body check rejecting a
// legitimately complete stream.
func TestSingleCompleteFullBody(t *testing.T) {
const body = "the whole thing"
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", fmt.Sprint(len(body)))
w.WriteHeader(http.StatusOK)
w.Write([]byte(body))
}))
defer srv.Close()
dir := t.TempDir()
out := filepath.Join(dir, "file.bin")
d := New([]string{srv.URL + "/file.bin"}, Config{
Dir: dir,
Out: "file.bin",
MaxConnPerServer: 1,
Tries: 1,
UserAgent: "got-test",
})
if err := d.single(context.Background(), out); err != nil {
t.Fatalf("single() of a full body returned %v; want nil", err)
}
}