|
|
|
@ -2,113 +2,48 @@ package main |
|
|
|
|
|
|
|
import ( |
|
|
|
"context" |
|
|
|
"fmt" |
|
|
|
"io/ioutil" |
|
|
|
"log" |
|
|
|
"net" |
|
|
|
"net/http" |
|
|
|
"time" |
|
|
|
|
|
|
|
"github.com/hashicorp/golang-lru" |
|
|
|
lru "github.com/hashicorp/golang-lru" |
|
|
|
) |
|
|
|
|
|
|
|
const uplinkSrvAddr = "127.0.0.1:9002" |
|
|
|
const uplinkTimeout = 30 |
|
|
|
const prefetchInterval = 300 |
|
|
|
const lruCacheSize = 12800 |
|
|
|
|
|
|
|
var lruCache *lru.Cache |
|
|
|
|
|
|
|
type ResponseWithHeader struct { |
|
|
|
type responseWithHeader struct { |
|
|
|
InProgress bool // true if the request is being processed
|
|
|
|
Expires time.Time // expiration time of the cache entry
|
|
|
|
|
|
|
|
Body []byte |
|
|
|
Header http.Header |
|
|
|
StatusCode int // e.g. 200
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
func init() { |
|
|
|
var err error |
|
|
|
lruCache, err = lru.New(12800) |
|
|
|
lruCache, err = lru.New(lruCacheSize) |
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
|
|
|
|
dialer := &net.Dialer{ |
|
|
|
Timeout: 30 * time.Second, |
|
|
|
KeepAlive: 30 * time.Second, |
|
|
|
Timeout: uplinkTimeout * time.Second, |
|
|
|
KeepAlive: uplinkTimeout * time.Second, |
|
|
|
DualStack: true, |
|
|
|
} |
|
|
|
|
|
|
|
http.DefaultTransport.(*http.Transport).DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { |
|
|
|
addr = "127.0.0.1:8002" |
|
|
|
return dialer.DialContext(ctx, network, addr) |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
func readUserIP(r *http.Request) string { |
|
|
|
IPAddress := r.Header.Get("X-Real-Ip") |
|
|
|
if IPAddress == "" { |
|
|
|
IPAddress = r.Header.Get("X-Forwarded-For") |
|
|
|
} |
|
|
|
if IPAddress == "" { |
|
|
|
IPAddress = r.RemoteAddr |
|
|
|
var err error |
|
|
|
IPAddress, _, err = net.SplitHostPort(IPAddress) |
|
|
|
if err != nil { |
|
|
|
fmt.Printf("userip: %q is not IP:port\n", IPAddress) |
|
|
|
} |
|
|
|
} |
|
|
|
return IPAddress |
|
|
|
} |
|
|
|
|
|
|
|
// implementation of the cache.get_signature of original wttr.in
|
|
|
|
func findCacheDigest(req *http.Request) string { |
|
|
|
|
|
|
|
userAgent := req.Header.Get("User-Agent") |
|
|
|
|
|
|
|
queryHost := req.Host |
|
|
|
queryString := req.RequestURI |
|
|
|
|
|
|
|
clientIpAddress := readUserIP(req) |
|
|
|
|
|
|
|
lang := req.Header.Get("Accept-Language") |
|
|
|
|
|
|
|
now := time.Now() |
|
|
|
secs := now.Unix() |
|
|
|
timestamp := secs / 1000 |
|
|
|
|
|
|
|
return fmt.Sprintf("%s:%s%s:%s:%s:%d", userAgent, queryHost, queryString, clientIpAddress, lang, timestamp) |
|
|
|
} |
|
|
|
|
|
|
|
func get(req *http.Request) ResponseWithHeader { |
|
|
|
|
|
|
|
client := &http.Client{} |
|
|
|
|
|
|
|
queryURL := fmt.Sprintf("http://%s%s", req.Host, req.RequestURI) |
|
|
|
|
|
|
|
proxyReq, err := http.NewRequest(req.Method, queryURL, req.Body) |
|
|
|
if err != nil { |
|
|
|
// handle error
|
|
|
|
} |
|
|
|
|
|
|
|
// proxyReq.Header.Set("Host", req.Host)
|
|
|
|
// proxyReq.Header.Set("X-Forwarded-For", req.RemoteAddr)
|
|
|
|
|
|
|
|
for header, values := range req.Header { |
|
|
|
for _, value := range values { |
|
|
|
proxyReq.Header.Add(header, value) |
|
|
|
} |
|
|
|
http.DefaultTransport.(*http.Transport).DialContext = func(ctx context.Context, network, _ string) (net.Conn, error) { |
|
|
|
return dialer.DialContext(ctx, network, uplinkSrvAddr) |
|
|
|
} |
|
|
|
|
|
|
|
res, err := client.Do(proxyReq) |
|
|
|
|
|
|
|
if err != nil { |
|
|
|
panic(err) |
|
|
|
} |
|
|
|
|
|
|
|
body, err := ioutil.ReadAll(res.Body) |
|
|
|
|
|
|
|
return ResponseWithHeader{ |
|
|
|
Body: body, |
|
|
|
Header: res.Header, |
|
|
|
StatusCode: res.StatusCode, |
|
|
|
} |
|
|
|
initPeakHandling() |
|
|
|
} |
|
|
|
|
|
|
|
func copyHeader(dst, src http.Header) { |
|
|
|
@ -120,26 +55,14 @@ func copyHeader(dst, src http.Header) { |
|
|
|
} |
|
|
|
|
|
|
|
func main() { |
|
|
|
|
|
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
|
|
|
var response ResponseWithHeader |
|
|
|
// printStat()
|
|
|
|
response := processRequest(r) |
|
|
|
|
|
|
|
cacheDigest := findCacheDigest(r) |
|
|
|
cacheBody, ok := lruCache.Get(cacheDigest) |
|
|
|
if ok { |
|
|
|
response = cacheBody.(ResponseWithHeader) |
|
|
|
} else { |
|
|
|
fmt.Println(cacheDigest) |
|
|
|
response = get(r) |
|
|
|
if response.StatusCode == 200 { |
|
|
|
lruCache.Add(cacheDigest, response) |
|
|
|
} |
|
|
|
} |
|
|
|
copyHeader(w.Header(), response.Header) |
|
|
|
w.WriteHeader(response.StatusCode) |
|
|
|
w.Write(response.Body) |
|
|
|
}) |
|
|
|
|
|
|
|
log.Fatal(http.ListenAndServe(":8081", nil)) |
|
|
|
|
|
|
|
log.Fatal(http.ListenAndServe(":8082", nil)) |
|
|
|
} |