()
| 132 | * 所有响应都带 Access-Control-Allow-Origin: *。CSP 目标主机额外下发严格 CSP 头。 |
| 133 | */ |
| 134 | export async function startMockServer(): Promise<MockServer> { |
| 135 | const requestLog: RequestRecord[] = []; |
| 136 | const pathHitCount = new Map<string, number>(); |
| 137 | const failingPaths = new Set<string>(); |
| 138 | |
| 139 | const handler = (req: IncomingMessage, res: ServerResponse) => { |
| 140 | const hostHeader = req.headers.host || "127.0.0.1"; |
| 141 | const hostNoPort = hostHeader.split(":")[0]; |
| 142 | const url = new URL(req.url || "/", `http://${hostHeader}`); |
| 143 | const pathname = url.pathname; |
| 144 | |
| 145 | requestLog.push({ |
| 146 | method: req.method || "GET", |
| 147 | host: hostNoPort, |
| 148 | path: req.url || "/", |
| 149 | pathname, |
| 150 | headers: req.headers as Record<string, string | string[] | undefined>, |
| 151 | }); |
| 152 | pathHitCount.set(pathname, (pathHitCount.get(pathname) || 0) + 1); |
| 153 | |
| 154 | res.setHeader("Access-Control-Allow-Origin", "*"); |
| 155 | res.setHeader("Access-Control-Allow-Headers", "*"); |
| 156 | res.setHeader("Access-Control-Expose-Headers", "*"); |
| 157 | |
| 158 | if (req.method === "OPTIONS") { |
| 159 | res.writeHead(204); |
| 160 | res.end(); |
| 161 | return; |
| 162 | } |
| 163 | |
| 164 | if (failingPaths.has(pathname)) { |
| 165 | res.writeHead(500, { "Content-Type": "text/plain" }); |
| 166 | res.end("mock failure"); |
| 167 | return; |
| 168 | } |
| 169 | |
| 170 | if (pathname === "/lib.js") { |
| 171 | const version = pathHitCount.get(pathname) || 1; |
| 172 | const token = url.searchParams.get("token") || ""; |
| 173 | res.writeHead(200, { "Content-Type": "application/javascript; charset=utf-8" }); |
| 174 | res.end(`window.__LIB_VERSION__ = ${version};\nwindow.__LIB_TOKEN__ = ${JSON.stringify(token)};\n`); |
| 175 | return; |
| 176 | } |
| 177 | |
| 178 | if (pathname === "/lib2.js") { |
| 179 | res.writeHead(200, { "Content-Type": "application/javascript; charset=utf-8" }); |
| 180 | res.end(`window.__LIB2_OK__ = true;\n`); |
| 181 | return; |
| 182 | } |
| 183 | |
| 184 | if (pathname === "/res.txt") { |
| 185 | res.writeHead(200, { "Content-Type": "text/plain; charset=utf-8" }); |
| 186 | res.end("RESOURCE_OK"); |
| 187 | return; |
| 188 | } |
| 189 | |
| 190 | if (pathname === "/xhr") { |
| 191 | res.writeHead(200, { "Content-Type": "application/json" }); |
no test coverage detected