wttr.in/cmd/srv.go

146 lines
2.8 KiB
Go
Raw Normal View History

package main
import (
"context"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"time"
"github.com/hashicorp/golang-lru"
)
var lruCache *lru.Cache
type ResponseWithHeader struct {
Body []byte
Header http.Header
StatusCode int // e.g. 200
}
func init() {
var err error
lruCache, err = lru.New(12800)
if err != nil {
panic(err)
}
dialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * 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)
}
}
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,
}
}
func copyHeader(dst, src http.Header) {
for k, vv := range src {
for _, v := range vv {
dst.Add(k, v)
}
}
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var response ResponseWithHeader
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))
}