http-extensions icon indicating copy to clipboard operation
http-extensions copied to clipboard

RESUMABLE: What happens when a POST request is made for an already-created incomplete resource?

Open mertalev opened this issue 3 months ago • 18 comments

My use-case is that a given user can only make one resource with a given checksum. This checksum is provided in the Repr-Digest header in the initial request and validated on upload completion. The resource URI itself is not a checksum, but a UUID. Once the client has created the resource with the initial POST request, it should either complete it (possibly in multiple parts) or cancel it. Once cancelled, the user is allowed to create a new resource with a different UUID for the same checksum.

The spec is clear enough that a complete resource should not be modified and provides a response to use for this case. However, what should happen if the client creates this resource without completing it, then tries to create it again?

  • Should it be rejected with a 400 code?
  • Should it cause the incomplete resource to be invalidated?
  • Should it include a Location header to direct it to the existing resource?
  • Should it truncate the existing resource and start over?

Given the client needs to know the resource in order to resume or cancel it in this case, I think it makes sense to include the Location header with an error status code to allow them to take further action, possibly also including the Upload-Offset.

mertalev avatar Oct 02 '25 18:10 mertalev

A good way to think of the resumable upload is that it's just an in-progress regular upload. What do you do if a user performs 2 concurrent POSTs with the same checksum without using resumable upload? Whatever you decide to do should apply here as well.

In general, we recommend that the new attempt overrides the old attempt since the client might have recovered from a crash and unaware of the old attempt. Yes, you could design something to try to recover the state, but I'm not certain that we need to add the complexity to the protocol. Essentially this would be adding a lookup mechanism of the temporary resource from the file checksum.

guoye-zhang avatar Oct 02 '25 19:10 guoye-zhang

As I understand it, the spec as written doesn't seem to have a way for the client to resume an upload if it doesn't know or forgets the URI provided by the server, such as if it crashes or restarts, unless it persists this somewhere. This would be a way for the client to retrieve this URI from the server based on some criteria (in this case the user ID and checksum). It would also mean the upload could be resumed from a different client with the same file.

It's of course possible to have a custom design that does this when the client and server agree on this behavior, but it'd be nice to have a standard way to do this that all RUFH clients can understand.

mertalev avatar Oct 02 '25 20:10 mertalev

It is achievable today with a 104 response followed immediately by a 5xx response. The client would perform offset retrieving and upload appending. We might want to change this sentence in "Offset Retrieval" but it's not a MUST:

The offset can be less than or equal to the number of bytes of representation data that the client has already sent.

guoye-zhang avatar Oct 02 '25 20:10 guoye-zhang

That's clever! It's maybe semantically incorrect since it isn't really a server error, but it addresses the use-case in question.

mertalev avatar Oct 02 '25 20:10 mertalev

Yeah we could in theory define a separate status code for "please retry", but the workaround above should already work with the implementation today on iOS/macOS

guoye-zhang avatar Oct 02 '25 21:10 guoye-zhang

It is achievable today with a 104 response followed immediately by a 5xx response. The client would perform offset retrieving and upload appending. We might want to change this sentence in "Offset Retrieval" but it's not a MUST:

The offset can be less than or equal to the number of bytes of representation data that the client has already sent.

The spec doesn't prohibit the offset from jumping ahead (from the client's perspective), but it explicitly allows clients to error out in such cases:

The client MAY reject an offset which is greater than the number of bytes it has already sent during this upload.

Therefore, I would assume that this server behavior won't work with every client.

Acconut avatar Oct 16 '25 16:10 Acconut

Should it allow the client to reject a greater offset? Even a streamed input can work with a greater offset.

mertalev avatar Oct 16 '25 16:10 mertalev

Allowing clients to reject greater offsets can make their implementation simpler, especially if they are uploading streaming data. In addition, the offset should not jump ahead for usual uploads where one client uploads a representation independently of previous uploads. That was the reason behind allowing this client behavior so far.

Acconut avatar Oct 16 '25 17:10 Acconut

I can say that as a server implementer, although supporting all RUFH clients is ideal, my priority is targeting URLSession and probably one or two more clients for other platforms. So long as it works for those target clients, I will consider the approach viable. If I did want to handle other clients as a server, I would need to go by user agent or something to detect what the client supports and behave differently for them. Allowing clients to reject this thus comes with the caveat of potentially fractured interoperability.

mertalev avatar Oct 16 '25 17:10 mertalev

URLSession passes the responsibility to its users. If they are uploading a file, arbitrary offset is supported automatically. Otherwise they are asked to provide a request body stream from a certain offset which they might be able to do or decline.

guoye-zhang avatar Oct 17 '25 00:10 guoye-zhang

I suppose that would still be fine as long as the client DELETE's the upload resource if declined. The only issue is if they both can't accept the offset and also take no action, since it would just get stuck at that point.

mertalev avatar Oct 17 '25 02:10 mertalev

We haven't implemented upload cancellation in URLSession yet, so yeah it will be stuck

guoye-zhang avatar Oct 17 '25 03:10 guoye-zhang

We haven't implemented upload cancellation in URLSession yet, so yeah it will be stuck

Hmm, perhaps it should be an opt-in behavior through a request header to be safe. If it isn't provided, the server can assume it won't support the offset and overwrite the earlier attempt instead.

Out of curiosity, is the RUFH handling open source? I might be able to contribute some spec functionality if so, mainly cancellation and limits in mind.

mertalev avatar Oct 17 '25 17:10 mertalev

URLSession is not open source unfortunately. Please file feedbacks requesting this and send me the FB number, so it can be prioritized.

guoye-zhang avatar Oct 18 '25 03:10 guoye-zhang

URLSession is not open source unfortunately. Please file feedbacks requesting this and send me the FB number, so it can be prioritized.

Thank you for prioritizing this! I made the feedbacks FB20751965 and FB20752104. I'm happy to provide more details if needed or participate in dev betas to give more feedback.

mertalev avatar Oct 20 '25 16:10 mertalev

Can we close this issue or are there inquiries to adapt the resumable upload process? To me it seems that the questions have been answered and no further action is necessary for now.

Acconut avatar Oct 20 '25 20:10 Acconut

The spec could probably use different wording to acknowledge that the server is allowed to send a greater offset and that the client should accept it if it's able. The 5xx status is also a bit awkward and might benefit from a more explicit response. These are the main things that come to mind for this issue.

mertalev avatar Oct 20 '25 21:10 mertalev

https://github.com/httpwg/http-extensions/pull/3319 proposed text for handling 409 on the client-side.

Acconut avatar Nov 02 '25 22:11 Acconut