Skip to content

Conversation

@matthewgrossman
Copy link

@matthewgrossman matthewgrossman commented Dec 6, 2025

Background Context

I read #469 and discussed with @gi0baro in this thread emmett-framework/granian#157 about trying to get this as part of the ASGI spec. The implementation here is just a straight copy of the discussion in the relevant thread. It didn't seem that people had too much of a disagreement of what it'd look like, and if we did we could hash it out in this PR.

Links:


(draft)

This extends the http.response.pathsend extension to support partial file sends by adding optional offset and count parameters, enabling HTTP Range request support.

The parameters mirror the existing http.response.zerocopysend API:

  • offset: byte position to start reading from (default: beginning)
  • count: number of bytes to send (default: to end of file)

This allows ASGI frameworks to implement range requests without loading the bytes themselves, offloading the operation to the server.

Servers may advertise range support capability by setting {"ranges": True} in the extension scope dict, allowing frameworks to detect this capability.

The application remains responsible for setting appropriate headers (Content-Range, Content-Length, Accept-Ranges) in the response.

Fixes #469


Implementation

This original comment also proposed an alternative that uses range instead of offset+count. I don't really have strong opinions one way or another about which we use, but I see the trade-offs as:

  1. range is just one key, and its explicit about the start/end bytes. If we were to do this, we'd need to make a decision about being inclusive/exclusive with the ending byte (I think exclusive ending byte is common in most situations, but http range GETs are inclusive, so we should likely match that: docs)
  2. offset/count matches what we have for zerocopysend, so we're staying consistent within ASGI

I'm also in the process of a branch on granian where I'm going to do a quick example implementation just to give an example. I'll likely use granian + starlette to showcase what this extended pathsend could look like

First time contributing to and submitting a PR to asgiref, so please feel free to badger me about bits that I'm missing!

@matthewgrossman matthewgrossman force-pushed the mgrossman/pathsend-with-offset branch from a45b794 to c62c6bb Compare December 7, 2025 01:42
This extends the http.response.pathsend extension to support partial
file sends by adding optional offset and count parameters, enabling
HTTP Range request support (RFC 7233) and efficient seeking in large
media files.

The parameters mirror the existing http.response.zerocopysend API:
- offset: byte position to start reading from (default: beginning)
- count: number of bytes to send (default: to end of file)

This allows ASGI frameworks to implement range requests without
handling file descriptors directly, offloading the operation to
the server while maintaining pathsend's simpler API.

Capability Signaling:
Servers that support offset and count parameters MUST advertise this
by setting {"ranges": True} in the pathsend extension scope dict.
Applications should check for this flag before using these parameters.
This provides a clear upgrade path from basic pathsend to range-capable
pathsend without ambiguity.

The application remains responsible for:
- Checking the "ranges" capability flag
- Parsing Range request headers
- Setting appropriate response headers (Content-Range, Content-Length,
  Accept-Ranges, status 206)

Fixes django#469
Related: emmett-framework/granian#157
@matthewgrossman matthewgrossman force-pushed the mgrossman/pathsend-with-offset branch from c62c6bb to e59e343 Compare December 7, 2025 01:45
@carltongibson
Copy link
Member

Cc @Kludex

@Kludex
Copy link
Contributor

Kludex commented Dec 8, 2025

cc @gi0baro

@gi0baro
Copy link
Contributor

gi0baro commented Dec 11, 2025

My 2 cents: the 2 keys vs single tuple key won't really change that much, the latter just simplify the message parsing on the server side.

I would say the offset and count naming doesn't really sound appealing to me though: while I understand the consistency argument, I don't really feel we need to make different extensions consistent with each other in terms of messages format, in fact pathsend already differs from zerocopysend by the fact it has the additional boolean in the extension scope dict.
I could argue offset and count make sense for zerocopysend given the object in the message is a file descriptor, thus it's quite clear we're talking about bytes in regarding of that descriptor. On the other hand, I don't see such words to be super-meaningful when we're talking about a string path: it's not very straightforward "count" is the number of bytes that should be read from the file at that path. This might be even more confusing coupled with the "ranges" naming in the extension scope.
I'd say if we use "ranges" in scope extension, maybe "range_start" and "range_len" are more explicit keys to have in the message. But maybe this is just me, and the actual proposal is perfectly fine for the majority of people involved.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add offset and count to Path Send

4 participants