package host import ( "bytes" "context" "encoding/json" "errors" "fmt" "io" "net" "net/http" "time" execlease "github.com/carlosframework/platform/internal/hostexec" ) func (h ClaimRootHTTP) serveExecution(w http.ResponseWriter, r *http.Request, peer StopPeer) { var q execlease.ExecutionRequest dec := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<10)) dec.DisallowUnknownFields() if dec.Decode(&q) != nil || dec.Decode(new(any)) != io.EOF { w.WriteHeader(http.StatusBadRequest) return } if h.Execution == nil { w.WriteHeader(http.StatusServiceUnavailable) return } out, err := h.Execution.Execute(r.Context(), execlease.ControllerProcess{KernelBootID: peer.BootID, PID: peer.PID, Start: peer.ProcessStart}, q) if errors.Is(err, errReinstallRetired) { w.WriteHeader(http.StatusGone) return } if err != nil { w.WriteHeader(http.StatusConflict) return } w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(out) } func rootExecutionClient(path string) func(context.Context, execlease.ExecutionRequest) (execlease.ExecutionResponse, error) { return func(ctx context.Context, q execlease.ExecutionRequest) (execlease.ExecutionResponse, error) { var out execlease.ExecutionResponse raw, err := json.Marshal(q) if err != nil { return out, err } ctx, cancel := context.WithTimeout(ctx, 30*time.Second) defer cancel() tr := &http.Transport{DisableKeepAlives: true, DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { return (&net.Dialer{}).DialContext(ctx, "unix", path) }} defer tr.CloseIdleConnections() for attempt := 0; attempt < 2; attempt++ { req, e := http.NewRequestWithContext(ctx, http.MethodPost, "http://control/claim/execution", bytes.NewReader(raw)) if e != nil { return out, e } resp, e := (&http.Client{Transport: tr}).Do(req) if e != nil { err = e continue } if resp.StatusCode == http.StatusGone { resp.Body.Close() return out, fmt.Errorf("%s: %w", q.Host, errReinstallRetired) } if resp.StatusCode != http.StatusOK { resp.Body.Close() return out, execlease.ErrLost } limited := &io.LimitedReader{R: resp.Body, N: (16 << 10) + 1} dec := json.NewDecoder(limited) dec.DisallowUnknownFields() err = dec.Decode(&out) if err == nil && (dec.Decode(new(any)) != io.EOF || limited.N == 0) { err = execlease.ErrState } resp.Body.Close() if err != nil { continue } serving := q.Operation == "executionView" && q.Token == "" && out.Status == "serving" && out.Token == "" && out.SafeUntil == 0 && out.LeaseETag == "" && out.Expires.IsZero() if q.Operation == "executionView" && q.Token == "" && !serving { return execlease.ExecutionResponse{}, execlease.ErrState } if out.Schema != 1 || out.Host != q.Host || out.Incarnation != q.Incarnation || out.Stamp != q.Stamp || out.Session != q.Session || out.Op != q.Op || (!serving && out.Token == "") || out.BootID == "" { return execlease.ExecutionResponse{}, execlease.ErrState } return out, nil } return out, err } }