URL Checker

HTTP client with timeout, context, and retry for checking if a bookmark URL is alive.

package main

import (
	"context"
	"fmt"
	"math"
	"net/http"
	"time"
)

func checkURL(ctx context.Context, url string) (int, error) {
	client := &http.Client{Timeout: 10 * time.Second}

	req, err := http.NewRequestWithContext(ctx, "HEAD", url, nil)
	if err != nil {
		return 0, fmt.Errorf("create request: %w", err)
	}

	resp, err := client.Do(req)
	if err != nil {
		return 0, fmt.Errorf("fetch %s: %w", url, err)
	}
	defer resp.Body.Close()

	return resp.StatusCode, nil
}

func checkWithRetry(ctx context.Context, url string, maxRetries int) (int, error) {
	var lastErr error
	for attempt := 0; attempt <= maxRetries; attempt++ {
		if attempt > 0 {
			backoff := time.Duration(math.Pow(2, float64(attempt-1))) * time.Second
			select {
			case <-time.After(backoff):
			case <-ctx.Done():
				return 0, ctx.Err()
			}
		}

		status, err := checkURL(ctx, url)
		if err == nil {
			return status, nil
		}
		lastErr = err
		fmt.Printf("attempt %d failed: %v\n", attempt+1, err)
	}
	return 0, fmt.Errorf("all %d attempts failed: %w", maxRetries+1, lastErr)
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	urls := []string{
		"https://go.dev",
		"https://pkg.go.dev",
		"https://example.com",
		"https://thisdomaindoesnotexist.invalid",
	}

	for _, url := range urls {
		status, err := checkWithRetry(ctx, url, 2)
		if err != nil {
			fmt.Printf("FAIL %s: %v\n", url, err)
		} else {
			fmt.Printf("OK   %s: %d\n", url, status)
		}
	}
}

💻 Run locally

Copy the code above and run it on your machine

© 2026 ByteLearn.dev. Free courses for developers. · Privacy