diff --git a/http_middleware.go b/http_middleware.go index e684fc0..e20cfba 100644 --- a/http_middleware.go +++ b/http_middleware.go @@ -3,6 +3,9 @@ package main import ( "net/http" "strings" + "sync" + "sync/atomic" + "time" ) func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc { @@ -26,3 +29,64 @@ func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc { next.ServeHTTP(w, r) }) } + +var ( + queue = [26]sync.Mutex{} + concurrentRequests = [26]atomic.Uint32{} +) + +func queueMiddleware(next http.HandlerFunc) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if len(r.URL.Path) <= 30 { + next.ServeHTTP(w, r) + return + } + + willQueue := false + for _, prefix := range []string{"/nevent1", "/image", "/naddr1", "/npub1", "/nprofile1", "/note1", "/embed"} { + if strings.HasPrefix(r.URL.Path, prefix) { + willQueue = true + break + } + } + + if !willQueue { + next.ServeHTTP(w, r) + return + } + + qidx := stupidHash(r.URL.Path) + curr := concurrentRequests[qidx].Load() + isFirst := curr == 0 + + // add 1 and lock (or wait for the lock) + concurrentRequests[qidx].Add(1) + queue[qidx].Lock() + + if isFirst { + next.ServeHTTP(w, r) + + // we are the first requesting this, so we have the duty to reset it to zero later + concurrentRequests[qidx].Store(0) + queue[qidx].Unlock() + return + } + + // if we are not the first to request this we will wait for the underlying page to be loaded + // then we will be redirect to open it again, so hopefully we will hit the cloudflare cache this time + path := r.URL.Path + if r.URL.RawQuery != "" { + path += "?" + r.URL.RawQuery + } + + time.Sleep(time.Millisecond * 90) + http.Redirect(w, r, path, http.StatusFound) + + queue[qidx].Unlock() + } +} + +// stupidHash doesn't care very much about collisions +func stupidHash(s string) int { + return int(s[3]+s[7]+s[18]+s[29]) % 26 +} diff --git a/main.go b/main.go index 94804fc..585b131 100644 --- a/main.go +++ b/main.go @@ -153,8 +153,10 @@ func main() { cloudflareBlock( agentBlock( loggingMiddleware( - corsM( - relay.ServeHTTP, + queueMiddleware( + corsM( + relay.ServeHTTP, + ), ), ), ),