Как в Go реализовать прокси-сервер и какие типы прокси существуют?

Ответ

Прокси-сервер — это посредник между клиентом и сервером. Основные типы прокси и их реализация в Go:

1. Reverse Proxy (Обратный прокси)

Назначение: Принимает запросы от клиентов и перенаправляет их на один или несколько бэкенд-серверов. Используется для балансировки нагрузки, SSL-терминации, кэширования и сокрытия внутренней архитектуры.

Реализация в Go: Очень проста благодаря пакету net/http/httputil.

import (
    "log"
    "net/http"
    "net/http/httputil"
    "net/url"
)

func main() {
    // URL бэкенд-сервера, на который будут проксироваться запросы
    target, err := url.Parse("http://localhost:8081")
    if err != nil {
        log.Fatal(err)
    }

    // Создаем reverse proxy
    proxy := httputil.NewSingleHostReverseProxy(target)

    // Запускаем HTTP-сервер, который будет использовать прокси как обработчик
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        log.Printf("Proxying request for: %s", r.URL.Path)
        proxy.ServeHTTP(w, r)
    })

    log.Println("Reverse proxy server started on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

2. Forward Proxy (Прямой прокси)

Назначение: Принимает запросы от клиентов из внутренней сети и перенаправляет их во внешнюю сеть (интернет). Используется для обхода блокировок, фильтрации трафика, анонимизации.

Реализация в Go: Можно написать вручную, реализовав интерфейс http.Handler. Приведенный ниже пример — упрощенный и не обрабатывает HTTPS-трафик (метод CONNECT).

func handleHTTP(w http.ResponseWriter, r *http.Request) {
    // Создаем новый запрос на основе оригинального
    proxyReq, err := http.NewRequest(r.Method, r.URL.String(), r.Body)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    proxyReq.Header = r.Header

    // Выполняем запрос
    client := &http.Client{}
    resp, err := client.Do(proxyReq)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadGateway)
        return
    }
    defer resp.Body.Close()

    // Копируем заголовки и тело ответа клиенту
    for key, values := range resp.Header {
        for _, value := range values {
            w.Header().Add(key, value)
        }
    }
    w.WriteHeader(resp.StatusCode)
    io.Copy(w, resp.Body)
}

Готовые решения

Помимо самостоятельной реализации, в экосистеме Go популярны готовые прокси-серверы, написанные на Go:

  • Traefik: Современный reverse proxy и балансировщик нагрузки, который легко интегрируется с Docker и Kubernetes.
  • Caddy: Веб-сервер с автоматической поддержкой HTTPS, который также может работать как мощный reverse proxy.
  • Envoy / Nginx: Хотя они написаны не на Go, для них существуют Go-библиотеки и SDK для управления и расширения их функциональности.