Сегодня мы рассмотрим как обрабатывать HTTP-запросы в Go и предотвращать автоматические переходы на 301, 302 и подобные перенаправления. Это может понадобиться для раскрытия сокращённых ссылок из twitter, buffer, bit.ly или маркетинговых рассылок. Или для проверки ваших утилит для генерации таких ссылок 😉 Демо-версию можно посмотреть тут.
Внимание: для дальнейшей работы понадобится go не старше версии 1.7, иначе вам будут недоступны некоторые важные функции пакета net/http. Узнать версию Go можно командой go version в терминале. Ответ должен быть примерно таким: go version go1.7.3 linux/amd64.
Создание HTTP запросов в Go
Посмотрим как можно создать простой HTTP запрос в Go и прочитать его код состояния:
// file: http-request.go
package main
import(
"fmt"
"net/http"
)
func main(){
resp, err := http.Get("http://www.jonathanmh.com/")
if err != nil {
fmt.Println(err)
}
fmt.Println("StatusCode:", resp.StatusCode)
fmt.Println(resp.Request.URL)
}
Ответ:
StatusCode: 200
https://jonathanmh.com/
Обратите внимание, что в resp.Request.URL мы получили совсем не тот адрес, что передали методу Get. Это произошло потому, что я перенаправляю пользователей с адреса www.jonathanmh.com на jonathanmh.com. Go делает всё как положено и просто следует правилам. В большинстве случаев такое поведение мы и ожидаем, но только не в том, случае, когда хотим сделать что-то вроде утилиты для проверки перенаправлений! В этом случае мы хотим знать каждый шаг перенаправления и код состояния каждого запроса.
Создание HTTP запросов в Go без следования редиректам
Для того, чтобы не следовать автоматическим перенаправлениям, создадим свой экземпляр http.Client с методом проверки CheckRedirect. Это поможет нам возвращать код состояния и адрес до перенаправления.
// file: http-nofollow-request.go
package main
import(
"fmt"
"net/http"
)
func main(){
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
} }
resp, err := client.Get("http://www.jonathanmh.com")
if err != nil {
fmt.Println(err)
}
fmt.Println("StatusCode:", resp.StatusCode)
fmt.Println(resp.Request.URL)
}
Ответ:
StatusCode: 301
http://www.jonathanmh.com
Супер! Мы пока не получили URL перенаправления, но у нас есть код состояния, что уже неплохо. Итак, продолжим.
Перебор HTTP-перенаправлений в Go
Теперь рассмотрим как можно обойти все перенаправления для получения конечного адреса. Сперва нам нужно задать лимит для запросов (иначе нас смогут отправить в бесконечную петлю перенаправлений) и добавить условие окончания перебора.
Будем считать, что получив код состояния 200, наш обход будет закончен и мы получим финальный адрес.
// file: http-redir-loop.go
package main
import(
"fmt"
"net/http"
)
func main(){
myURL := "http://www.jonathanmh.com"
nextURL := myURL
var i int
for i < 100 {
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
} }
resp, err := client.Get(nextURL)
if err != nil {
fmt.Println(err)
}
fmt.Println("StatusCode:", resp.StatusCode)
fmt.Println(resp.Request.URL)
if resp.StatusCode == 200 {
fmt.Println("Done!")
break
} else {
nextURL = resp.Header.Get("Location")
}
}
}
Ответ:
StatusCode: 301
StatusCode: 301
StatusCode: 200
Done!
Этот код практически повторяет предыдущий пример, однако здесь мы добавили пару переменных, myURL и nextURL. И в конце цикла, если состояние не 200 - OK, мы присваиваем переменной nextURL значение resp.Header.Get("Location") - это HTTP заголовок цели перенаправления. Вы можете отслеживать эти заголовки на вкладке Сеть (Network) в Инструментах разработчика (Developer Tools) в Chrome или Firefox, поставив галочку Preserve Log.
Вот и всё! Теперь вы знаете, как делать несколько прикольных штук с net/http в Go. Теперь вы можете обернуть этот код в http API, запустить программу на сервере, ну и, конечно же, добавить ей простенький интерфейс, вроде того, как я сделал тут.
Для дальнейшего изучения
Одной статьёй не охватить всего, поэтому рекомендую почитать следующие материалы по теме:
- пакет net/http
- stackoverflow: Как мне сделать HTTP клиента в Go без автоматического перенаправления? (англ.)
- echo golang web framework
Буду рад, если поделитесь мыслями по поводу материала и если он был полезен вам!
От переводчика: вы можете прокомментировать как оригинальный пост, так и перевод, но автор будет очень рад комментариям в своём блоге 🙂
Источник: "Tracing or Preventing HTTP Redirects in Golang"
Разработчик: java, kotlin, c#, javascript, dart, 1C, python, php.
Пишите: @ighar. Buy me a coffee, please :).
i++ забыл