package main
import (
"bytes"
"errors"
"fmt"
"github.com/levigross/grequests"
"golang.org/x/net/html/charset"
"io/ioutil"
)
var workerComplete = make(chan []string)
func fetch(url string) (string, error) {
response, err := grequests.Get(url, nil)
if err != nil {
return "", errors.New("Ошибка при выполнении запроса: " + err.Error())
}
defer response.ClearInternalBuffer()
raw_page, err := charset.NewReader(bytes.NewReader(response.Bytes()), response.Header.Get("Content-Type"))
if err != nil {
return "", errors.New("Ошибка при определении кодировки: " + err.Error())
}
unicodePage, err := ioutil.ReadAll(raw_page)
if err != nil {
return "", errors.New("Ошибка при чтении декодированного response : " + err.Error())
}
return string(unicodePage), nil
}
func downloadWebPage(url_ string, pageDownloaded chan []string) {
html, err := fetch(url_)
if err != nil {
err = errors.New("Ошибка при получении данных со страницы " + url_ + " : " + err.Error())
pageDownloaded <- []string{"", err.Error()}
}
pageDownloaded <- []string{html, ""}
}
func main() {
urls := []string{"http://golang-book.ru/chapter-10-concurrency.html",
"http://golang-book.ru/chapter-10-concurrency.html", "https://gobyexample.com/json"}
urlsCount := 0
for _, url_ := range urls {
urlsCount += 1
go downloadWebPage(url_, workerComplete)
}
for i := 0; i < urlsCount; {
result := <-workerComplete
html, err := result[0], result[1]
if err != "" {
fmt.Println(err)
}
fmt.Println(html[:10])
i += 1
}
}
В стандартной библиотеке Go есть возможность отправлять запросы с контекстом: https://golang.org/pkg/net/http/#Request.WithContext
Если контекст отменится, запрос тут же прервется и вернёт ошибку.
Использовать примерно так:
ctx, cancel := context.WithCancel(context.Background())
req := &Request{...}
req = req.WithContext(ctx)
resp, err := http.DefaultTransport.RoundTrip(req)
--
Вы получили это сообщение, поскольку подписаны на группу "Golang Russian".
Чтобы отменить подписку на эту группу и больше не получать от нее сообщения, отправьте письмо на электронный адрес golang-ru+...@googlegroups.com.
Чтобы настроить другие параметры, перейдите по ссылке https://groups.google.com/d/optout.
package main
import (
"context"
"net/http"
"time"
"fmt"
)
func main() {
cx, cancel := context.WithCancel(context.Background())
req, _ := http.NewRequest("GET", "http://google.com", nil)
req = req.WithContext(cx)
ch := make(chan []string)
go func() {
_, err := http.DefaultClient.Do(req)
select {
case <-cx.Done():
default:
ch <- []string{"body", err.Error()}
}
}()
// Simulating user cancel request
go func() {
time.Sleep(100 * time.Millisecond)
fmt.Println(cancel)
//cancel()
}()
select {
case err := <-ch:
if err != nil {
// HTTP error
fmt.Println("error without calcel")
}
fmt.Println("body")
case <-cx.Done():
fmt.Println("cancel")
}
}
req, _ := http.NewRequest("GET", "http://google.com", nil)
_, err := http.DefaultClient.Do(req)1. Вы делаете запрос без использования контекста - прервать его невозможно. Вам надо перед этой строчкой вставить
select {
case <-cx.Done():
default:
ch <- []string{"body", err.Error()}
}
select {
case err := <-ch:
if err != nil {
// HTTP error
fmt.Println("error without calcel")
}
fmt.Println("body")
case <-cx.Done():
fmt.Println("cancel")
}
...
package main
import (
"context"
"net/http"
"time"
"fmt"
"io/ioutil"
)
var ch = make(chan []string)
func main() {
cx, cancel := context.WithCancel(context.Background())
req, _ := http.NewRequest("GET", "http://google.com", nil)
req = req.WithContext(cx)
go func() {
resp, err := http.DefaultClient.Do(req)
if err != nil {
ch <- []string{"", err.Error()}
return
}
data, err := ioutil.ReadAll(resp.Body)
if err != nil {
// Соединение прервалось на середине и данные не докачались.
ch <- []string{"", err.Error()}
return
}
resp.Body.Close()
ch <- []string{string(data), ""}
}()
// Simulating user cancel request
go func() {
time.Sleep(100 * time.Millisecond)
fmt.Println(cancel)
//cancel()
}()
res := <-ch
if res[1] != "" {
select {
case <-cx.Done():
// Отмена пользователем.
fmt.Println("Запрос отменен пользователем")
default:
// Запрос не удался.
fmt.Println("Ошибка при выполнении запроса: ", res[1])
}
} else {
// Нормальный ответ.
fmt.Println("Нормальный ответ: ", res[0][:50])
}
}
res := <-ch
if res[1] != "" {
select {
case <-cx.Done():
// Отмена пользователем.
fmt.Println("Запрос отменен пользователем")
default:
// Запрос не удался.
fmt.Println("Ошибка при выполнении запроса: ", res[1])
}
} else {
// Нормальный ответ.
Как можно вернуть результат запроса и ошибку? Учитывая строгую типизацию я не знаю как мне это сделать.
--
package main
import (
"context"
"net/http"
"time"
"fmt"
"io/ioutil"
)
type DownloadResult struct {
page string
err error
}
var ch = make(chan DownloadResult)
func main() {
cx, cancel := context.WithCancel(context.Background())
req, _ := http.NewRequest("GET", "http://google.com", nil)
req = req.WithContext(cx)
go func() {
resp, err := http.DefaultClient.Do(req)
if err != nil {
ch <- DownloadResult{
page: "",
err: err,
}
return
}
data, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
// Соединение прервалось на середине и данные не докачались.
ch <- DownloadResult{
page: "",
err: err,
}
return
}
ch <- DownloadResult{
page: string(data),
err: nil,
}
}()
// Simulating user cancel request
go func() {
time.Sleep(100 * time.Millisecond)
fmt.Println(cancel)
//cancel()
}()
res := <-ch
if res.err != nil {
if res.err == context.Canceled {
fmt.Println("Отмена загрузки")
} else {
fmt.Println("Ошибка при загрузке: ", res.err)
}
} else {
fmt.Println("Загрузка завершена\n", res.page[:50])
}
}
Ну да, типа того. Работает?
Может http заворачивает ошибку в свою ошибку. Нехорошо, конечно, с его стороны. Тогда сделайте проверку - если ошибка пришла, тогда проверяйте - если контекст отменен, то одно сообщение, если не отменен - другое. Через select-default
--