mirror of
https://github.com/hanzoai/commerce.git
synced 2026-10-30 15:08:42 +00:00
Multi-tenant commerce and billing: orders, subscriptions, invoices, plans, usage metering and a credit ledger. One Go binary. MIT OR Apache-2.0. Claude-Session: https://claude.ai/code/session_01SAtaDFm8VDiWrhYHNNtxMw
57 lines
1.9 KiB
Go
57 lines
1.9 KiB
Go
package checkout
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// HTTPForwarder is the production Forwarder. It uses a shared *http.Client
|
|
// with sane timeouts so a slow backend cannot exhaust commerce's goroutine
|
|
// budget. TLS is enforced (org Backend.URL must be https://) — an http
|
|
// backend is a misconfiguration and requests to it will be rejected by
|
|
// Go's transport anyway.
|
|
type HTTPForwarder struct {
|
|
client *http.Client
|
|
}
|
|
|
|
// NewHTTPForwarder builds a forwarder with 15s connect timeout, 30s
|
|
// request timeout, 20s TLS handshake. These numbers are tuned for BD:
|
|
// BD's deposit-intent creation path is typically sub-second.
|
|
func NewHTTPForwarder() *HTTPForwarder {
|
|
transport := &http.Transport{
|
|
Proxy: http.ProxyFromEnvironment,
|
|
DialContext: (&net.Dialer{
|
|
Timeout: 15 * time.Second,
|
|
KeepAlive: 30 * time.Second,
|
|
}).DialContext,
|
|
TLSHandshakeTimeout: 20 * time.Second,
|
|
// Pin TLS 1.2+ — commerce never talks to a backend over
|
|
// plaintext, and old TLS versions are not acceptable on the
|
|
// inter-service path.
|
|
TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12},
|
|
ForceAttemptHTTP2: true,
|
|
MaxIdleConns: 50,
|
|
MaxIdleConnsPerHost: 10,
|
|
IdleConnTimeout: 90 * time.Second,
|
|
}
|
|
return &HTTPForwarder{
|
|
client: &http.Client{
|
|
Transport: transport,
|
|
Timeout: 30 * time.Second,
|
|
// Never follow redirects — a redirect from BD would be
|
|
// surprising and a potential SSRF pivot. Error instead.
|
|
CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse },
|
|
},
|
|
}
|
|
}
|
|
|
|
// Forward sends req upstream. org is accepted so future per-org
|
|
// policy (custom timeouts, mTLS client certs) can be layered without
|
|
// changing the interface.
|
|
func (h *HTTPForwarder) Forward(req *http.Request, org Org) (*http.Response, error) {
|
|
_ = org // reserved for per-org policy
|
|
return h.client.Do(req)
|
|
}
|