Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/.htaccess/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Http Go lang捕获重定向URL和带有超时的状态代码_Http_Redirect_Go - Fatal编程技术网

Http Go lang捕获重定向URL和带有超时的状态代码

Http Go lang捕获重定向URL和带有超时的状态代码,http,redirect,go,Http,Redirect,Go,我正试图向给定的url发出请求,并捕获重定向url及其随后的状态代码 我已经试着为我的具体问题寻找答案——很接近 但是,我还需要在整个连接上添加代理、用户代理和超时,即无论有多少重定向/代理延迟等,时间量不应超过X秒 我通过设置请求头来处理用户代理,通过将其添加到传输结构来处理代理。 我尝试探索CheckRedirect的重定向-但这只给了我Url,我还需要状态代码,所以我必须实现往返功能 现在一切正常-除了超时。 以下是我到目前为止得到的- 我也在这里粘贴了相关的代码-游乐场有一个完整的版本,

我正试图向给定的url发出请求,并捕获重定向url及其随后的状态代码

我已经试着为我的具体问题寻找答案——很接近

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

我通过设置请求头来处理用户代理,通过将其添加到传输结构来处理代理。 我尝试探索CheckRedirect的重定向-但这只给了我Url,我还需要状态代码,所以我必须实现往返功能

现在一切正常-除了超时。 以下是我到目前为止得到的- 我也在这里粘贴了相关的代码-游乐场有一个完整的版本,有一个模拟重定向服务器-不幸的是,它担心连接被拒绝,可能是因为游乐场的限制-但它完全在本地工作

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)
    }

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

尝试#2:

但是Go抱怨说“dt.CancelRequest未定义(类型http.RoundTripper没有字段或方法CancelRequest)”

我如何实现这个CancelRequest而不做太多事情,让默认的CancelRequest接管

问题2:我是否走上了一条糟糕的道路,是否有解决问题的替代方案

给定一个Url、代理、用户代理和超时-返回响应以及重定向Url及其状态代码

我希望我的措辞恰当


谢谢

已经有一个用于检查重定向的钩子

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

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

func (t *TransportWrapper) CancelRequest(req *Request) {
    t.Transport.CancelRequest(req)
}
更常见的是,您将嵌入
传输
,以便自动升级所有方法和字段。但是,您应该避免在传输中使用可写字段,因为并发使用是安全的,否则您应该使用互斥锁保护所有访问,或者您必须确保它只在一个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
}

Client.CheckRedirect
允许我捕获重定向URL,但我也想知道它们的状态代码,即301、302等,我相信这些代码表示我需要创建自己的往返程序。我在这里尝试了CancelRequest,但没有什么区别——我让服务器休眠5秒,超时1秒,但仍然没有成功。使用sleep和cancelrequest更新的代码是@AbhishekShivanna:我不知道为什么cancelrequest在没有代码和错误的情况下对您不起作用,但我给出了一个简单的示例。我也会避免将
Do
方法放在
Transport
中,因为它属于
客户机
,并且使代码与两者之间的循环引用混淆。非常感谢!我决定采用嵌入的方法。当您谈到并发保护时,是不是因为同一个传输包装器也可能在另一个客户机中使用?我计划为每个URL创建一个新的传输包装器和客户机,我希望它的所有重定向。在这种情况下,我不需要锁,对吗?或者是默认传输执行的一些内部magic and go例程的锁?在给出响应之前,我让服务器休眠了5秒钟。这会导致死机:运行时错误:无效内存地址或零指针取消引用。它指向
t.reqMu.Lock()
。即使在我得到响应之后,它似乎也在调用CancelRequest。我通过在请求成功完成后运行sleep来测试这一点。我发现这是因为我没有执行
resp.Body.Close()
。在成功的跑步中,我可以做到这一点,但在往返中呢?我不想把尸体关在那里,对吗?
func (t *TransportWrapper) CancelRequest(req *Request) {
    t.Transport.CancelRequest(req)
}
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
}
client := &http.Client{
    Transport: &TransportWrapper{
        Transport: http.DefaultTransport.(*http.Transport),
    },
    Timeout: 5 * time.Second,
}