// Изменено: 2026-03-07 // invoke.go — прокси-обработчик для вызова HTTP-триггеров функций. // Маршрут: ANY /fn/{namespace}/{name} // Не защищён auth-токеном — это публичный эндпоинт для вызова функций. // Проксирует запрос к ClusterIP Service функции внутри кластера: // http://{name}.sless-fn-{namespace}.svc.cluster.local:8080 // Функция должна быть задеплоена (HTTP триггер активен). package handler import ( "fmt" "io" "net/http" "time" "github.com/gorilla/mux" ) // httpClient используется для обращения к функциям внутри кластера. // Таймаут 30s — достаточно для холодного старта функции. var httpClient = &http.Client{Timeout: 30 * time.Second} // InvokeFunction проксирует входящий запрос к Service функции в кластере. // Namespace выбирается из пути, имя функции — тоже из пути. // Сохраняет метод, тело, и Content-Type заголовок оригинального запроса. func (h *Handler) InvokeFunction(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) ns := vars["namespace"] name := vars["name"] // Внутренний URL к Service функции (DNS внутри кластера) // Namespace функций: sless-fn-{user-namespace} target := fmt.Sprintf("http://%s.sless-fn-%s.svc.cluster.local:8080", name, ns) // Создаём проксируемый запрос с тем же методом и телом proxyReq, err := http.NewRequestWithContext(r.Context(), r.Method, target, r.Body) if err != nil { h.Log.Error("invoke: failed to create proxy request", "err", err, "ns", ns, "fn", name) writeJSON(w, http.StatusInternalServerError, errResp("failed to create proxy request")) return } // Пробрасываем Content-Type если есть if ct := r.Header.Get("Content-Type"); ct != "" { proxyReq.Header.Set("Content-Type", ct) } resp, err := httpClient.Do(proxyReq) if err != nil { h.Log.Error("invoke: function unreachable", "err", err, "ns", ns, "fn", name, "target", target) writeJSON(w, http.StatusBadGateway, errResp("function unreachable: "+err.Error())) return } defer resp.Body.Close() // Копируем заголовки и статус из ответа функции for k, vals := range resp.Header { for _, v := range vals { w.Header().Add(k, v) } } w.WriteHeader(resp.StatusCode) _, _ = io.Copy(w, resp.Body) }