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