Detail http's request for redirect in golang development

  • 2020-11-18 06:17:23
  • OfStack

These two days, I encountered a problem in the development project. I requested an URL, and it would send 302 to another address. The original intention was to check whether this URL would do the redirect jump of 3XX, but each time reqeust would return the result of the last hop. Then looked at the source code, to understand the mechanism of the request jump

The implementation code

Take a look at the simple code for the implementation


func main() {
 client := &http.Client{}
 url := "http://www.qq.com"
 reqest, err := http.NewRequest("GET", url, nil)
 if err != nil {
 panic(err)
 }
 response, _ := client.Do(reqest)
 fmt.Println(response.Status)
}

curl http://www.qq.com
<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>stgw/1.3.12.4_1.13.5</center>
</body>
</html>

We know that in the browser input http: / / www qq. com will jump to 302 https: / / www qq. com. Using curl we can see a jump using 302.

However, I only want to get the status code of response for the first hop. I found it impossible to implement, so I looked at the source code.

Why can http requests be done multiple times

Take a look at the client. Do source code implementation


607 err = c.checkRedirect(req, reqs)

In the context of the code, you can see that req is the request to be requested and request has been requested by reqs

Take a look at checkRedirect


func (c *Client) checkRedirect(req *Request, via []*Request) error {
 fn := c.CheckRedirect
 if fn == nil {
 fn = defaultCheckRedirect
 }
 return fn(req, via)
}

You can see that checkRedirect is performed if checkRedirect is set, and defaultCheckRedirect is performed if it is not set.

See defaultCheckRedirect again


func defaultCheckRedirect(req *Request, via []*Request) error {
 if len(via) >= 10 {
 return errors.New("stopped after 10 redirects")
 }
 return nil
}

You can see that redirect can be used up to 10 times, and jumps greater than 10 will throw an error to end the request.

The general process has been worked out. As long as you set checkRedirect to return error, you can theoretically achieve the goal of only requesting once.


func main() {
 client := &http.Client{}
 url := "http://www.qq.com"
 reqest, err := http.NewRequest("GET", url, nil)
 if err != nil {
 panic(err)
 }
 client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
 return fmt.Errorf("first response")
 }
 response, _ := client.Do(reqest)
 fmt.Println(response.StatusCode)
}

/private/var/folders/4h/lrsc4fyd12v9ctl31ggk5ckc0000gp/T/___go_build_main_go #gosetup
302

It's basically done.

In fact, there's a line above the CheckRedirect method that says,

[

ErrUseLastResponse can be returned by Client.CheckRedirect hooks to control how redirects are processed. If returned, the next request is not sent and the most recent response is returned with its body unclosed.

Client.CheckRedirect hooks can return ErrUseLastResponse to control how redirects are handled. If returned, the next request is not sent, and the most recent response is returned with its body not closed.

]

You can see that returning ErrUseLastResponse is the official recommended setting

The final code implementation should look like this.


func main() {
 client := &http.Client{}
 url := "http://www.qq.com"
 reqest, err := http.NewRequest("GET", url, nil)
 if err != nil {
 panic(err)
 }
 client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
 return http.ErrUseLastResponse
 }
 response, _ := client.Do(reqest)
 fmt.Println(response.StatusCode)
}

Related articles: