package main import ( "context" "fmt" "io" "net/http" "net/url" "strings" "time" ) type upstreamResponse struct { statusCode int headers http.Header body []byte } var upstreamClient = newPublicHTTPClient(30 * time.Second) func PostForm(ctx context.Context, upstreamURL string, formParams url.Values, dpopHeader string) (*upstreamResponse, error) { req, err := http.NewRequestWithContext(ctx, http.MethodPost, upstreamURL, strings.NewReader(formParams.Encode())) if err != nil { return nil, fmt.Errorf("failed to create upstream request: %w", err) } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") if dpopHeader != "" { req.Header.Set("DPoP", dpopHeader) } resp, err := upstreamClient.Do(req) if err != nil { return nil, fmt.Errorf("upstream request failed: %w", err) } defer resp.Body.Close() body, err := io.ReadAll(io.LimitReader(resp.Body, maxUpstreamResponseBodySize+1)) if err != nil { return nil, fmt.Errorf("failed to read upstream response body: %w", err) } if len(body) > maxUpstreamResponseBodySize { return nil, fmt.Errorf("upstream response body exceeded %d bytes", maxUpstreamResponseBodySize) } return &upstreamResponse{ statusCode: resp.StatusCode, headers: resp.Header.Clone(), body: body, }, nil } func WriteProxiedResponse(w http.ResponseWriter, resp *upstreamResponse) error { for key, values := range resp.headers { for _, value := range values { w.Header().Add(key, value) } } w.WriteHeader(resp.statusCode) if _, err := w.Write(resp.body); err != nil { return fmt.Errorf("failed to write response body: %w", err) } return nil }