From 27cc6bd05889561ea8c43d03ef25b107bfb3cbfa Mon Sep 17 00:00:00 2001 From: Daniel Alley Date: Fri, 3 Apr 2026 10:07:13 -0400 Subject: [PATCH 1/3] Send a Content-Type header with cargo publish requests closes #16830 --- crates/crates-io/lib.rs | 1 + src/doc/src/reference/registry-web-api.md | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/crates-io/lib.rs b/crates/crates-io/lib.rs index b1dbd59c485..7c7589aa2ba 100644 --- a/crates/crates-io/lib.rs +++ b/crates/crates-io/lib.rs @@ -293,6 +293,7 @@ impl Registry { self.handle.url(&url)?; self.handle.in_filesize(size as u64)?; let mut headers = List::new(); + headers.append("Content-Type: application/octet-stream")?; headers.append("Accept: application/json")?; headers.append(&format!("Authorization: {}", self.token()?))?; self.handle.http_headers(headers)?; diff --git a/src/doc/src/reference/registry-web-api.md b/src/doc/src/reference/registry-web-api.md index e8481b3a4b1..439dec1e17b 100644 --- a/src/doc/src/reference/registry-web-api.md +++ b/src/doc/src/reference/registry-web-api.md @@ -38,9 +38,11 @@ be null. The endpoints are versioned with the `v1` component of the path, and Cargo is responsible for handling backwards compatibility fallbacks should any be required in the future. -Cargo sets the following headers for all requests: +Cargo sets the following headers for requests: -- `Content-Type`: `application/json` (for requests with a body payload) +- `Content-Type`: `application/json` for JSON body payloads, or + `application/octet-stream` for the [publish](#publish) endpoint (which uses a + custom binary format). Not set for requests without a body. - `Accept`: `application/json` - `User-Agent`: The Cargo version such as `cargo/1.32.0 (8610973aa 2019-01-02)`. This may be modified by the user in a configuration value. From cefce4681c3d641c2d59d3bfa5eefdb80379ece7 Mon Sep 17 00:00:00 2001 From: Daniel Alley Date: Sat, 4 Apr 2026 11:14:40 -0400 Subject: [PATCH 2/3] Update the Web API docs re: headers Revise how the header documentation is written such that per-endpoint headers are more straightforwards. --- src/doc/src/reference/registry-web-api.md | 38 ++++++++++++++++++----- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/src/doc/src/reference/registry-web-api.md b/src/doc/src/reference/registry-web-api.md index 439dec1e17b..b627550dee6 100644 --- a/src/doc/src/reference/registry-web-api.md +++ b/src/doc/src/reference/registry-web-api.md @@ -38,21 +38,21 @@ be null. The endpoints are versioned with the `v1` component of the path, and Cargo is responsible for handling backwards compatibility fallbacks should any be required in the future. -Cargo sets the following headers for requests: +Cargo sets the `User-Agent` header for all requests to the Cargo version such +as `cargo/1.32.0 (8610973aa 2019-01-02)`. This may be modified by the user in +a configuration value. Added in 1.29. -- `Content-Type`: `application/json` for JSON body payloads, or - `application/octet-stream` for the [publish](#publish) endpoint (which uses a - custom binary format). Not set for requests without a body. -- `Accept`: `application/json` -- `User-Agent`: The Cargo version such as `cargo/1.32.0 (8610973aa - 2019-01-02)`. This may be modified by the user in a configuration value. - Added in 1.29. +Other headers vary by endpoint and are documented below. ## Publish - Endpoint: `/api/v1/crates/new` - Method: PUT - Authorization: Included +- Headers: + - `Content-Type`: `application/octet-stream` + - `Accept`: `application/json` +- Body: Included (see below) The publish endpoint is used to publish a new version of a crate. The server should validate the crate, make it available for download, and add it to the @@ -190,6 +190,9 @@ A successful response includes the JSON object: - Endpoint: `/api/v1/crates/{crate_name}/{version}/yank` - Method: DELETE - Authorization: Included +- Headers: + - `Accept`: `application/json` +- Body: None The yank endpoint will set the `yank` field of the given version of a crate to `true` in the index. @@ -208,6 +211,10 @@ A successful response includes the JSON object: - Endpoint: `/api/v1/crates/{crate_name}/{version}/unyank` - Method: PUT - Authorization: Included +- Headers: + - `Content-Type`: `application/json` + - `Accept`: `application/json` +- Body: "" The unyank endpoint will set the `yank` field of the given version of a crate to `false` in the index. @@ -234,6 +241,9 @@ how [crates.io] handles owners via GitHub users and teams. - Endpoint: `/api/v1/crates/{crate_name}/owners` - Method: GET - Authorization: Included +- Headers: + - `Accept`: `application/json` +- Body: None The owners endpoint returns a list of owners of the crate. @@ -261,6 +271,10 @@ A successful response includes the JSON object: - Endpoint: `/api/v1/crates/{crate_name}/owners` - Method: PUT - Authorization: Included +- Headers: + - `Content-Type`: `application/json` + - `Accept`: `application/json` +- Body: Included (see below) A PUT request will send a request to the registry to add a new owner to a crate. It is up to the registry how to handle the request. For example, @@ -292,6 +306,10 @@ A successful response includes the JSON object: - Endpoint: `/api/v1/crates/{crate_name}/owners` - Method: DELETE - Authorization: Included +- Headers: + - `Content-Type`: `application/json` + - `Accept`: `application/json` +- Body: Included (see below) A DELETE request will remove an owner from a crate. The request should include the following JSON object: @@ -318,6 +336,10 @@ A successful response includes the JSON object: - Endpoint: `/api/v1/crates` - Method: GET +- Authorization: Not Included +- Headers: + - `Accept`: `application/json` +- Body: None - Query Parameters: - `q`: The search query string. - `per_page`: Number of results, default 10, max 100. From c70bba2cafdaa4001eda83a157021306b666a1dd Mon Sep 17 00:00:00 2001 From: Daniel Alley Date: Sat, 4 Apr 2026 15:30:20 -0400 Subject: [PATCH 3/3] Make the put() method consistent with the others Take the body argument as Option<&[u8] --- crates/crates-io/lib.rs | 8 ++++---- src/doc/src/reference/registry-web-api.md | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/crates-io/lib.rs b/crates/crates-io/lib.rs index 7c7589aa2ba..d95ce91c888 100644 --- a/crates/crates-io/lib.rs +++ b/crates/crates-io/lib.rs @@ -244,7 +244,7 @@ impl Registry { pub fn add_owners(&mut self, krate: &str, owners: &[&str]) -> Result { let body = serde_json::to_string(&OwnersReq { users: owners })?; - let body = self.put(&format!("/crates/{}/owners", krate), body.as_bytes())?; + let body = self.put(&format!("/crates/{}/owners", krate), Some(body.as_bytes()))?; assert!(serde_json::from_str::(&body)?.ok); Ok(serde_json::from_str::(&body)?.msg) } @@ -365,14 +365,14 @@ impl Registry { } pub fn unyank(&mut self, krate: &str, version: &str) -> Result<()> { - let body = self.put(&format!("/crates/{}/{}/unyank", krate, version), &[])?; + let body = self.put(&format!("/crates/{}/{}/unyank", krate, version), None)?; assert!(serde_json::from_str::(&body)?.ok); Ok(()) } - fn put(&mut self, path: &str, b: &[u8]) -> Result { + fn put(&mut self, path: &str, b: Option<&[u8]>) -> Result { self.handle.put(true)?; - self.req(path, Some(b), Auth::Authorized) + self.req(path, b, Auth::Authorized) } fn get(&mut self, path: &str) -> Result { diff --git a/src/doc/src/reference/registry-web-api.md b/src/doc/src/reference/registry-web-api.md index b627550dee6..b9100d55ca9 100644 --- a/src/doc/src/reference/registry-web-api.md +++ b/src/doc/src/reference/registry-web-api.md @@ -212,9 +212,8 @@ A successful response includes the JSON object: - Method: PUT - Authorization: Included - Headers: - - `Content-Type`: `application/json` - `Accept`: `application/json` -- Body: "" +- Body: None The unyank endpoint will set the `yank` field of the given version of a crate to `false` in the index.