2015-08-22 20 views
4

我正在尝试向给定的URL发出请求,并捕获后续的重定向URL及其状态代码。Go lang Capture重定向URL和超时状态代码

我试过寻找我的具体问题的答案 - this接近。

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

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

现在一切正常 - 除了超时。 继承人我到目前为止 - playground link 我已经粘贴了相关的代码 - 操场上有一个完整版本的模拟重定向服务器 - 不幸的是,它恐慌说连接被拒绝可能是因为操场限制 - 它的工作完全尽管在本地。

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

} 

问题1:如何添加超时?

尝试#1:

client := &http.Client{ Transport: t, Timeout: myTimeout } 

而去笙歌说: “* main.TransportWrapper不支持CancelRequest;不支持超时”

尝试#2:

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

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

如何在不做太多的情况下实现这个CancelRequest,并让默认的CancelRequest接管?

问题2:有我走了下来坏路,是有解决问题的替代方案,

获得一个网址,代理,用户代理和超时 - 返回与重定向URL及其状态码响应随后到达那里。

我希望我适当地说过这句话。

感谢

回答

3

已经有用于检查重定向钩,Client.CheckRedirect

你可以提供一个回调来做你想做的事情。

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

func (t *TransportWrapper) CancelRequest(req *Request) { 
    t.Transport.CancelRequest(req) 
} 

更常见的是,你会嵌入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 
} 

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

client := &http.Client{ 
    Transport: &TransportWrapper{ 
     Transport: http.DefaultTransport.(*http.Transport), 
    }, 
    Timeout: 5 * time.Second, 
} 
+0

的'Client.CheckRedirect'让我捕捉到重定向的URL,但我也想知道他们的状态代码,即301,302等,我相信[这个SO回答](http://stackoverflow.com/questions/24577494/how-to-get-the-http-redirect-status代码在golang)说我需要创建我自己的RoundTripper。我在这里尝试了CancelRequest,但没有任何区别 - 我让服务器睡了5秒,并在一秒钟超时,仍然没有运气。更新的代码与睡眠和取消请求是[这里](http://play.golang.org/p/zOxvQdCjOz) –

+0

@AbhishekShivanna:我不知道为什么CancelRequest不适合你没有代码和错误,但我举了一个最小的例子。我也会避免将'Do'方法放在'Transport'中,因为它属于'Client',并且使代码混淆,并在两者之间引用循环引用。 – JimB

+0

非常感谢!我决定采用嵌入的方法。当你谈论并发保护时,是否因为同一个传输包装可能在另一个客户中使用?我计划为每个我想要的所有重定向的URL创建一个新的传输包装器和客户端。在这种情况下,我不应该需要锁吗?或者是一些内部魔法的锁定,以及默认传输执行的例程? –