diff options
| -rw-r--r-- | roseh.moe.go | 92 |
1 files changed, 61 insertions, 31 deletions
diff --git a/roseh.moe.go b/roseh.moe.go index 637c22b..4f0b86d 100644 --- a/roseh.moe.go +++ b/roseh.moe.go @@ -5,6 +5,7 @@ import ( "bytes" "cmp" "compress/gzip" + "context" "crypto/hkdf" "crypto/hmac" "crypto/rand" @@ -1019,6 +1020,10 @@ type gzipResponseWriter struct { code int } +func (w *gzipResponseWriter) Unwrap() http.ResponseWriter { + return w.ResponseWriter +} + func shouldGzip(w http.ResponseWriter) bool { disposition, _, _ := strings.Cut(w.Header().Get("Content-Disposition"), ";") length, lengthErr := strconv.Atoi(w.Header().Get("Content-Length")) @@ -1138,6 +1143,26 @@ func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.h.ServeHTTP(w, r) } +type timeoutHandler struct { + h http.Handler + timeout time.Duration +} + +func (h *timeoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + rc := http.NewResponseController(w) + deadline := time.Now().Add(h.timeout) + if err := rc.SetReadDeadline(deadline); err != nil { + log.Printf("Error: set read deadline: %s", err) + } + if err := rc.SetWriteDeadline(deadline); err != nil { + log.Printf("Error: set write deadline: %s", err) + } + ctx, cancel := context.WithDeadline(ctx, deadline) + defer cancel() + h.h.ServeHTTP(w, r.WithContext(ctx)) +} + type loggingHandler struct { h http.Handler } @@ -1179,7 +1204,7 @@ func main() { if err != nil { log.Fatal(err) } - mux.Handle("GET /{$}", indexHandler) + mux.Handle("GET /{$}", &timeoutHandler{h: indexHandler, timeout: 5 * time.Minute}) gamesIndexTemplate, err := outlinedTemplate(gamesIndexString) if err != nil { log.Fatal(err) @@ -1188,7 +1213,7 @@ func main() { if err != nil { log.Fatal(err) } - mux.Handle("GET /games/{$}", gamesIndexHandler) + mux.Handle("GET /games/{$}", &timeoutHandler{h: gamesIndexHandler, timeout: 5 * time.Minute}) pongTemplate, err := template.New("pong").Parse(pongString) if err != nil { log.Fatal(err) @@ -1197,7 +1222,7 @@ func main() { if err != nil { log.Fatal(err) } - mux.Handle("GET /games/pong", pongHandler) + mux.Handle("GET /games/pong", &timeoutHandler{h: pongHandler, timeout: 5 * time.Minute}) stallmanShooterTemplate, err := outlinedTemplate(stallmanShooterString) if err != nil { log.Fatal(err) @@ -1206,29 +1231,29 @@ func main() { if err != nil { log.Fatal(err) } - mux.Handle("GET /games/rms", stallmanShooterHandler) - mux.HandleFunc("GET /qr.png", qrHandler) - mux.HandleFunc("GET /wormhole", wormhole) + mux.Handle("GET /games/rms", &timeoutHandler{h: stallmanShooterHandler, timeout: 5 * time.Minute}) + mux.Handle("GET /qr.png", &timeoutHandler{h: http.HandlerFunc(qrHandler), timeout: 5 * time.Minute}) + mux.Handle("GET /wormhole", &timeoutHandler{h: http.HandlerFunc(wormhole), timeout: 5 * time.Minute}) mux.HandleFunc("POST /wormhole", wormholeUpload) mux.HandleFunc("GET /wormhole/{hole}", wormholeDownload) - mux.HandleFunc("GET /login", serveLogin) - mux.HandleFunc("POST /login", login) - mux.Handle("GET /otp", &authHandler{h: http.HandlerFunc(otp)}) - mux.HandleFunc("GET /otp/{code}", otpCode) - mux.Handle("GET /logout", &authHandler{h: http.HandlerFunc(logout), noRedirect: true}) - mux.Handle("GET /notepad", &authHandler{h: http.HandlerFunc(notepad)}) - mux.Handle("POST /notepad", &authHandler{h: http.HandlerFunc(autosave), noRedirect: true}) - mux.HandleFunc("GET /cmd/{$}", cmdIndex) - mux.HandleFunc("GET /cmd/{cmd}", cmd) - mux.HandleFunc("GET /pkg/{$}", pkgIndex) - mux.HandleFunc("GET /pkg/{pkg}", pkg) - mux.Handle("GET /nezuko/", &authHandler{h: http.StripPrefix("/nezuko/", http.FileServer(http.Dir(*nezuko)))}) - mux.HandleFunc("GET /static/", static) - mux.HandleFunc("GET /favicon.ico", favicon) + mux.Handle("GET /login", &timeoutHandler{h: http.HandlerFunc(serveLogin), timeout: 5 * time.Minute}) + mux.Handle("POST /login", &timeoutHandler{h: http.HandlerFunc(login), timeout: 5 * time.Minute}) + mux.Handle("GET /otp", &timeoutHandler{h: &authHandler{h: http.HandlerFunc(otp)}, timeout: 5 * time.Minute}) + mux.Handle("GET /otp/{code}", &timeoutHandler{h: http.HandlerFunc(otpCode), timeout: 5 * time.Minute}) + mux.Handle("GET /logout", &timeoutHandler{h: &authHandler{h: http.HandlerFunc(logout), noRedirect: true}, timeout: 5 * time.Minute}) + mux.Handle("GET /notepad", &timeoutHandler{h: &authHandler{h: http.HandlerFunc(notepad)}, timeout: 5 * time.Minute}) + mux.Handle("POST /notepad", &timeoutHandler{h: &authHandler{h: http.HandlerFunc(autosave), noRedirect: true}, timeout: 5 * time.Minute}) + mux.Handle("GET /cmd/{$}", &timeoutHandler{h: http.HandlerFunc(cmdIndex), timeout: 5 * time.Minute}) + mux.Handle("GET /cmd/{cmd}", &timeoutHandler{h: http.HandlerFunc(cmd), timeout: 5 * time.Minute}) + mux.Handle("GET /pkg/{$}", &timeoutHandler{h: http.HandlerFunc(pkgIndex), timeout: 5 * time.Minute}) + mux.Handle("GET /pkg/{pkg}", &timeoutHandler{h: http.HandlerFunc(pkg), timeout: 5 * time.Minute}) + mux.Handle("GET /nezuko/", &timeoutHandler{h: &authHandler{h: http.StripPrefix("/nezuko/", http.FileServer(http.Dir(*nezuko)))}, timeout: 10 * time.Minute}) + mux.Handle("GET /static/", &timeoutHandler{h: http.HandlerFunc(static), timeout: 5 * time.Minute}) + mux.Handle("GET /favicon.ico", &timeoutHandler{h: http.HandlerFunc(favicon), timeout: 5 * time.Minute}) apiMux := http.NewServeMux() apiMux.HandleFunc("PUT /api/wormhole/{name}", wormholeAPIUpload) mux.Handle("/api/", apiMux) - mux.HandleFunc("/", notFound) + mux.Handle("/", &timeoutHandler{h: http.HandlerFunc(notFound), timeout: 5 * time.Minute}) serverMux := http.NewServeMux() serverMux.Handle(selfURL.Hostname()+"/", http.NewCrossOriginProtection().Handler(&gzipHandler{mux})) jellyfinURL, err := url.Parse("http://127.0.0.1:8096") @@ -1243,10 +1268,10 @@ func main() { }}) codeMux := http.NewServeMux() cgitStaticFiles := http.FileServer(http.Dir(*cgit)) - codeMux.Handle("/cgit.css", cgitStaticFiles) - codeMux.Handle("/cgit.js", cgitStaticFiles) - codeMux.Handle("/robots.txt", cgitStaticFiles) - codeMux.HandleFunc("/favicon.ico", favicon) + codeMux.Handle("/cgit.css", &timeoutHandler{h: cgitStaticFiles, timeout: 5 * time.Minute}) + codeMux.Handle("/cgit.js", &timeoutHandler{h: cgitStaticFiles, timeout: 5 * time.Minute}) + codeMux.Handle("/robots.txt", &timeoutHandler{h: cgitStaticFiles, timeout: 5 * time.Minute}) + codeMux.Handle("/favicon.ico", &timeoutHandler{h: http.HandlerFunc(favicon), timeout: 5 * time.Minute}) cgitConfigAbsPath, err := filepath.Abs(*cgitConfig) if err != nil { log.Fatal(err) @@ -1256,9 +1281,14 @@ func main() { Env: []string{"CGIT_CONFIG=" + cgitConfigAbsPath}, } cgitCGI.PathLocationHandler = cgitCGI - codeMux.Handle("/", cgitCGI) + codeMux.Handle("/", &timeoutHandler{h: cgitCGI, timeout: 5 * time.Minute}) serverMux.Handle("code."+selfURL.Hostname()+"/", &gzipHandler{codeMux}) - handler := &loggingHandler{serverMux} + server := &http.Server{ + Handler: &loggingHandler{serverMux}, + + ReadHeaderTimeout: 5 * time.Minute, + IdleTimeout: 10 * time.Minute, + } port := selfURL.Port() if port == "" { @@ -1267,11 +1297,11 @@ func main() { port = "443" } } - addr := ":" + port - log.Printf("Listening on %q", addr) + server.Addr = ":" + port + log.Printf("Listening on %q", server.Addr) if *https != "" { - log.Fatal(http.ListenAndServeTLS(addr, *https+"/cert.pem", *https+"/key.pem", handler)) + log.Fatal(server.ListenAndServeTLS(*https+"/cert.pem", *https+"/key.pem")) } else { - log.Fatal(http.ListenAndServe(addr, handler)) + log.Fatal(server.ListenAndServe()) } } |
