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}