我正在尝试向给定的网址发出请求,并捕获重定向的网址及其后跟的状态代码。
我已经尝试寻找我的具体问题的答案 - this接近了。
但是,我还需要在整个连接上添加代理,用户代理和超时,即无论有多少重定向/代理延迟等,时间量不应超过X秒。
我通过设置请求标头来处理用户代理,并通过将其添加到Transport结构来代理。 我尝试使用CheckRedirect进行重定向 - 但这只给了我Url,我也需要状态代码,所以我必须实现RoundTrip函数。
截至目前,一切运作良好 - 超时除外。 这是迄今为止我所拥有的 - playground link 我也在这里粘贴了相关的代码 - 游乐场有一个完整的版本,里面有一个模拟重定向服务器 - 不幸的是,恐慌说连接被拒绝可能是因为操场限制 - 它完全在本地工作。
public unsafe T ConvertTo<T>(byte[] bytes, int offset)
where T: struct // not needed to work, just to eliminate some errors
{
fixed(byte* ptr = bytes)
{
return GenericPointerHelper.Read<T>(ptr + offset);
}
}
问题1:如何添加超时?
尝试#1:
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抱怨说&#34; * main.TransportWrapper不支持CancelRequest;超时不受支持&#34;
尝试#2:
client := &http.Client{ Transport: t, Timeout: myTimeout }
但Go抱怨说&#34; dt.CancelRequest undefined(类型http.RoundTripper没有字段或方法CancelRequest)&#34;
如何在不做太多的情况下实现此CancelRequest,只是让默认的CancelRequest接管?
问题2:我是否走上了一条糟糕的道路,是否有解决问题的替代方案,
给定一个Url,Proxy,UserAgent和Timeout - 返回响应以及重定向网址及其状态代码,然后到达那里。
我希望我说得恰到好处。
由于
答案 0 :(得分:4)
已经有一个用于检查重定向的挂钩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,
}