(
client: &reqwest::Client,
sandbox_id: &str,
envd_token: &str,
traffic_token: Option<&str>,
code: &str,
)
| 655 | } |
| 656 | |
| 657 | async fn exec_in_sandbox( |
| 658 | client: &reqwest::Client, |
| 659 | sandbox_id: &str, |
| 660 | envd_token: &str, |
| 661 | traffic_token: Option<&str>, |
| 662 | code: &str, |
| 663 | ) -> Result<ExecOutcome, String> { |
| 664 | let url = format!("https://{}-{}.e2b.app/execute", E2B_EXEC_PORT, sandbox_id); |
| 665 | |
| 666 | let mut req = client |
| 667 | .post(&url) |
| 668 | .header("X-Access-Token", envd_token) |
| 669 | .header("Content-Type", "application/json") |
| 670 | .timeout(std::time::Duration::from_secs(REQUEST_TIMEOUT_SECS)); |
| 671 | if let Some(tok) = traffic_token { |
| 672 | req = req.header("E2B-Traffic-Access-Token", tok); |
| 673 | } |
| 674 | let resp = req |
| 675 | .json(&serde_json::json!({ |
| 676 | "code": code, |
| 677 | "language": "python", |
| 678 | })) |
| 679 | .send() |
| 680 | .await |
| 681 | .map_err(|e| format!("E2B /execute request failed: {}", e))?; |
| 682 | |
| 683 | if !resp.status().is_success() { |
| 684 | let status = resp.status(); |
| 685 | let body = resp.text().await.unwrap_or_default(); |
| 686 | return Err(format!("E2B /execute returned {}: {}", status, body)); |
| 687 | } |
| 688 | |
| 689 | let mut stream = resp.bytes_stream(); |
| 690 | // Raw byte buffer so UTF-8 sequences that straddle TCP chunk boundaries |
| 691 | // aren't corrupted by lossy per-chunk decoding. We split on the literal |
| 692 | // `\n` byte (JSONL framing is ASCII), then decode each complete line as |
| 693 | // UTF-8 once we have it whole. |
| 694 | let mut buffer: Vec<u8> = Vec::new(); |
| 695 | let mut stdout = String::new(); |
| 696 | let mut stderr = String::new(); |
| 697 | let mut ports: Option<serde_json::Map<String, Value>> = None; |
| 698 | let mut user_error: Option<String> = None; |
| 699 | |
| 700 | 'outer: while let Some(chunk) = stream.next().await { |
| 701 | let chunk = chunk.map_err(|e| format!("E2B stream read error: {}", e))?; |
| 702 | buffer.extend_from_slice(&chunk); |
| 703 | |
| 704 | while let Some(newline_pos) = buffer.iter().position(|&b| b == b'\n') { |
| 705 | let line_bytes = buffer.drain(..=newline_pos).collect::<Vec<u8>>(); |
| 706 | let line_str = match std::str::from_utf8(&line_bytes[..line_bytes.len().saturating_sub(1)]) { |
| 707 | Ok(s) => s.trim(), |
| 708 | Err(e) => return Err(format!("E2B stream produced invalid UTF-8: {}", e)), |
| 709 | }; |
| 710 | if line_str.is_empty() { continue; } |
| 711 | |
| 712 | let event: E2bEvent = serde_json::from_str(line_str) |
| 713 | .map_err(|e| format!("E2B event JSON was malformed: {} (raw: {})", e, line_str))?; |
| 714 |
no test coverage detected