| 206 | }); |
| 207 | |
| 208 | export const startTestServer = async (port) => { |
| 209 | const handler = async (req) => { |
| 210 | const parsed = new URL(req.url, `http://localhost:${port}`); |
| 211 | |
| 212 | const params = Object.fromEntries(parsed.searchParams); |
| 213 | |
| 214 | const response = { |
| 215 | url: req.url, |
| 216 | pathname: parsed.pathname, |
| 217 | params, |
| 218 | method: req.method, |
| 219 | headers: req.headers, |
| 220 | }; |
| 221 | |
| 222 | const contentType = req.headers['content-type'] || ''; |
| 223 | |
| 224 | const { delay = 0 } = params; |
| 225 | |
| 226 | if (+delay) { |
| 227 | await setTimeoutAsync(+delay); |
| 228 | } |
| 229 | |
| 230 | switch (parsed.pathname.replace(/\/$/, '')) { |
| 231 | case '/echo/json': |
| 232 | default: |
| 233 | if (contentType.startsWith('multipart/')) { |
| 234 | const { fields, files } = await handleFormData(req); |
| 235 | response.form = fields; |
| 236 | response.files = files; |
| 237 | } else { |
| 238 | response.body = (await getStreamAsBuffer(req)).toString('hex'); |
| 239 | } |
| 240 | |
| 241 | return { |
| 242 | body: response, |
| 243 | }; |
| 244 | } |
| 245 | }; |
| 246 | |
| 247 | return await startHTTPServer( |
| 248 | (req, res) => { |
| 249 | res.setHeader('Access-Control-Allow-Origin', `*`); |
| 250 | res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); |
| 251 | res.setHeader('Access-Control-Allow-Headers', '*'); |
| 252 | res.setHeader('Access-Control-Max-Age', '86400'); |
| 253 | |
| 254 | if (req.method === 'OPTIONS') { |
| 255 | res.writeHead(204); |
| 256 | res.end(); |
| 257 | return; |
| 258 | } |
| 259 | |
| 260 | Promise.resolve(handler(req, res)).then((result) => { |
| 261 | const { status = 200, headers = {}, body } = result || {}; |
| 262 | |
| 263 | res.statusCode = status; |
| 264 | |
| 265 | Object.entries(headers).forEach(([header, value]) => { |