Skip to content

πŸ› Bug Report β€” fetch() can complete a chunked upload after its body stream errorsΒ #7983

Open

Description

@macovedj

When an unknown-length ReadableStream used as a fetch() request body errors partway through an upload, workerd can still send the terminating HTTP/1.1 zero chunk. The receiver therefore sees the partial body as a complete request and may return a successful response.

For example, this Worker sends four 64 KiB chunks, then fails while producing the next chunk:

export default {
  async fetch(request, env) {
    let chunks = 0;
    const body = new ReadableStream({
      pull(controller) {
        if (chunks++ < 4) {
          controller.enqueue(new Uint8Array(64 * 1024));
        } else {
          controller.error(new Error("source failed partway"));
        }
      },
    });

    try {
      const response = await fetch(env.UPLOAD_URL, {
        method: "PUT",
        body,
      });
      return new Response(
        `fetch resolved ${response.status}: ${await response.text()}`
      );
    } catch (error) {
      return new Response(`fetch rejected: ${error.message}`);
    }
  },
};

Set UPLOAD_URL to an HTTP/1.1 receiver that reads the request body and responds with 200: stored when its request end event fires.

Observed:

worker: fetch resolved 200: stored
upstream: end after 262144 bytes (complete body)

Expected: the source error should abort the upload without sending the terminating chunk, so the receiver recognizes an incomplete request. A still-pending fetch() should reject. If response headers have already arrived, an ongoing response-body read should reject.

This affects streaming proxies that stop forwarding a body after validation fails or an upload limit is exceeded: the destination may accept the forwarded prefix as a complete upload.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions