()
| 90 | } |
| 91 | |
| 92 | fn worker_main() -> io::Result<()> { |
| 93 | let token_hex = std::env::var(TOKEN_ENV_VAR).map_err(|_| { |
| 94 | io::Error::other("worker token not set; cannot run extract-worker directly") |
| 95 | })?; |
| 96 | // Scrub immediately so a child of a child cannot inherit it. |
| 97 | std::env::remove_var(TOKEN_ENV_VAR); |
| 98 | let expected = |
| 99 | hex::decode(token_hex.trim()).map_err(|_| io::Error::other("worker token malformed"))?; |
| 100 | if expected.len() != TOKEN_LEN { |
| 101 | return Err(io::Error::other("worker token wrong length")); |
| 102 | } |
| 103 | |
| 104 | let stdin = io::stdin(); |
| 105 | let stdout = io::stdout(); |
| 106 | let mut reader = BufReader::new(stdin.lock()); |
| 107 | let mut writer = BufWriter::new(stdout.lock()); |
| 108 | |
| 109 | let mut received = [0u8; TOKEN_LEN]; |
| 110 | reader.read_exact(&mut received)?; |
| 111 | // Constant-time-ish comparison; the token isn't a long-term secret but |
| 112 | // there's no reason to leak timing. |
| 113 | if !slices_eq(&received, &expected) { |
| 114 | return Err(io::Error::other("worker token mismatch")); |
| 115 | } |
| 116 | |
| 117 | let registry = LanguageRegistry::new(); |
| 118 | loop { |
| 119 | let req: ExtractRequest = match read_message(&mut reader) { |
| 120 | Ok(req) => req, |
| 121 | Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(()), |
| 122 | Err(e) => return Err(e), |
| 123 | }; |
| 124 | let resp = process_request(®istry, &req); |
| 125 | write_message(&mut writer, &resp)?; |
| 126 | writer.flush()?; |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | fn process_request(registry: &LanguageRegistry, req: &ExtractRequest) -> ExtractResponse { |
| 131 | let abs_path = req.project_root.join(&req.file_path); |
no test coverage detected