about/internal/plugins/http_tools.go

138 lines
3.2 KiB
Go

package plugins
import (
"context"
"fmt"
"net"
"net/http"
"net/url"
"os"
"time"
)
func NewHTTPClient() *http.Client {
transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
ResponseHeaderTimeout: 15 * time.Second,
}
if proxyURL := os.Getenv("HTTP_PROXY"); proxyURL != "" {
if parsed, err := url.Parse(proxyURL); err == nil {
transport.Proxy = http.ProxyURL(parsed)
}
} else if proxyURL := os.Getenv("HTTPS_PROXY"); proxyURL != "" {
if parsed, err := url.Parse(proxyURL); err == nil {
transport.Proxy = http.ProxyURL(parsed)
}
}
return &http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}
}
func NewHTTPClientWithTimeout(timeout time.Duration) *http.Client {
transport := &http.Transport{
DialContext: (&net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 50,
MaxIdleConnsPerHost: 5,
IdleConnTimeout: 60 * time.Second,
TLSHandshakeTimeout: 5 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
DisableKeepAlives: false,
ForceAttemptHTTP2: true,
}
if proxyURL := os.Getenv("HTTP_PROXY"); proxyURL != "" {
if parsed, err := url.Parse(proxyURL); err == nil {
transport.Proxy = http.ProxyURL(parsed)
}
} else if proxyURL := os.Getenv("HTTPS_PROXY"); proxyURL != "" {
if parsed, err := url.Parse(proxyURL); err == nil {
transport.Proxy = http.ProxyURL(parsed)
}
}
return &http.Client{
Transport: transport,
Timeout: timeout,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if len(via) >= 3 {
return fmt.Errorf("too many redirects")
}
return nil
},
}
}
func DoRequestWithContext(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) {
if ctx == nil {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
}
reqCtx, cancel := context.WithTimeout(ctx, 15*time.Second)
defer cancel()
req = req.WithContext(reqCtx)
req.Header.Set("User-Agent", "AboutPage/1.0 (about.akarpov.ru)")
req.Header.Set("Accept", "application/json")
req.Header.Set("Connection", "close")
done := make(chan struct {
resp *http.Response
err error
}, 1)
go func() {
defer func() {
if r := recover(); r != nil {
done <- struct {
resp *http.Response
err error
}{nil, fmt.Errorf("http request panic: %v", r)}
}
}()
resp, err := client.Do(req)
done <- struct {
resp *http.Response
err error
}{resp, err}
}()
select {
case result := <-done:
return result.resp, result.err
case <-reqCtx.Done():
return nil, reqCtx.Err()
}
}
func SafeHTTPRequest(ctx context.Context, client *http.Client, url string) (*http.Response, error) {
if client == nil {
client = NewHTTPClientWithTimeout(10 * time.Second)
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return DoRequestWithContext(ctx, client, req)
}