mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 07:40:42 +00:00
fix: proxy direct APNs HTTP2 sessions (#74905)
Summary: - This PR routes direct APNs HTTP/2 sends through an APNs allowlisted managed-proxy CONNECT wrapper, adds APNs proxy validation/docs/guardrails, and expands regression and live-test coverage. - Reproducibility: yes. source-reproducible: current main `sendApnsRequest()` still uses raw `http2.connect(au ... nly covers HTTP/global-agent/Undici hooks. I did not run a live APNs reproduction in this read-only review. Automerge notes: - PR branch already contained follow-up commit before automerge: test: guard raw HTTP2 APNs connections - PR branch already contained follow-up commit before automerge: test: guard raw HTTP2 with OpenGrep - PR branch already contained follow-up commit before automerge: lint: ban raw HTTP2 imports - PR branch already contained follow-up commit before automerge: fix: use managed proxy state for APNs - PR branch already contained follow-up commit before automerge: test: exercise APNs active proxy state - PR branch already contained follow-up commit before automerge: fix: reject conflicting managed proxy activation Validation: - ClawSweeper review passed for headdab7c86a75. - Required merge gates passed before the squash merge. Prepared head SHA:dab7c86a75Review: https://github.com/openclaw/openclaw/pull/74905#issuecomment-4350181159 Co-authored-by: jesse-merhi <79823012+jesse-merhi@users.noreply.github.com> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
getDebugProxyCaptureStore,
|
||||
} from "../proxy-capture/store.sqlite.js";
|
||||
import type { CaptureQueryPreset } from "../proxy-capture/types.js";
|
||||
import { colorize, isRich, theme } from "../terminal/theme.js";
|
||||
|
||||
export async function runDebugProxyStartCommand(opts: { host?: string; port?: number }) {
|
||||
const settings = resolveDebugProxySettings();
|
||||
@@ -148,11 +149,41 @@ function redactProxyValidationResult(result: ProxyValidationResult): ProxyValida
|
||||
};
|
||||
}
|
||||
|
||||
function formatProxyCheckLine(check: ProxyValidationResult["checks"][number]): string {
|
||||
const icon = check.ok ? "✓" : "✗";
|
||||
const paddedKind = check.kind.padEnd(7, " ");
|
||||
const status = check.status === undefined ? "" : ` HTTP ${check.status}`;
|
||||
return ` ${icon} ${paddedKind} ${check.url}${status}`;
|
||||
type ProxyValidationTextColors = {
|
||||
heading: (value: string) => string;
|
||||
success: (value: string) => string;
|
||||
error: (value: string) => string;
|
||||
muted: (value: string) => string;
|
||||
warn: (value: string) => string;
|
||||
};
|
||||
|
||||
function getProxyValidationTextColors(): ProxyValidationTextColors {
|
||||
const rich = isRich();
|
||||
const apply = (color: (value: string) => string) => (value: string) =>
|
||||
colorize(rich, color, value);
|
||||
return {
|
||||
heading: apply(theme.heading),
|
||||
success: apply(theme.success),
|
||||
error: apply(theme.error),
|
||||
muted: apply(theme.muted),
|
||||
warn: apply(theme.warn),
|
||||
};
|
||||
}
|
||||
|
||||
function formatProxyCheckLine(
|
||||
check: ProxyValidationResult["checks"][number],
|
||||
colors: ProxyValidationTextColors,
|
||||
): string {
|
||||
const icon = check.ok ? colors.success("✓") : colors.error("✗");
|
||||
const paddedKind = colors.muted(check.kind.padEnd(7, " "));
|
||||
const status =
|
||||
check.status === undefined
|
||||
? ""
|
||||
: ` ${check.ok ? colors.success(`HTTP ${check.status}`) : colors.error(`HTTP ${check.status}`)}`;
|
||||
const detail = check.error
|
||||
? ` — ${check.ok ? colors.muted(check.error) : colors.error(check.error)}`
|
||||
: "";
|
||||
return ` ${icon} ${paddedKind} ${check.url}${status}${detail}`;
|
||||
}
|
||||
|
||||
function formatProxyValidationNextSteps(result: ProxyValidationResult): string[] {
|
||||
@@ -185,37 +216,35 @@ function formatProxyValidationNextSteps(result: ProxyValidationResult): string[]
|
||||
}
|
||||
|
||||
function formatProxyValidationText(result: ProxyValidationResult): string {
|
||||
const colors = getProxyValidationTextColors();
|
||||
const redactedProxyUrl = redactProxyUrl(result.config.proxyUrl);
|
||||
const lines = [
|
||||
`Proxy validation ${result.ok ? "passed" : "failed"}`,
|
||||
result.ok ? colors.success("Proxy validation passed") : colors.error("Proxy validation failed"),
|
||||
"",
|
||||
"Proxy",
|
||||
` Source: ${result.config.source}`,
|
||||
` URL: ${redactedProxyUrl ?? "not configured"}`,
|
||||
colors.heading("Proxy"),
|
||||
` Source: ${colors.muted(result.config.source)}`,
|
||||
` URL: ${redactedProxyUrl ?? colors.muted("not configured")}`,
|
||||
];
|
||||
|
||||
if (result.config.errors.length > 0) {
|
||||
lines.push("", "Problems");
|
||||
lines.push("", colors.heading("Problems"));
|
||||
for (const error of result.config.errors) {
|
||||
lines.push(` - ${error}`);
|
||||
lines.push(` - ${colors.error(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.checks.length > 0) {
|
||||
lines.push("", "Checks");
|
||||
lines.push("", colors.heading("Checks"));
|
||||
for (const check of result.checks) {
|
||||
lines.push(formatProxyCheckLine(check));
|
||||
if (check.error) {
|
||||
lines.push(` ${check.error}`);
|
||||
}
|
||||
lines.push(formatProxyCheckLine(check, colors));
|
||||
}
|
||||
}
|
||||
|
||||
const nextSteps = formatProxyValidationNextSteps(result);
|
||||
if (nextSteps.length > 0) {
|
||||
lines.push("", "Next steps");
|
||||
lines.push("", colors.heading("Next steps"));
|
||||
for (const nextStep of nextSteps) {
|
||||
lines.push(` ${nextStep}`);
|
||||
lines.push(` ${colors.warn(nextStep)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,6 +256,8 @@ export async function runProxyValidateCommand(opts: {
|
||||
proxyUrl?: string;
|
||||
allowedUrls?: string[];
|
||||
deniedUrls?: string[];
|
||||
apnsReachability?: boolean;
|
||||
apnsAuthority?: string;
|
||||
timeoutMs?: number;
|
||||
}) {
|
||||
const config = getRuntimeConfig();
|
||||
@@ -236,6 +267,8 @@ export async function runProxyValidateCommand(opts: {
|
||||
proxyUrlOverride: opts.proxyUrl,
|
||||
allowedUrls: opts.allowedUrls,
|
||||
deniedUrls: opts.deniedUrls,
|
||||
apnsReachability: opts.apnsReachability,
|
||||
apnsAuthority: opts.apnsAuthority,
|
||||
timeoutMs: opts.timeoutMs,
|
||||
});
|
||||
const outputResult = redactProxyValidationResult(result);
|
||||
|
||||
Reference in New Issue
Block a user