Программа server
Программа server обрабатывает запросы, такие как /search?q=golang
, обслуживая первые несколько результатов поиска Google для golang. Она регистрирует handleSearch для обработки endpoint /search
. Обработчик создает начальный Context с именем ctx
и организует его отмену при возврате обработчика. Если запрос включает в себя параметр timeout
в URL-адресе, Context отменится автоматически по истечении таймаута:
func handleSearch(w http.ResponseWriter, req *http.Request) {
// ctx - это Context для данного обработчика.
// Вызов cancel закрывает канал ctx.Done,
// который является сигналом отмены запросов,
// запущенных этим обработчиком.
var (
ctx context.Context
cancel context.CancelFunc
)
timeout, err := time.ParseDuration(req.FormValue("timeout"))
if err == nil {
// Запрос имеет timeout, поэтому создайте context,
// который автоматически отменяется по истечении времени ожидания.
ctx, cancel = context.WithTimeout(context.Background(), timeout)
} else {
ctx, cancel = context.WithCancel(context.Background())
}
defer cancel() // Отмена ctx при возврате handleSearch.
Обработчик берет query из запроса и извлекает IP-адрес клиента, вызывая пакет userip. IP-адрес клиента необходим для серверных запросов, поэтому handleSearch прикрепляет его к ctx:
// Проверка поискового запроса
query := req.FormValue("q")
if query == "" {
http.Error(w, "no query", http.StatusBadRequest)
return
}
// Сохранение IP пользователя в ctx для дальнейшего использования в других пакетах
userIP, err := userip.FromRequest(req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
ctx = userip.NewContext(ctx, userIP)
Обработчик вызывает google.Search с ctx и query:
// Запуск Google поиска и вывод результатов.
start := time.Now()
results, err := google.Search(ctx, query)
elapsed := time.Since(start)
Если поиск прошел успешно, обработчик отобразит результаты:
if err := resultsTemplate.Execute(w, struct {
Results google.Results
Timeout, Elapsed time.Duration
}{
Results: results,
Timeout: timeout,
Elapsed: elapsed,
}); err != nil {
log.Print(err)
return
}