package usecase import ( "context" "errors" "fmt" "net/http" "net/url" "gitea.maximumdirect.net/eric/promptkit/internal/domain" "gitea.maximumdirect.net/eric/promptkit/internal/generation" "gitea.maximumdirect.net/eric/promptkit/internal/llm" ) type smokeHTTPGetter interface { GetWithEvidence(context.Context, string, int) (*http.Response, llm.HTTPExecutionEvidence, error) GetWithAuthenticationEvidence(context.Context, string, int, domain.ExecutionTarget) (*http.Response, llm.HTTPExecutionEvidence, error) } type reachabilityProbe struct { client smokeHTTPGetter } type reachabilityProbeOutcome struct { supported bool reason domain.SmokeTestReason initialEndpoint string reachable *bool evidence llm.HTTPExecutionEvidence failure domain.ExecutionFailureKind } func (p reachabilityProbe) run( ctx context.Context, endpoint string, timeoutSeconds int, ) (reachabilityProbeOutcome, error) { if p.client == nil { return reachabilityProbeOutcome{ reason: domain.SmokeTestReasonHTTPDiagnosticsUnavailable, }, nil } endpoint, err := endpointWithoutUserInfo(endpoint) if err != nil { return reachabilityProbeOutcome{}, fmt.Errorf("prepare reachability endpoint: %w", err) } result := reachabilityProbeOutcome{supported: true, initialEndpoint: endpoint} response, evidence, requestErr := p.client.GetWithEvidence(ctx, endpoint, timeoutSeconds) if response != nil && response.Body != nil { _ = response.Body.Close() } result.evidence = evidence if evidence.ResponseReceived || response != nil { result.reachable = boolPointer(true) return result, nil } if evidence.DispatchInvoked { result.reachable = boolPointer(false) } if requestErr != nil { result.failure = httpProbeFailure(ctx, requestErr) if !evidence.DispatchInvoked { return result, fmt.Errorf("execute reachability request: %w", requestErr) } } return result, nil } // Failed I/O can lose context error identity. Check the caller at that boundary, // without reclassifying a probe that already established its result. func httpProbeFailure(ctx context.Context, err error) domain.ExecutionFailureKind { if contextErr := ctx.Err(); contextErr != nil { return generation.ClassifyFailure(contextErr) } return generation.ClassifyFailure(errors.Join(llm.ErrTransport, err)) } func endpointWithoutUserInfo(endpoint string) (string, error) { parsed, err := url.Parse(endpoint) if err != nil { return "", err } parsed.User = nil return parsed.String(), nil } func boolPointer(value bool) *bool { return &value }