This is a personal torrent client, not a web client or daemon, so the rarely-used HTTP-client knobs and redundant tuning flags are gone — flag, config field, implementation, and tests removed for each. The option surface drops from 54 to 28. Web-client cluster removed: --all-proxy (HTTP_PROXY env still works), load/save-cookies, --referer, -U/--user-agent, --http-user/--http-passwd, --conditional-get, --remote-time, --check-certificate, --ca-certificate, --lowest-speed-limit. Pass --header for a one-off auth/cookie/UA header. Redundant knobs removed: -x and -k (folded into -s), --connect-timeout, --retry-wait, per-item --max-download-limit/-u, --bt-max-peers, --bt-stop-timeout, --disable-ipv6, --auto-save-interval, --human-readable, --stop, -T (a positional .torrent works). follow-torrent and human-readable are now unconditional defaults.
85 lines
2.6 KiB
Go
85 lines
2.6 KiB
Go
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",
|
|
Tries: 1, // a single attempt so the retryable short read surfaces, not loops
|
|
})
|
|
|
|
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",
|
|
Tries: 1,
|
|
})
|
|
|
|
if err := d.single(context.Background(), out); err != nil {
|
|
t.Fatalf("single() of a full body returned %v; want nil", err)
|
|
}
|
|
}
|