2015-08-22 19 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創建一個新的傳輸包裝器和客戶端。在這種情況下,我不應該需要鎖嗎?或者是一些內部魔法的鎖定,以及默認傳輸執行的例程? –