1
0
mirror of https://github.com/bgp/stayrtr.git synced 2024-05-06 15:54:54 +00:00
bgp-stayrtr/utils/utils.go
Ties de Kock e5c5a99a0c check for err
2021-10-25 20:19:28 +02:00

173 lines
4.2 KiB
Go

package utils
import (
"fmt"
"io"
"net"
"net/http"
"os"
"sync"
"time"
)
type FetchConfig struct {
UserAgent string
Mime string
etags map[string]string
lastModified map[string]time.Time
conditionalRequestLock *sync.RWMutex
EnableEtags bool
EnableLastModified bool
}
func NewFetchConfig() *FetchConfig {
return &FetchConfig{
etags: make(map[string]string),
lastModified: make(map[string]time.Time),
conditionalRequestLock: &sync.RWMutex{},
Mime: "application/json",
}
}
type HttpNotModified struct {
File string
}
func (e HttpNotModified) Error() string {
return fmt.Sprintf("HTTP 304 Not modified for %s", e.File)
}
type IdenticalEtag struct {
File string
Etag string
}
func (e IdenticalEtag) Error() string {
return fmt.Sprintf("File %s is identical according to Etag: %s", e.File, e.Etag)
}
func (c *FetchConfig) FetchFile(file string) ([]byte, int, bool, error) {
var f io.Reader
var err error
if len(file) > 8 && (file[0:7] == "http://" || file[0:8] == "https://") {
// Copying base of DefaultTransport from https://golang.org/src/net/http/transport.go
// There is a proposal for a Clone of
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
ProxyConnectHeader: map[string][]string{},
}
// Keep User-Agent in proxy request
tr.ProxyConnectHeader.Set("User-Agent", c.UserAgent)
client := &http.Client{Transport: tr}
req, err := http.NewRequest("GET", file, nil)
if err != nil {
return nil, -1, false, err
}
req.Header.Set("User-Agent", c.UserAgent)
if c.Mime != "" {
req.Header.Set("Accept", c.Mime)
}
c.conditionalRequestLock.RLock()
if c.EnableEtags {
etag, ok := c.etags[file]
if ok {
req.Header.Set("If-None-Match", etag)
}
}
if c.EnableLastModified {
lastModified, ok := c.lastModified[file]
if ok {
req.Header.Set("If-Modified-Since", lastModified.UTC().Format(http.TimeFormat))
}
}
c.conditionalRequestLock.RUnlock()
proxyurl, err := http.ProxyFromEnvironment(req)
if err != nil {
return nil, -1, false, err
}
proxyreq := http.ProxyURL(proxyurl)
tr.Proxy = proxyreq
if err != nil {
return nil, -1, false, err
}
fhttp, err := client.Do(req)
if err != nil {
return nil, -1, false, err
}
if fhttp.Body != nil {
defer fhttp.Body.Close()
}
defer client.CloseIdleConnections()
//RefreshStatusCode.WithLabelValues(file, fmt.Sprintf("%d", fhttp.StatusCode)).Inc()
if fhttp.StatusCode == 304 {
//LastRefresh.WithLabelValues(file).Set(float64(s.lastts.UnixNano() / 1e9))
return nil, fhttp.StatusCode, true, HttpNotModified{
File: file,
}
} else if fhttp.StatusCode != 200 {
c.conditionalRequestLock.Lock()
delete(c.etags, file)
delete(c.lastModified, file)
c.conditionalRequestLock.Unlock()
return nil, fhttp.StatusCode, true, fmt.Errorf("HTTP %s", fhttp.Status)
}
//LastRefresh.WithLabelValues(file).Set(float64(s.lastts.UnixNano() / 1e9))
f = fhttp.Body
newEtag := fhttp.Header.Get("ETag")
if !c.EnableEtags || newEtag == "" || newEtag != c.etags[file] { // check lock here
c.conditionalRequestLock.Lock()
c.etags[file] = newEtag
c.conditionalRequestLock.Unlock()
} else {
return nil, fhttp.StatusCode, true, IdenticalEtag{
File: file,
Etag: newEtag,
}
}
if c.EnableLastModified {
// Accept any valid Last-Modified values. Because of the 1s resolution,
// getting the same value is not an error (c.f. the IdenticalEtag error).
ifModifiedSince, err := http.ParseTime(fhttp.Header.Get("Last-Modified"))
c.conditionalRequestLock.Lock()
if err == nil {
c.lastModified[file] = ifModifiedSince
} else {
delete(c.lastModified, file)
}
c.conditionalRequestLock.Unlock()
}
} else {
f, err = os.Open(file)
if err != nil {
return nil, -1, false, err
}
}
data, err := io.ReadAll(f)
if err != nil {
return nil, -1, false, err
}
return data, -1, false, nil
}