From 3b4f9d49b5bbd319438f25c4fb4dc5e0f4baa73f Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 6 Mar 2026 00:52:38 +0800 Subject: [PATCH 1/2] fix(executor): add Content-Length: 0 header for body-less POST/PUT/PATCH requests Google API servers return HTTP 411 (Length Required) when a POST request is sent without a Content-Length header, even if there is no body. This affects all Discovery API methods where httpMethod is POST but no requestBody is defined (e.g. gmail users.messages.trash). Fixes #182 --- src/executor.rs | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/src/executor.rs b/src/executor.rs index 8bc4f02..a21062b 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -191,6 +191,8 @@ async fn build_http_request( } else if let Some(ref body_val) = input.body { request = request.header("Content-Type", "application/json"); request = request.json(body_val); + } else if matches!(method.http_method.as_str(), "POST" | "PUT" | "PATCH") { + request = request.header("Content-Length", "0"); } } @@ -1817,3 +1819,84 @@ fn test_get_value_type_helper() { assert_eq!(get_value_type(&json!([1, 2])), "array"); assert_eq!(get_value_type(&json!({"a": 1})), "object"); } + +#[tokio::test] +async fn test_post_without_body_sets_content_length_zero() { + let client = reqwest::Client::new(); + let method = RestMethod { + http_method: "POST".to_string(), + path: "messages/trash".to_string(), + ..Default::default() + }; + let input = ExecutionInput { + full_url: "https://example.com/messages/trash".to_string(), + body: None, + params: Map::new(), + query_params: HashMap::new(), + is_upload: false, + }; + + let request = build_http_request(&client, &method, &input, None, &AuthMethod::None, None, 0, None) + .await + .unwrap(); + + let built = request.build().unwrap(); + assert_eq!( + built.headers().get("Content-Length").map(|v| v.to_str().unwrap()), + Some("0"), + "POST with no body must include Content-Length: 0" + ); +} + +#[tokio::test] +async fn test_post_with_body_does_not_add_content_length_zero() { + let client = reqwest::Client::new(); + let method = RestMethod { + http_method: "POST".to_string(), + path: "files".to_string(), + ..Default::default() + }; + let input = ExecutionInput { + full_url: "https://example.com/files".to_string(), + body: Some(json!({"name": "test"})), + params: Map::new(), + query_params: HashMap::new(), + is_upload: false, + }; + + let request = build_http_request(&client, &method, &input, None, &AuthMethod::None, None, 0, None) + .await + .unwrap(); + + let built = request.build().unwrap(); + // When body is present, Content-Length should NOT be "0" + let cl = built.headers().get("Content-Length").map(|v| v.to_str().unwrap().to_string()); + assert!(cl.is_none() || cl.as_deref() != Some("0")); +} + +#[tokio::test] +async fn test_get_does_not_set_content_length_zero() { + let client = reqwest::Client::new(); + let method = RestMethod { + http_method: "GET".to_string(), + path: "files".to_string(), + ..Default::default() + }; + let input = ExecutionInput { + full_url: "https://example.com/files".to_string(), + body: None, + params: Map::new(), + query_params: HashMap::new(), + is_upload: false, + }; + + let request = build_http_request(&client, &method, &input, None, &AuthMethod::None, None, 0, None) + .await + .unwrap(); + + let built = request.build().unwrap(); + assert!( + built.headers().get("Content-Length").is_none(), + "GET with no body should not have Content-Length header" + ); +} From a838afc413ba52fe22d12cf025c258383cd7fcd8 Mon Sep 17 00:00:00 2001 From: Frank Date: Fri, 6 Mar 2026 00:53:44 +0800 Subject: [PATCH 2/2] chore: add changeset for content-length fix --- .changeset/fix-content-length-bodyless-post.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fix-content-length-bodyless-post.md diff --git a/.changeset/fix-content-length-bodyless-post.md b/.changeset/fix-content-length-bodyless-post.md new file mode 100644 index 0000000..53fe828 --- /dev/null +++ b/.changeset/fix-content-length-bodyless-post.md @@ -0,0 +1,5 @@ +--- +"@googleworkspace/cli": patch +--- + +Add Content-Length: 0 header for POST/PUT/PATCH requests with no body to fix HTTP 411 errors