fix(executor): add Content-Length: 0 for body-less POST requests (#183)
* 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 * chore: add changeset for content-length fix
This commit is contained in:
@@ -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
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user