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