diff --git a/cmd/processRequest.go b/cmd/processRequest.go index 7c44a9f..a4b9f14 100644 --- a/cmd/processRequest.go +++ b/cmd/processRequest.go @@ -14,6 +14,10 @@ import ( func processRequest(r *http.Request) responseWithHeader { var response responseWithHeader + if response, ok := redirectInsecure(r); ok { + return *response + } + if dontCache(r) { return get(r) } @@ -120,8 +124,56 @@ func dontCache(req *http.Request) bool { // dont cache cyclic requests loc := strings.Split(req.RequestURI, "?")[0] - if strings.Contains(loc, ":") { - return true + return strings.Contains(loc, ":") +} + +// redirectInsecure returns redirection response, and bool value, if redirection was needed, +// if the query comes from a browser, and it is insecure. +// +// Insecure queries are marked by the frontend web server +// with X-Forwarded-Proto header: +// +// proxy_set_header X-Forwarded-Proto $scheme; +// +// +func redirectInsecure(req *http.Request) (*responseWithHeader, bool) { + if isPlainTextAgent(req.Header.Get("User-Agent")) { + return nil, false + } + + if strings.ToLower(req.Header.Get("X-Forwarded-Proto")) == "https" { + return nil, false + } + + target := "https://" + req.Host + req.URL.Path + if len(req.URL.RawQuery) > 0 { + target += "?" + req.URL.RawQuery + } + + body := []byte(fmt.Sprintf(` +301 Moved +

301 Moved

+The document has moved +here. + +`, target)) + + return &responseWithHeader{ + InProgress: false, + Expires: time.Now().Add(time.Duration(randInt(1000, 1500)) * time.Second), + Body: body, + Header: http.Header{"Location": []string{target}}, + StatusCode: 301, + }, true +} + +// isPlainTextAgent returns true if userAgent is a plain-text agent +func isPlainTextAgent(userAgent string) bool { + userAgentLower := strings.ToLower(userAgent) + for _, signature := range plainTextAgents { + if strings.Contains(userAgentLower, signature) { + return true + } } return false } diff --git a/cmd/srv.go b/cmd/srv.go index aae32d5..b609b96 100644 --- a/cmd/srv.go +++ b/cmd/srv.go @@ -2,6 +2,7 @@ package main import ( "context" + "fmt" "log" "net" "net/http" @@ -10,11 +11,26 @@ import ( lru "github.com/hashicorp/golang-lru" ) +const serverPort = 8083 const uplinkSrvAddr = "127.0.0.1:9002" const uplinkTimeout = 30 const prefetchInterval = 300 const lruCacheSize = 12800 +// plainTextAgents contains signatures of the plain-text agents +var plainTextAgents = []string{ + "curl", + "httpie", + "lwp-request", + "wget", + "python-requests", + "openbsd ftp", + "powershell", + "fetch", + "aiohttp", + "http_get", +} + var lruCache *lru.Cache type responseWithHeader struct { @@ -65,5 +81,5 @@ func main() { w.Write(response.Body) }) - log.Fatal(http.ListenAndServe(":8082", nil)) + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", serverPort), nil)) }