Go lang使用超时捕获重定向网址和状态代码

Abh*_*nna 5 redirect http go

我正在尝试向给定的URL发出请求,并捕获重定向的URL及其后面的状态代码.

我已经尝试寻找我的具体问题的答案 - 这很接近.

但是,我还需要在整个连接上添加代理,用户代理和超时,即无论有多少重定向/代理延迟等,时间量不应超过X秒.

我通过设置请求标头来处理user-agent,并通过将其添加到Transport结构来代理.我尝试探索CheckRedirect的重定向 - 但这只给了我Url,我也需要状态代码,所以我必须实现RoundTrip函数.

到目前为止一切运作良好 - 除了Timeout.继承人到目前为止我所拥有的 - 游乐场链接 我也在这里粘贴了相关的代码 - 游乐场有一个完整版本,有一个模拟重定向服务器 - 不幸的是,恐慌说连接被拒绝可能是因为游乐场的限制 - 它完全在本地工作虽然.

type Redirect struct {
    StatusCode int
    URL string
}

type TransportWrapper struct {
    Transport http.RoundTripper
    Url string
    Proxy string
    UserAgent string
    TimeoutInSeconds int
    FinalUrl string
    RedirectUrls []Redirect
}
// Implementing Round Tripper to capture intermediate urls
func (t *TransportWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
    transport := t.Transport
    if transport == nil {
        transport = http.DefaultTransport
    }

    resp, err := transport.RoundTrip(req)
    if err != nil {
        return resp, err
    }

    // Remember redirects
    if resp.StatusCode >= 300 && resp.StatusCode <= 399 {
        t.RedirectUrls = append(
            t.RedirectUrls, Redirect{resp.StatusCode, req.URL.String()},
        )
    }
    return resp, err
}

func (t *TransportWrapper) Do() (*http.Response, error) {
    t.Transport = &http.Transport{}
    if t.Proxy != "" {
        proxyUrl, err := url.Parse(t.Proxy)
        if err != nil {
            return nil, err
        }

        t.Transport = &http.Transport{Proxy:http.ProxyURL(proxyUrl)}
        // HELP
        // Why does this fail
        // t.Transport.Proxy = http.ProxyUrl(proxyUrl)
    }

    client := &http.Client{
        Transport: t, // Since I've implemented RoundTrip I can pass this
        // Timeout: t.TimeoutInSeconds * time.Second, // This Fails 
    }

    req, err := http.NewRequest("GET", t.Url, nil)
    if err != nil {
        return nil, err
    }

    if t.UserAgent != "" {
        req.Header.Set("User-Agent", t.UserAgent)
    }

    resp, err := client.Do(req)
    if err != nil {
        return nil, err
    }

    t.FinalUrl = resp.Request.URL.String()
    return resp, nil
}

func startClient() {
    t := &TransportWrapper {
        Url: "http://127.0.0.1:8080/temporary/redirect?num=5",
        // Proxy
        // UserAgent
        // Timeout
    }

    _, err := t.Do()
    if err != nil {
        panic(err)
    }

    fmt.Printf("Intermediate Urls: \n")
    for i, v := range t.RedirectUrls {
        fmt.Printf("[%d] %s\n", i, v)
    }

}
Run Code Online (Sandbox Code Playgroud)

问题1:如何添加超时?

尝试#1:

client := &http.Client{ Transport: t, Timeout: myTimeout }
Run Code Online (Sandbox Code Playgroud)

但Go抱怨说"*main.TransportWrapper不支持CancelRequest;超时不支持"

尝试#2:

// Adding a CancelRequest
func (t *TransportWrapper) CancelRequest(req *http.Request) {
    dt := http.DefaultTransport
    dt.CancelRequest(req)
}
Run Code Online (Sandbox Code Playgroud)

但Go抱怨说"dt.CancelRequest undefined(类型http.RoundTripper没有字段或方法CancelRequest)"

如何在不做太多的情况下实现此CancelRequest,只是让默认的CancelRequest接管?

问题2:我是否走上了一条糟糕的道路,是否可以选择解决问题,

给定一个Url,Proxy,UserAgent和Timeout - 返回响应以及重定向URL及其状态代码,然后到达那里.

我希望我说得恰到好处.

谢谢

Jim*_*imB 4

已经有一个用于检查重定向的钩子,Client.CheckRedirect.

您可以提供回调来执行您想要的操作。

如果您确实想创建自己的传输来扩展其他功能,则需要提供一个CancelRequest类似于错误所示的方法来处理Client.Timeout

func (t *TransportWrapper) CancelRequest(req *Request) {
    t.Transport.CancelRequest(req)
}
Run Code Online (Sandbox Code Playgroud)

更常见的是,您可以嵌入Transport,以便自动提升所有方法和字段。然而,您应该避免在传输中使用可写字段,因为它应该可以安全地同时使用,否则您应该使用互斥锁来保护所有访问,或者您必须确保它仅在一个 goroutine 中使用。

一个最小的例子如下:

type TransportWrapper struct {
    *http.Transport
    RedirectUrls []Redirect
}

func (t *TransportWrapper) RoundTrip(req *http.Request) (*http.Response, error) {
    transport := t.Transport
    if transport == nil {
        transport = http.DefaultTransport.(*http.Transport)
    }

    resp, err := transport.RoundTrip(req)
    if err != nil {
        return resp, err
    }

    // Remember redirects
    if resp.StatusCode >= 300 && resp.StatusCode <= 399 {
        fmt.Println("redirected")
        t.RedirectUrls = append(
            t.RedirectUrls, Redirect{resp.StatusCode, req.URL.String()},
        )
    }
    return resp, err
}
Run Code Online (Sandbox Code Playgroud)

然后您可以在客户端中使用超时:

client := &http.Client{
    Transport: &TransportWrapper{
        Transport: http.DefaultTransport.(*http.Transport),
    },
    Timeout: 5 * time.Second,
}
Run Code Online (Sandbox Code Playgroud)