package starnet import ( "io" "net" "net/http" "net/http/httptest" "testing" "time" ) func TestRequestTimeoutDoesNotMutateClientTimeout(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { time.Sleep(200 * time.Millisecond) w.WriteHeader(http.StatusOK) })) defer s.Close() client := NewClientNoErr() baseTimeout := client.HTTPClient().Timeout _, err := client.Get(s.URL, WithTimeout(80*time.Millisecond)) if err == nil { t.Fatal("expected request timeout error") } if client.HTTPClient().Timeout != baseTimeout { t.Fatalf("client timeout mutated: got=%v want=%v", client.HTTPClient().Timeout, baseTimeout) } resp, err := client.Get(s.URL, WithTimeout(400*time.Millisecond)) if err != nil { t.Fatalf("second request should succeed with larger timeout: %v", err) } defer resp.Close() } func TestRequestTimeoutNoLingeringConnDeadline(t *testing.T) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _, _ = io.Copy(io.Discard, r.Body) w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("ok")) })) defer s.Close() client := NewClientNoErr() resp1, err := client.Post(s.URL, WithBodyString("first"), WithTimeout(120*time.Millisecond)) if err != nil { t.Fatalf("first request should succeed: %v", err) } _ = resp1.Close() // 如果请求超时依赖连接级绝对 deadline,经过该等待后复用连接会出现误超时。 time.Sleep(220 * time.Millisecond) resp2, err := client.Post(s.URL, WithBodyString("second"), WithTimeout(1*time.Second)) if err != nil { t.Fatalf("second request should not be affected by previous timeout window: %v", err) } _ = resp2.Close() } func TestConnReadDeadlineTimeoutAndRecover(t *testing.T) { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatalf("listen error: %v", err) } defer ln.Close() done := make(chan struct{}) go func() { defer close(done) conn, err := ln.Accept() if err != nil { return } defer conn.Close() time.Sleep(180 * time.Millisecond) _, _ = conn.Write([]byte("x")) }() c, err := Dial("tcp", ln.Addr().String()) if err != nil { t.Fatalf("dial error: %v", err) } defer c.Close() if err := c.SetReadDeadline(time.Now().Add(60 * time.Millisecond)); err != nil { t.Fatalf("set read deadline error: %v", err) } buf := make([]byte, 1) _, err = c.Read(buf) if err == nil { t.Fatal("expected read timeout error") } if ne, ok := err.(net.Error); !ok || !ne.Timeout() { t.Fatalf("expected net timeout error, got: %v", err) } if err := c.SetReadDeadline(time.Time{}); err != nil { t.Fatalf("clear read deadline error: %v", err) } if _, err := io.ReadFull(c, buf); err != nil { t.Fatalf("read after clearing deadline should succeed: %v", err) } if string(buf) != "x" { t.Fatalf("unexpected payload: %q", string(buf)) } <-done }