feat: allow PATCH in http_request for non-replicated calls - #6245
Conversation
Mirrors dfinity#6199 (PUT/DELETE). PATCH is a mutation, so like PUT/DELETE it is restricted to non-replicated mode (a replicated outcall is sent once per replica; PATCH is not guaranteed idempotent per RFC 5789). Requested in dfinity#6244. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
http_request for non-replicated calls
| @@ -1,5 +1,8 @@ | |||
| ## Changelog {#changelog} | |||
|
|
|||
| ### 0.62.0 (2025-06-03) {$0_62_0} | |||
There was a problem hiding this comment.
The date should be updated and this PR merged when the feature is rolled out on mainnet.
mraszyk
left a comment
There was a problem hiding this comment.
LGTM I'll approve when the feature is rolled out to prevent accidental merging.
Thank you @mraszyk, the implementation PR sits right here: dfinity/ic#10378! |
…dfinity#10378) ## What Adds the `PATCH` HTTP method to canister HTTPS outcalls (`http_request`), following the `PUT`/`DELETE` rollout in dfinity#8715 / dfinity#8717. `PATCH` variants are added across every HTTP-method enum and conversion in the outcall path: - `ic_management_canister_types_private::HttpMethod` and the public `ic-management-canister-types` `HttpMethod` (+ `tests/ic.did` candid + fixture) - `CanisterHttpMethod` (+ the `state.metadata.v1` protobuf enum, its `.proto`, and the generated Rust) - the `https_outcalls` service `.proto`, the adapter method mapping (`rpc_server.rs`), and the client (`client.rs`) - the pocket-ic REST `CanisterHttpMethod` and the pocket-ic-server mapping ## Rejected in the execution layer until rollout Per review, and following the staged approach from dfinity#8715: this PR **plumbs** `PATCH` but does **not** enable it on replicated subnets yet. `generate_from_args` and `generate_from_flexible_args` reject `PATCH` outright with a new `CanisterHttpRequestContextError::HttpMethodNotYetSupported` (*"The PATCH HTTP method is not yet supported."*), independent of `is_replicated`. This guarantees no `PATCH` `http_method` enters replicated state before the variant is understood by all replicas (avoiding cross-version deserialization / replay divergence during rollout). A follow-up PR removes the guard once the rollout is complete, at which point `PATCH` behaves like `PUT`/`DELETE` — permitted only for non-replicated / deterministic-response-count requests, since `PATCH` is a non-idempotent mutation (RFC 5789 §2). ## Tests - `canister_http.rs`: `patch_is_rejected_until_rollout` asserts `PATCH` is rejected via both entry points; `PATCH` is dropped from the `PUT`/`DELETE` cases in `put_delete_requires_non_replicated` and `flexible_methods_require_deterministic_response_counts`; the enum-stability assertion stays `[1, 2, 3, 4, 5, 6]`. - `https_outcalls/adapter/tests/server_test.rs`: `test_canister_http_server_patch` + a `/patch` route on the test server (the adapter mapping is below the execution-layer guard, so it is exercised directly). - `rs/tests/networking/canister_http_correctness_test.rs`: `test_patch_rejected` asserts a `PATCH` outcall is rejected. - `httpbin-rs`: `/anything` gains a `PATCH` route. ## Notes for reviewers - Staged rollout per @eichhorl: `PATCH` is plumbed but hard-rejected in the execution layer; the enabling PR follows once the rollout is done. - Built/validated by CI rather than locally. Spec/docs companion: dfinity/portal#6245. Requested in dfinity/portal#6244. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@ggreif @mraszyk we should make these changes in the https://github.com/dfinity/developer-docs repository |
|
The content of this PR has been migrated to One item for human confirmation: this PR adds a This PR (#6245) can be closed once the |
|
Closing in favour of dfinity/developer-docs#289. |
Mirrors dfinity/portal#6199 (PUT/DELETE). PATCH is a mutation, so like PUT/DELETE it is restricted to non-replicated mode (a replicated outcall is sent once per replica; PATCH is not guaranteed idempotent per RFC 5789). Requested in dfinity/portal#6244. Migrated from dfinity/portal#6245. NOTE: The changelog entry uses 0.63.0 because developer-docs already has a 0.62.0 entry with different content. The correct version number needs human confirmation before this PR is merged. Co-Authored-By: Claude <noreply@anthropic.com>
Migrated from dfinity/portal#6245 (moved to this repo per reviewer request — the spec is now hosted here). ### What - `public/references/ic.did`: `method` variant → `variant { get; head; post; put; delete; patch }`. - `docs/references/ic-interface-spec/management-canister.md`: document `PATCH` alongside `PUT`/`DELETE` as supported in non-replicated mode only (two paragraphs). - `docs/references/ic-interface-spec/changelog.md`: new `0.63.0 (TBD)` entry. > **Note:** the changelog uses `0.63.0` because `0.62.0` is already present in this repo with different content (2025-05-26). Please confirm the correct version number before merging. ### Why `PATCH` is the last common REST verb missing after `PUT`/`DELETE` landed (dfinity/portal#6199). It is pervasive in modern REST APIs for partial updates (Google Calendar, GitHub, Stripe, …). The concrete driver is generated Motoko clients for the Google Calendar API whose partial-update operations are otherwise un-callable. ### Why non-replicated only Same rationale as `PUT`/`DELETE`: in replicated mode an outcall is issued once per replica; `PATCH` is additionally not guaranteed idempotent (RFC 5789 §2). The existing `is_replicated` machinery already covers it; no new mechanism is introduced. ### Notes - Spec/docs change only. The corresponding runtime implementation is in dfinity/ic#10378. - The date in the changelog entry should be updated to the mainnet rollout date before merging. - Originally opened as dfinity/portal#6245; that PR will be closed once this one merges. --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: mraszyk <31483726+mraszyk@users.noreply.github.com>
Resolves #6244.
Adds the
PATCHHTTP method to the canisterhttp_request(HTTPS outcalls) management-canister interface, mirroring thePUT/DELETEaddition in #6199.What
ic.did:methodvariant →variant { get; head; post; put; delete; patch }.ic-interface-spec.md: documentPATCHalongsidePUT/DELETEas supported in non-replicated mode only.interface-spec-changelog.md: new0.62.0entry.Why
PATCHis pervasive in modern REST APIs for partial updates (Google Calendar, GitHub, Stripe, …). It is currently the only common verb missing afterPUT/DELETElanded, which blocks canisters from calling those endpoints. The concrete driver is a generated Motoko client for the Google Calendar API, whose partial-update operations (events.patch,calendars.patch,acl.patch,calendarList.patch) are otherwise un-callable.Why non-replicated only
Same rationale as
PUT/DELETE: in replicated mode an outcall is issued once per replica, so a mutation would hit the remote N times, and mutation responses rarely agree byte-for-byte across replicas.PATCHadditionally is not guaranteed idempotent (RFC 5789 §2) — it is idempotent only for field-merge bodies — which makes the non-replicated restriction even more appropriate. The existingis_replicatedmachinery already covers it; no new mechanism is introduced.Notes
dfinity/ic.🤖 Generated with Claude Code