httpclient.go

 1// Package httpclient centralizes HTTP timeout defaults so the rest of the
 2// codebase doesn't sprinkle magic numbers across packages.
 3package httpclient
 4
 5import (
 6	"fmt"
 7	"net/http"
 8	"time"
 9)
10
11// Named timeouts. Each constant documents the call site it covers so
12// future contributors don't have to grep for callers.
13const (
14	// PluginCallTimeout bounds Lua-driven plugin HTTP calls (plugin/http.go).
15	PluginCallTimeout = 10 * time.Second
16	// RegistryFetchTimeout bounds plugin registry / plugin file fetches (plugins/embed.go).
17	RegistryFetchTimeout = 10 * time.Second
18	// RemoteImageTimeout bounds inline image fetches (view/html.go).
19	// Kept short so message rendering doesn't stall.
20	RemoteImageTimeout = 5 * time.Second
21	// InstallTimeout bounds CLI install downloads (cli/install.go).
22	InstallTimeout = 30 * time.Second
23	// UpdateCheckTimeout bounds version checks and asset downloads from main (main.go).
24	UpdateCheckTimeout = 30 * time.Second
25	// IMAPBatchActionTimeout bounds bulk IMAP operations (delete/archive/move) from main (main.go).
26	IMAPBatchActionTimeout = 60 * time.Second
27	// IMAPSearchTimeout bounds server-side IMAP search queries from main (main.go).
28	IMAPSearchTimeout = 60 * time.Second
29)
30
31// New returns an http.Client preconfigured with the given timeout.
32func New(timeout time.Duration) *http.Client {
33	return &http.Client{Timeout: timeout}
34}
35
36// NewWithRedirectCap returns an http.Client with the given timeout and a
37// hard cap on the number of redirects it will follow before giving up.
38// Used by the main update / asset download client to avoid infinite chains.
39func NewWithRedirectCap(timeout time.Duration, maxRedirects int) *http.Client {
40	return &http.Client{
41		Timeout: timeout,
42		CheckRedirect: func(req *http.Request, via []*http.Request) error {
43			if len(via) >= maxRedirects {
44				return fmt.Errorf("stopped after %d redirects", maxRedirects)
45			}
46			return nil
47		},
48	}
49}