diff --git a/crates/worker/src/feature/builtin/ticket.rs b/crates/worker/src/feature/builtin/ticket.rs index 28ec53e7..6fd4292e 100644 --- a/crates/worker/src/feature/builtin/ticket.rs +++ b/crates/worker/src/feature/builtin/ticket.rs @@ -313,7 +313,6 @@ impl FeatureModule for TicketFeature { struct WorkspaceHttpTicketBackend { workspace_id: String, base_url: String, - client: reqwest::blocking::Client, } impl WorkspaceHttpTicketBackend { @@ -321,7 +320,6 @@ impl WorkspaceHttpTicketBackend { Self { workspace_id, base_url: base_url.trim_end_matches('/').to_string(), - client: reqwest::blocking::Client::new(), } } @@ -335,13 +333,27 @@ impl WorkspaceHttpTicketBackend { fn invoke( &self, operation: TicketBackendOperation, + ) -> TicketResult { + let endpoint = self.endpoint(); + if tokio::runtime::Handle::try_current().is_ok() { + return std::thread::spawn(move || Self::invoke_http(endpoint, operation)) + .join() + .map_err(|_| { + TicketError::Conflict("ticket backend request thread panicked".to_string()) + })?; + } + Self::invoke_http(endpoint, operation) + } + + fn invoke_http( + endpoint: String, + operation: TicketBackendOperation, ) -> TicketResult { let body = serde_json::to_string(&operation).map_err(|error| { TicketError::Conflict(format!("serialize ticket operation: {error}")) })?; - let response = self - .client - .post(self.endpoint()) + let response = reqwest::blocking::Client::new() + .post(endpoint) .header(reqwest::header::CONTENT_TYPE, "application/json") .body(body) .send() @@ -981,6 +993,20 @@ provider = "github" assert!(!root.join("closed").exists()); } + #[tokio::test(flavor = "multi_thread")] + async fn workspace_http_backend_invoke_is_safe_inside_async_context() { + let backend = + WorkspaceHttpTicketBackend::new("workspace-a".to_string(), "not-a-url".to_string()); + + let error = backend + .invoke(TicketBackendOperation::DefaultIntakeReadyStateChangeBody { + from: "planning".to_string(), + }) + .unwrap_err(); + + assert!(error.to_string().contains("ticket backend request failed")); + } + #[test] fn workspace_http_backend_executes_ticket_create_operation() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); diff --git a/scripts/dev-workspace.sh b/scripts/dev-workspace.sh index 5017796b..c7c6533d 100755 --- a/scripts/dev-workspace.sh +++ b/scripts/dev-workspace.sh @@ -318,7 +318,7 @@ start_runtime() { return 1 fi start_service runtime "$ROOT_DIR" "$port" \ - "$runtime_bin" --bind "$RUNTIME_BIND" + env RUST_BACKTRACE="${RUST_BACKTRACE:-1}" "$runtime_bin" --bind "$RUNTIME_BIND" } start_backend() {