// Создано: 2026-03-11 // Изменено: 2026-03-21 (v0.1.2) — panic recovery: при panic в Handle() возвращаем HTTP 500 вместо EOF. // HTTP-обёртка для serverless функций на Go 1.23. // Компилируется kaniko ВМЕСТЕ с пользовательским кодом (package handler). // // Интерфейс пользователя — файл handler.go: // // package handler // // func Handle(event map[string]interface{}) interface{} { // return map[string]interface{}{"hello": event["name"]} // } // // SLESS_MODE=job: разово вызвать Handle(event) → вывести JSON → выйти (для FunctionJob). // По умолчанию: HTTP-сервер, каждый запрос → Handle(event) → JSON ответ. package main import ( "encoding/json" "fmt" "io" "log" "net/http" "os" "sless/fn/handler" ) func main() { mode := os.Getenv("SLESS_MODE") // job-runner: разово вызвать Handle(event), вывести результат в JSON и выйти. if mode == "job" { eventJSON := os.Getenv("SLESS_EVENT") if eventJSON == "" { eventJSON = "{}" } var event map[string]interface{} if err := json.Unmarshal([]byte(eventJSON), &event); err != nil { event = map[string]interface{}{} } result := handler.Handle(event) out, _ := json.Marshal(result) fmt.Println(string(out)) return } // HTTP-сервер port := "8080" http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") fmt.Fprint(w, `{"status":"ok"}`) }) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { // Перехватываем panic в пользовательском коде — возвращаем 500 вместо EOF. // Без recover() Go закрывает соединение при панике → ingress видит EOF → 502. defer func() { if rec := recover(); rec != nil { log.Printf("panic recovered: %v", rec) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, `{"error":"panic: %v"}`, rec) } }() event := map[string]interface{}{} if r.Method == http.MethodPost || r.Method == http.MethodPut { body, err := io.ReadAll(r.Body) if err == nil && len(body) > 0 { _ = json.Unmarshal(body, &event) } } event["_path"] = r.URL.Path event["_method"] = r.Method if q := r.URL.Query(); len(q) > 0 { qmap := map[string]interface{}{} for k, v := range q { if len(v) == 1 { qmap[k] = v[0] } else { qmap[k] = v } } event["_query"] = qmap } result := handler.Handle(event) out, err := json.Marshal(result) if err != nil { http.Error(w, `{"error":"marshal failed"}`, 500) return } w.Header().Set("Content-Type", "application/json") w.Write(out) }) log.Printf("sless runtime (go1.23) listening on :%s", port) log.Fatal(http.ListenAndServe(":"+port, nil)) }