From 6663caf758f1f160433333fcd6610a8be2bb46fb Mon Sep 17 00:00:00 2001 From: Ludvig Axelsson Date: Mon, 28 Sep 2026 14:28:30 +0200 Subject: [PATCH 1/6] Stream proxied bodies through the worker unchanged The proxy converted every request and response into http types, which copied each body chunk through Wasm memory. Large uploads (a 96 MB Playwright trace) used seconds of CPU and hit Cloudflare's resource limit. The runtime then terminated the request, cancelling the Wasm executor's queued microtask, and every later request on that isolate hung with error 1101. Proxy requests now forward the original JS body streams in both directions and only rewrite headers. Uploads also keep their Content-Length instead of arriving chunked. Only /linkup routes still go through the axum router. --- worker/src/handlers/proxy.rs | 136 ++++++++++++++++------------------- worker/src/lib.rs | 36 ++++++++-- worker/src/router.rs | 6 +- 3 files changed, 92 insertions(+), 86 deletions(-) diff --git a/worker/src/handlers/proxy.rs b/worker/src/handlers/proxy.rs index 46b65cf4..a506d18b 100644 --- a/worker/src/handlers/proxy.rs +++ b/worker/src/handlers/proxy.rs @@ -1,61 +1,63 @@ -use axum::{ - extract::{Request, State}, - response::IntoResponse, -}; -use http::{HeaderMap, StatusCode, Uri}; +use axum::response::IntoResponse; +use http::{HeaderMap, StatusCode}; use linkup::{Session, get_additional_headers, get_target_service}; -use worker::{Fetch, HttpResponse}; +use worker::{Fetch, RequestInit, wasm_bindgen::JsValue, worker_sys::web_sys}; use crate::{http_error::HttpError, worker_state::WorkerState, ws::handle_ws_resp}; -#[worker::send] -pub async fn handle_all(State(state): State, mut req: Request) -> impl IntoResponse { - let headers: linkup::HeaderMap = req.headers().into(); - let url = req.uri().to_string(); +pub async fn handle_all( + state: WorkerState, + req: worker::Request, +) -> Result { + let mut request_headers: HeaderMap = req.headers().into(); + let headers: linkup::HeaderMap = (&request_headers).into(); + let url = req.inner().url(); let (session_name, config) = match state.session_allocator.get_request_session(&url, &headers).await { Ok(session) => session, Err(_) => { - return HttpError::new( + return Err(HttpError::new( "Linkup was unable to determine the session origin of the request.\nMake sure your request includes a valid session ID in the referer or tracestate headers. - Worker".to_string(), StatusCode::UNPROCESSABLE_ENTITY, - ) - .into_response() + )) } }; let target_service = match get_target_service(&url, &headers, &config, &session_name) { Some(result) => result, None => { - return HttpError::new( + return Err(HttpError::new( "The request belonged to a session, but there was no target for the request.\nCheck your routing rules in the linkup config for a match. - Worker".to_string(), StatusCode::NOT_FOUND, - ) - .into_response() + )) } }; let extra_headers = get_additional_headers(&url, &headers, &session_name, &target_service); - let is_websocket = req - .headers() + let is_websocket = request_headers .get("upgrade") .map(|v| v == "websocket") .unwrap_or(false); // Rewrite request for the target service - *req.uri_mut() = Uri::try_from(target_service.url).unwrap(); let extra_http_headers: HeaderMap = extra_headers.into(); - req.headers_mut().extend(extra_http_headers); - req.headers_mut().remove(http::header::HOST); - linkup::normalize_cookie_header(req.headers_mut()); + request_headers.extend(extra_http_headers); + request_headers.remove(http::header::HOST); + linkup::normalize_cookie_header(&mut request_headers); - let upstream_request: worker::Request = match req.try_into() { + let mut upstream_init = RequestInit::new(); + upstream_init + .with_method(req.method()) + .with_headers((&request_headers).into()) + .with_body(req.inner().body().map(JsValue::from)); + + let upstream_request = match worker::Request::new_with_init(&target_service.url, &upstream_init) + { Ok(req) => req, Err(e) => { - return HttpError::new( + return Err(HttpError::new( format!("Failed to parse request: {}", e), StatusCode::BAD_REQUEST, - ) - .into_response(); + )); } }; @@ -63,57 +65,54 @@ pub async fn handle_all(State(state): State, mut req: Request) -> i let cache_key = get_cache_key(&upstream_request, &session_name).unwrap_or_default(); if cacheable_req && let Some(upstream_response) = get_cached_req(cache_key.clone()).await { - let resp: HttpResponse = match upstream_response.try_into() { - Ok(resp) => resp, - Err(e) => { - return HttpError::new( - format!("Failed to parse cached response: {}", e), - StatusCode::BAD_GATEWAY, - ) - .into_response(); - } - }; - - return resp.into_response(); + return Ok(upstream_response.into()); } let mut upstream_response = match Fetch::Request(upstream_request).send().await { Ok(resp) => resp, Err(e) => { - return HttpError::new( + return Err(HttpError::new( format!("Failed to fetch from target service: {}", e), StatusCode::BAD_GATEWAY, - ) - .into_response(); + )); } }; if is_websocket { - handle_ws_resp(upstream_response).await.into_response() - } else { - if cacheable_req { - let cache_clone = match upstream_response.cloned() { - Ok(resp) => resp, - Err(e) => { - return HttpError::new( - format!("Failed to clone response: {}", e), - StatusCode::BAD_GATEWAY, - ) - .into_response(); - } - }; - - if let Err(e) = set_cached_req(cache_key, cache_clone).await { - return HttpError::new( - format!("Failed to cache response: {}", e), + return crate::into_raw_response(handle_ws_resp(upstream_response).await.into_response()) + .map_err(|e| { + HttpError::new( + format!("Failed to create websocket response: {}", e), StatusCode::INTERNAL_SERVER_ERROR, ) - .into_response(); + }); + } + + if cacheable_req { + let cache_clone = match upstream_response.cloned() { + Ok(resp) => resp, + Err(e) => { + return Err(HttpError::new( + format!("Failed to clone response: {}", e), + StatusCode::BAD_GATEWAY, + )); } - } + }; - handle_http_resp(upstream_response).await.into_response() + if let Err(e) = set_cached_req(cache_key, cache_clone).await { + return Err(HttpError::new( + format!("Failed to cache response: {}", e), + StatusCode::INTERNAL_SERVER_ERROR, + )); + } } + + let mut response_headers: HeaderMap = upstream_response.headers().into(); + response_headers.extend(linkup::allow_all_cors()); + + Ok(upstream_response + .with_headers((&response_headers).into()) + .into()) } fn is_cacheable_request(req: &worker::Request, config: &Session) -> bool { @@ -156,18 +155,3 @@ async fn set_cached_req(cache_key: String, resp: worker::Response) -> worker::Re worker::Cache::default().put(cache_key, resp).await?; Ok(()) } - -async fn handle_http_resp(worker_resp: worker::Response) -> impl IntoResponse { - let mut resp: HttpResponse = match worker_resp.try_into() { - Ok(resp) => resp, - Err(e) => { - return HttpError::new( - format!("Failed to parse response: {}", e), - StatusCode::BAD_GATEWAY, - ) - .into_response(); - } - }; - resp.headers_mut().extend(linkup::allow_all_cors()); - resp.into_response() -} diff --git a/worker/src/lib.rs b/worker/src/lib.rs index 97812d86..a8143712 100644 --- a/worker/src/lib.rs +++ b/worker/src/lib.rs @@ -1,7 +1,10 @@ -use axum::{body::Body, response::Response}; +use axum::response::IntoResponse; use linkup::TunnelData; use tower_service::Service; -use worker::{Env, HttpRequest, console_error, console_log, console_warn, event}; +use worker::{ + Env, FromRequest, HttpRequest, console_error, console_log, console_warn, event, + worker_sys::web_sys, +}; use crate::{router::router, worker_state::WorkerState}; @@ -18,15 +21,38 @@ pub(crate) const MIN_SUPPORTED_CLIENT_VERSION: &str = "2.1.0"; #[event(fetch)] async fn fetch( - req: HttpRequest, + req: web_sys::Request, env: Env, _ctx: worker::Context, -) -> Result, worker::Error> { +) -> Result { console_error_panic_hook::set_once(); let state = WorkerState::load(env).await?; - Ok(router(state).call(req).await?) + let url = worker::Url::parse(&req.url())?; + if url.path().starts_with("/linkup") { + let req = HttpRequest::from_raw(req).map_err(to_worker_error)?; + let response = router(state).call(req).await?; + + return into_raw_response(response); + } + + match handlers::proxy::handle_all(state, req.into()).await { + Ok(response) => Ok(response), + Err(error) => into_raw_response(error.into_response()), + } +} + +pub(crate) fn into_raw_response( + response: impl worker::IntoResponse, +) -> Result { + worker::IntoResponse::into_raw(response).map_err(to_worker_error) +} + +fn to_worker_error(error: impl Into>) -> worker::Error { + let error: Box = error.into(); + + worker::Error::RustError(error.to_string()) } #[event(scheduled)] diff --git a/worker/src/router.rs b/worker/src/router.rs index cf093f6b..42a6d341 100644 --- a/worker/src/router.rs +++ b/worker/src/router.rs @@ -3,7 +3,7 @@ use axum::{ extract::{Request, State}, middleware::{Next, from_fn_with_state}, response::IntoResponse, - routing::{any, get, post}, + routing::{get, post}, }; use http::{HeaderMap, StatusCode}; use linkup::{Version, VersionChannel}; @@ -39,10 +39,6 @@ pub fn router(state: WorkerState) -> Router { post(handlers::v2::sessions::upsert_tunneled), ) .route_layer(from_fn_with_state(state.clone(), authenticate)) - // ---------------------------------------------------------------------------------------- - // --- Fallback - // ---------------------------------------------------------------------------------------- - .fallback(any(handlers::proxy::handle_all)) .with_state(state) } From 367e6e363042483647bde12fbdb1dfcc433120ba Mon Sep 17 00:00:00 2001 From: Ludvig Axelsson Date: Mon, 28 Sep 2026 14:28:30 +0200 Subject: [PATCH 2/6] Upgrade worker to 0.8.7 worker 0.8 reinitializes the Wasm instance after a Rust panic or a JavaScript error passes through Wasm, so one failed request no longer leaves the isolate broken for every request that follows. worker-build 0.8 emits build/index.js and build/index_bg.wasm, so the CLI now embeds and uploads those instead of shim.mjs and index.wasm. --- .github/workflows/ci.yml | 4 +- Cargo.lock | 281 +++++++----------- linkup-cli/build.rs | 14 +- .../commands/infra/cloudflare/resources.rs | 12 +- worker/Cargo.toml | 2 +- worker/wrangler.toml.sample | 2 +- 6 files changed, 130 insertions(+), 185 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e6acd55..d2f69fd1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,7 +58,7 @@ jobs: working-directory: worker run: | rustup target add wasm32-unknown-unknown - cargo install -q worker-build@0.1.4 + cargo install -q worker-build@0.8.7 worker-build --release - name: Start Wrangler Worker @@ -87,6 +87,6 @@ jobs: # TODO(augustoccesar)[2026-02-25]: From 0.7.0, the worker-build version is aligned # with the version of the worker crate. When bumping the worker, it needs to also # bump the worker-build. - - run: cargo install -q worker-build@0.1.4 + - run: cargo install -q worker-build@0.8.7 - run: worker-build --release working-directory: worker diff --git a/Cargo.lock b/Cargo.lock index 0bed81c2..c55813ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,7 +112,7 @@ checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -124,7 +124,7 @@ checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -145,7 +145,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -182,40 +182,13 @@ dependencies = [ "fs_extra", ] -[[package]] -name = "axum" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" -dependencies = [ - "async-trait", - "axum-core 0.4.5", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "itoa", - "matchit 0.7.3", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper", - "tower", - "tower-layer", - "tower-service", -] - [[package]] name = "axum" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ - "axum-core 0.5.6", + "axum-core", "base64", "bytes", "form_urlencoded", @@ -244,26 +217,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "axum-core" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http", - "http-body", - "http-body-util", - "mime", - "pin-project-lite", - "rustversion", - "sync_wrapper", - "tower-layer", - "tower-service", -] - [[package]] name = "axum-core" version = "0.5.6" @@ -399,9 +352,9 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" [[package]] name = "cfg_aliases" @@ -474,7 +427,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -521,7 +474,7 @@ version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -731,7 +684,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.117", ] [[package]] @@ -742,7 +695,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -794,7 +747,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.117", ] [[package]] @@ -838,7 +791,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1067,7 +1020,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1600,7 +1553,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1725,7 +1678,7 @@ checksum = "e000de030ff8022ea1da3f466fbb0f3a809f5e51ed31f6dd931c35181ad8e6d7" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1771,7 +1724,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn", + "syn 2.0.117", ] [[package]] @@ -1796,7 +1749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -1810,11 +1763,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" dependencies = [ - "once_cell", + "cfg-if", + "futures-util", "wasm-bindgen", ] @@ -1923,7 +1877,7 @@ name = "linkup-local-server" version = "4.1.1" dependencies = [ "async-trait", - "axum 0.8.9", + "axum", "axum-server", "futures", "hickory-server", @@ -1948,7 +1902,7 @@ name = "linkup-server-tests" version = "4.1.1" dependencies = [ "anyhow", - "axum 0.8.9", + "axum", "futures", "http", "linkup", @@ -1966,7 +1920,7 @@ dependencies = [ name = "linkup-worker" version = "4.1.1" dependencies = [ - "axum 0.8.9", + "axum", "base64", "cloudflare", "console_error_panic_hook", @@ -2141,7 +2095,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2248,7 +2202,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2383,7 +2337,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2466,7 +2420,7 @@ checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2561,7 +2515,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", ] [[package]] @@ -2750,7 +2704,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2921,7 +2875,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn", + "syn 2.0.117", "unicode-ident", ] @@ -2972,7 +2926,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3039,7 +2993,7 @@ dependencies = [ "security-framework 3.7.0", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3178,17 +3132,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-wasm-bindgen" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3b143e2833c57ab9ad3ea280d21fd34e285a42837aeb0ee301f4f41890fa00e" -dependencies = [ - "js-sys", - "serde", - "wasm-bindgen", -] - [[package]] name = "serde-wasm-bindgen" version = "0.6.5" @@ -3217,7 +3160,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3284,7 +3227,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3442,6 +3385,27 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "subtle" version = "2.6.1" @@ -3459,6 +3423,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "sync_wrapper" version = "1.0.2" @@ -3476,7 +3451,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3539,10 +3514,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.3.2", + "getrandom 0.4.2", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3577,7 +3552,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3588,7 +3563,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3682,7 +3657,7 @@ checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4039,48 +4014,33 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "3cbab34de2d982e9b48e18d216d04c4a6f641066ff19ffb699980f591ee3610e" dependencies = [ - "cfg-if", "js-sys", - "once_cell", + "tokio", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4088,22 +4048,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn", - "wasm-bindgen-backend", + "syn 3.0.6", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" dependencies = [ "unicode-ident", ] @@ -4132,9 +4092,9 @@ dependencies = [ [[package]] name = "wasm-streams" -version = "0.4.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +checksum = "94ce9bdac782bbca70d6ac48a3ba18b873c848a706d6dc3bd9a03c1b96f04693" dependencies = [ "futures-util", "js-sys", @@ -4157,9 +4117,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "88261b9deccee56594c11a3460c462c41f58d148598fe70ad77070126a68aba4" dependencies = [ "js-sys", "wasm-bindgen", @@ -4221,7 +4181,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -4315,7 +4275,7 @@ checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4326,7 +4286,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4337,7 +4297,7 @@ checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4348,7 +4308,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4694,7 +4654,7 @@ dependencies = [ "heck", "indexmap 2.7.1", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -4710,7 +4670,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -4754,12 +4714,12 @@ dependencies = [ [[package]] name = "worker" -version = "0.5.0" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "727789ca7eff9733efbea9d0e97779edc1cf1926e98aee7d7d8afe32805458aa" +checksum = "4057bce8ec66c80b7e270d884c312e2a4ff60eb997fd02936656106c06a964e5" dependencies = [ "async-trait", - "axum 0.7.9", + "axum", "bytes", "chrono", "futures-channel", @@ -4770,56 +4730,41 @@ dependencies = [ "matchit 0.7.3", "pin-project", "serde", - "serde-wasm-bindgen 0.6.5", + "serde-wasm-bindgen", "serde_json", "serde_urlencoded", + "strum", "tokio", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "worker-kv", "worker-macros", "worker-sys", ] -[[package]] -name = "worker-kv" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f06d4d1416a9f8346ee9123b0d9a11b3cfa38e6cfb5a139698017d1597c4d41" -dependencies = [ - "js-sys", - "serde", - "serde-wasm-bindgen 0.5.0", - "serde_json", - "thiserror 1.0.69", - "wasm-bindgen", - "wasm-bindgen-futures", -] - [[package]] name = "worker-macros" -version = "0.5.0" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d625c24570ba9207a2617476013335f28a95cbe513e59bb814ffba092a18058" +checksum = "f3d7389670b55aab89760be149007aeff773cdad7b650d807f7fb556d04bb304" dependencies = [ "async-trait", "proc-macro2", "quote", - "syn", + "strum", + "syn 3.0.6", "wasm-bindgen", - "wasm-bindgen-futures", "wasm-bindgen-macro-support", "worker-sys", ] [[package]] name = "worker-sys" -version = "0.5.0" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34563340d41016b4381257c5a16b0d2bc590dbe00500ecfbebcaa16f5f85ce90" +checksum = "6a440092e7ae7c5feb5507fbdc6aee0cc241d132757c83e1729766a24cddff82" dependencies = [ "cfg-if", "js-sys", @@ -4898,7 +4843,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -4929,7 +4874,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4940,7 +4885,7 @@ checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -4960,7 +4905,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -4989,7 +4934,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] diff --git a/linkup-cli/build.rs b/linkup-cli/build.rs index 7304edfd..72f29c5b 100644 --- a/linkup-cli/build.rs +++ b/linkup-cli/build.rs @@ -15,7 +15,7 @@ fn main() { // TODO(augustoccesar)[2026-02-25]: From 0.7.0, the worker-build version is aligned // with the version of the worker crate. When bumping the worker, it needs to also // bump the worker-build. - .args(["install", "-q", "worker-build@0.1.4"]) + .args(["install", "-q", "worker-build@0.8.7"]) .current_dir("../worker") .env("CARGO_TARGET_DIR", &worker_target_dir) .status() @@ -36,13 +36,13 @@ fn main() { panic!("Failed to build worker"); } - let shim_src = Path::new("../worker/build/worker/shim.mjs"); - let wasm_src = Path::new("../worker/build/worker/index.wasm"); - let shim_dest = Path::new(&out_dir).join("shim.mjs"); - let wasm_dest = Path::new(&out_dir).join("index.wasm"); + let index_js_src = Path::new("../worker/build/index.js"); + let wasm_src = Path::new("../worker/build/index_bg.wasm"); + let index_js_dest = Path::new(&out_dir).join("index.js"); + let wasm_dest = Path::new(&out_dir).join("index_bg.wasm"); fs::create_dir_all(&out_dir).expect("failed to create output directories"); - fs::copy(shim_src, &shim_dest).expect("failed to copy shim.mjs"); - fs::copy(wasm_src, &wasm_dest).expect("failed to copy index.wasm"); + fs::copy(index_js_src, &index_js_dest).expect("failed to copy index.js"); + fs::copy(wasm_src, &wasm_dest).expect("failed to copy index_bg.wasm"); } diff --git a/linkup-cli/src/commands/infra/cloudflare/resources.rs b/linkup-cli/src/commands/infra/cloudflare/resources.rs index a68e7ccf..1a6d4c96 100644 --- a/linkup-cli/src/commands/infra/cloudflare/resources.rs +++ b/linkup-cli/src/commands/infra/cloudflare/resources.rs @@ -9,8 +9,8 @@ use crate::commands::infra::notifier::DeployNotifier; use super::{DeployError, api::CloudflareApi}; -const LINKUP_WORKER_SHIM: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shim.mjs")); -const LINKUP_WORKER_INDEX_WASM: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/index.wasm")); +const LINKUP_WORKER_INDEX_JS: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/index.js")); +const LINKUP_WORKER_INDEX_WASM: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/index_bg.wasm")); #[derive(Debug, Clone)] pub struct TargetCfResources { @@ -1097,15 +1097,15 @@ pub fn cf_resources( tunnel_zone_id: tunnel_zone_id.clone(), account_token_name, worker_script_name: linkup_script_name.clone(), - worker_script_entry: "shim.mjs".to_string(), + worker_script_entry: "index.js".to_string(), worker_script_parts: vec![ WorkerScriptPart { - name: "shim.mjs".to_string(), - data: LINKUP_WORKER_SHIM.to_vec(), + name: "index.js".to_string(), + data: LINKUP_WORKER_INDEX_JS.to_vec(), content_type: "application/javascript+module".to_string(), }, WorkerScriptPart { - name: "index.wasm".to_string(), + name: "index_bg.wasm".to_string(), data: LINKUP_WORKER_INDEX_WASM.to_vec(), content_type: "application/wasm".to_string(), }, diff --git a/worker/Cargo.toml b/worker/Cargo.toml index 98e81c7e..07436e44 100644 --- a/worker/Cargo.toml +++ b/worker/Cargo.toml @@ -21,4 +21,4 @@ reqwest = { workspace = true, features = ["default-tls"] } console_error_panic_hook = { version = "0.1.7" } getrandom = { version = "0.4.2", features = ["wasm_js"] } tower-service = "0.3.3" -worker = { version = "0.5.0", features = ["http", "axum"] } +worker = { version = "0.8.7", features = ["http", "axum"] } diff --git a/worker/wrangler.toml.sample b/worker/wrangler.toml.sample index 3ccc0a29..fe060643 100644 --- a/worker/wrangler.toml.sample +++ b/worker/wrangler.toml.sample @@ -22,4 +22,4 @@ crons = [ "0 12 * * 2-6" ] # TODO(augustoccesar)[2026-02-25]: From 0.7.0, the worker-build version is aligned # with the version of the worker crate. When bumping the worker, it needs to also # bump the worker-build. -command = "cargo install -q worker-build@0.1.4 && worker-build --release" +command = "cargo install -q worker-build@0.8.7 && worker-build --release" From a0089a36cd29371dcff8f29030b65f4de207150a Mon Sep 17 00:00:00 2001 From: Ludvig Axelsson Date: Mon, 28 Sep 2026 14:43:20 +0200 Subject: [PATCH 3/6] Remove resolved worker-build version TODOs --- .github/workflows/ci.yml | 6 ------ linkup-cli/build.rs | 3 --- worker/wrangler.toml.sample | 3 --- 3 files changed, 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d2f69fd1..033cd90b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,9 +51,6 @@ jobs: with: node-version: 22 - # TODO(augustoccesar)[2026-05-04]: From 0.7.0, the worker-build version is aligned - # with the version of the worker crate. When bumping the worker, it needs to also - # bump the worker-build. - name: Pre-build Worker working-directory: worker run: | @@ -84,9 +81,6 @@ jobs: - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: cache-targets: "false" - # TODO(augustoccesar)[2026-02-25]: From 0.7.0, the worker-build version is aligned - # with the version of the worker crate. When bumping the worker, it needs to also - # bump the worker-build. - run: cargo install -q worker-build@0.8.7 - run: worker-build --release working-directory: worker diff --git a/linkup-cli/build.rs b/linkup-cli/build.rs index 72f29c5b..b426ca4a 100644 --- a/linkup-cli/build.rs +++ b/linkup-cli/build.rs @@ -12,9 +12,6 @@ fn main() { let worker_target_dir = Path::new(&out_dir).join("worker-target"); let install_status = Command::new("cargo") - // TODO(augustoccesar)[2026-02-25]: From 0.7.0, the worker-build version is aligned - // with the version of the worker crate. When bumping the worker, it needs to also - // bump the worker-build. .args(["install", "-q", "worker-build@0.8.7"]) .current_dir("../worker") .env("CARGO_TARGET_DIR", &worker_target_dir) diff --git a/worker/wrangler.toml.sample b/worker/wrangler.toml.sample index fe060643..5e42e936 100644 --- a/worker/wrangler.toml.sample +++ b/worker/wrangler.toml.sample @@ -19,7 +19,4 @@ WORKER_TOKEN = "token123" crons = [ "0 12 * * 2-6" ] [build] -# TODO(augustoccesar)[2026-02-25]: From 0.7.0, the worker-build version is aligned -# with the version of the worker crate. When bumping the worker, it needs to also -# bump the worker-build. command = "cargo install -q worker-build@0.8.7 && worker-build --release" From f3307970343cd3ea3ef0de71d70a4a1d0c36372d Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Mon, 28 Sep 2026 15:02:17 +0200 Subject: [PATCH 4/6] Only route /linkup/ paths to the worker router --- worker/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worker/src/lib.rs b/worker/src/lib.rs index a8143712..38e04549 100644 --- a/worker/src/lib.rs +++ b/worker/src/lib.rs @@ -30,7 +30,7 @@ async fn fetch( let state = WorkerState::load(env).await?; let url = worker::Url::parse(&req.url())?; - if url.path().starts_with("/linkup") { + if url.path().starts_with("/linkup/") { let req = HttpRequest::from_raw(req).map_err(to_worker_error)?; let response = router(state).call(req).await?; From c6bb7c16eb52605ad5d1a6326e43fe428c7919e5 Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Mon, 28 Sep 2026 15:02:17 +0200 Subject: [PATCH 5/6] Add CORS headers to cached proxy responses --- worker/src/handlers/proxy.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/worker/src/handlers/proxy.rs b/worker/src/handlers/proxy.rs index a506d18b..94f25b00 100644 --- a/worker/src/handlers/proxy.rs +++ b/worker/src/handlers/proxy.rs @@ -64,8 +64,8 @@ pub async fn handle_all( let cacheable_req = is_cacheable_request(&upstream_request, &config); let cache_key = get_cache_key(&upstream_request, &session_name).unwrap_or_default(); - if cacheable_req && let Some(upstream_response) = get_cached_req(cache_key.clone()).await { - return Ok(upstream_response.into()); + if cacheable_req && let Some(cached_response) = get_cached_req(cache_key.clone()).await { + return Ok(with_cors(cached_response).into()); } let mut upstream_response = match Fetch::Request(upstream_request).send().await { @@ -107,12 +107,14 @@ pub async fn handle_all( } } - let mut response_headers: HeaderMap = upstream_response.headers().into(); + Ok(with_cors(upstream_response).into()) +} + +fn with_cors(response: worker::Response) -> worker::Response { + let mut response_headers: HeaderMap = response.headers().into(); response_headers.extend(linkup::allow_all_cors()); - Ok(upstream_response - .with_headers((&response_headers).into()) - .into()) + response.with_headers((&response_headers).into()) } fn is_cacheable_request(req: &worker::Request, config: &Session) -> bool { From 16e6b83043322be493b6b7fcb635a89bd6f463f9 Mon Sep 17 00:00:00 2001 From: Ludvig Axelsson Date: Mon, 28 Sep 2026 15:08:50 +0200 Subject: [PATCH 6/6] Revert "Remove resolved worker-build version TODOs" This reverts commit a0089a36cd29371dcff8f29030b65f4de207150a. --- .github/workflows/ci.yml | 6 ++++++ linkup-cli/build.rs | 3 +++ worker/wrangler.toml.sample | 3 +++ 3 files changed, 12 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 033cd90b..d2f69fd1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,9 @@ jobs: with: node-version: 22 + # TODO(augustoccesar)[2026-05-04]: From 0.7.0, the worker-build version is aligned + # with the version of the worker crate. When bumping the worker, it needs to also + # bump the worker-build. - name: Pre-build Worker working-directory: worker run: | @@ -81,6 +84,9 @@ jobs: - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 with: cache-targets: "false" + # TODO(augustoccesar)[2026-02-25]: From 0.7.0, the worker-build version is aligned + # with the version of the worker crate. When bumping the worker, it needs to also + # bump the worker-build. - run: cargo install -q worker-build@0.8.7 - run: worker-build --release working-directory: worker diff --git a/linkup-cli/build.rs b/linkup-cli/build.rs index b426ca4a..72f29c5b 100644 --- a/linkup-cli/build.rs +++ b/linkup-cli/build.rs @@ -12,6 +12,9 @@ fn main() { let worker_target_dir = Path::new(&out_dir).join("worker-target"); let install_status = Command::new("cargo") + // TODO(augustoccesar)[2026-02-25]: From 0.7.0, the worker-build version is aligned + // with the version of the worker crate. When bumping the worker, it needs to also + // bump the worker-build. .args(["install", "-q", "worker-build@0.8.7"]) .current_dir("../worker") .env("CARGO_TARGET_DIR", &worker_target_dir) diff --git a/worker/wrangler.toml.sample b/worker/wrangler.toml.sample index 5e42e936..fe060643 100644 --- a/worker/wrangler.toml.sample +++ b/worker/wrangler.toml.sample @@ -19,4 +19,7 @@ WORKER_TOKEN = "token123" crons = [ "0 12 * * 2-6" ] [build] +# TODO(augustoccesar)[2026-02-25]: From 0.7.0, the worker-build version is aligned +# with the version of the worker crate. When bumping the worker, it needs to also +# bump the worker-build. command = "cargo install -q worker-build@0.8.7 && worker-build --release"