首页 > 解决方案 > 如果在 http.Server 中超时,为什么请求会在 Firefox 中无限重复?

问题描述

我在 golang 中设置了一个带有超时的简单服务器。当运行一个比超时时间更长的处理程序时,如果我用 Firefox 请求它,请求会无限期地重复。但是,如果我使用 Postman 或 curl,则不会重复请求。我想防止浏览器中的重复循环。

我试图手动关闭请求正文或检查上下文是否被取消,但是这些方法都不起作用。

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        defer r.Body.Close()
        fmt.Printf("Hello, you've requested: %s\n", r.URL.Path)
        time.Sleep(time.Second * 2)
        fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path)
    })
    s := http.Server{
        Addr:         ":8080",
        Handler:      http.DefaultServeMux,
        ReadTimeout:  1 * time.Second,
        WriteTimeout: 1 * time.Second,
    }
    s.ListenAndServe()
}

我希望处理程序退出而不是重复。

标签: httpgofirefoxservertimeout

解决方案


据我了解,您面临的问题是服务器超时突然关闭底层 tcp conn 而没有编写正确的 http 响应,同时,当 firefox 检测到 conn 突然关闭时,它似乎决定重试 N 次,可能是因为它假设它遇到连接问题。

我相信解决方案是使用http.Handler来控制处理程序处理持续时间并在超时到期时返回正确的 HTTP 响应。

服务器超时应该更长,用于防止异常客户端行为,而不是处理程序缓慢。

标准 HTTP 包为此提供了TimeoutHandler函数。

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    slowHandler := func(w http.ResponseWriter, r *http.Request) {
        defer r.Body.Close()
        fmt.Printf("Hello, you've requested: %s\n", r.URL.Path)
        time.Sleep(time.Second * 2)
        fmt.Fprintf(w, "Hello, you've requested: %s\n", r.URL.Path)
    }
    http.HandleFunc("/", slowHandler)

    var handler http.Handler = http.DefaultServeMux
    handler = http.TimeoutHandler(handler, time.Second, "processing timeout")

    s := http.Server{
        Addr:    ":8080",
        Handler: handler,
        // ReadTimeout:  1 * time.Second,
        // WriteTimeout: 1 * time.Second,
    }
    s.ListenAndServe()
}

推荐阅读