diff --git a/crates/tower-cmd/src/util/deploy.rs b/crates/tower-cmd/src/util/deploy.rs index 3ce5c5de..660c2abe 100644 --- a/crates/tower-cmd/src/util/deploy.rs +++ b/crates/tower-cmd/src/util/deploy.rs @@ -14,6 +14,12 @@ use tower_api::apis::Error; use tower_api::apis::ResponseContent; use tower_api::models::DeployAppResponse; +/// Advisory only: the server enforces the actual bundle-size limit and reports +/// it in its error response when a bundle is too large. +const LARGE_PACKAGE_WARNING_THRESHOLD: u64 = 500 * 1024 * 1024; + +/// Streams a package to the server, reporting progress and leaving size enforcement +/// to the server. Large-package warnings are suppressed in JSON output mode. pub async fn upload_file_with_progress( out: &output::Out, api_config: &Configuration, @@ -36,13 +42,10 @@ pub async fn upload_file_with_progress( let metadata = file.metadata().await?; let file_size = metadata.len(); - // Check if bundle size exceeds the maximum allowed size - if file_size > tower_package::MAX_PACKAGE_SIZE { - let size_mb = file_size as f64 / (1024.0 * 1024.0); - let max_mb = tower_package::MAX_PACKAGE_SIZE as f64 / (1024.0 * 1024.0); - out.die(&format!( - "Your App is too big! ({:.2} MB) exceeds maximum allowed size ({:.0} MB). Please consider reducing app size by removing unnecessary files or import_paths in the Towerfile.", - size_mb, max_mb + if file_size > LARGE_PACKAGE_WARNING_THRESHOLD { + out.note(&format!( + "Warning: Your app package is large ({}). The server may reject it depending on its configured maximum bundle size. You can reduce app size by removing unnecessary files or import_paths in the Towerfile.\n", + indicatif::BinaryBytes(file_size) )); } @@ -57,6 +60,7 @@ pub async fn upload_file_with_progress( .header("X-Tower-Checksum-SHA256", package_hash) .header("Content-Type", content_type) .header("Content-Encoding", "gzip") + .header(reqwest::header::CONTENT_LENGTH, file_size) .body(Body::wrap_stream(progress_stream)); // When supplied, ask the server to reuse an existing AppVersion that was @@ -174,3 +178,45 @@ pub async fn deploy_app_package( Ok(response) } + +#[cfg(test)] +mod tests { + use super::*; + use axum::{body::Bytes, http::HeaderMap, routing::post, Json, Router}; + use std::io::Write; + + #[tokio::test] + async fn upload_advertises_exact_streamed_length() { + let _ = rustls::crypto::ring::default_provider().install_default(); + let payload = b"test package contents"; + let mut package = tempfile::NamedTempFile::new().unwrap(); + package.write_all(payload).unwrap(); + + let app = Router::new().route( + "/deploy", + post(|headers: HeaderMap, body: Bytes| async move { + assert_eq!(headers["content-length"], "21"); + assert_eq!(headers["content-encoding"], "gzip"); + assert!(!headers.contains_key("transfer-encoding")); + assert_eq!(body.as_ref(), b"test package contents"); + Json(DeployAppResponse::default()) + }), + ); + let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); + let address = listener.local_addr().unwrap(); + let server = tokio::spawn(async move { axum::serve(listener, app).await.unwrap() }); + + let result = upload_file_with_progress( + &output::Out::sink(), + &Configuration::default(), + format!("http://{address}/deploy"), + package.path().to_path_buf(), + "application/tar", + None, + Box::new(|_, _| {}), + ) + .await; + server.abort(); + result.unwrap(); + } +} diff --git a/crates/tower-package/src/core.rs b/crates/tower-package/src/core.rs index b376f83a..69383c4f 100644 --- a/crates/tower-package/src/core.rs +++ b/crates/tower-package/src/core.rs @@ -15,8 +15,6 @@ use crate::towerfile::{Parameter, Towerfile}; // 3 - Change checksum algorithm to be cross-platform pub const CURRENT_PACKAGE_VERSION: i32 = 3; -pub const MAX_PACKAGE_SIZE: u64 = 50 * 1024 * 1024; - #[derive(Debug, Snafu)] pub enum Error { #[snafu(display("Invalid path"))] diff --git a/crates/tower-package/src/lib.rs b/crates/tower-package/src/lib.rs index d876067b..3f8520ed 100644 --- a/crates/tower-package/src/lib.rs +++ b/crates/tower-package/src/lib.rs @@ -3,7 +3,7 @@ mod towerfile; pub use core::{ build_package, compute_sha256_bytes, compute_sha256_package, normalize_path, BuiltPackage, - Entry, Manifest, PackageInputs, CURRENT_PACKAGE_VERSION, MAX_PACKAGE_SIZE, + Entry, Manifest, PackageInputs, CURRENT_PACKAGE_VERSION, }; pub use towerfile::{App, Parameter, Towerfile};