From d9ebb8a0260d2c5b5cd8b9defffe05c25433a340 Mon Sep 17 00:00:00 2001 From: Igor Chubin Date: Fri, 24 Apr 2020 00:16:11 +0200 Subject: [PATCH] initial implementation of go web frontend --- cmd/srv.go | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 cmd/srv.go diff --git a/cmd/srv.go b/cmd/srv.go new file mode 100644 index 0000000..371cf21 --- /dev/null +++ b/cmd/srv.go @@ -0,0 +1,145 @@ +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)) + +}