document that headers must be set before streaming

This commit is contained in:
David Lord 2026-03-04 07:36:09 -08:00
parent f00ad424ee
commit a29f88ce6f
No known key found for this signature in database
GPG key ID: 43368A7AA8CC5926
4 changed files with 55 additions and 8 deletions

View file

@ -8,6 +8,21 @@ roundtrip to the filesystem?
The answer is by using generators and direct responses.
HTTP Response Behavior
----------------------
**Headers cannot be changed after the streaming response starts.**
When using streaming, it's important to be aware of the order than an HTTP
response is sent. All headers must be sent first, then the body. More headers
cannot be sent after the body has begun. Therefore, you must make sure all
headers are set before starting the response, outside the generator.
In particular, if the generator will access ``session``, be sure to do so in the
view as well so that the ``Vary: cookie`` header will be set. Do not modify the
session in the generator, as the ``Set-Cookie`` header will already be sent.
Basic Usage
-----------

View file

@ -225,5 +225,11 @@ functions to make this easier to use.
return stream_template("timeline.html")
These functions automatically apply the
:func:`~flask.stream_with_context` wrapper if a request is active, so
that it remains available in the template.
:func:`~flask.stream_with_context` wrapper if a request is active, so that
:data:`.request`, :data:`.session`, and :data:`.g` remain available in the
template.
More headers cannot be sent after the body has begun. Therefore, you must
make sure all headers are set before starting the response. In particular,
if the template will access ``session``, be sure to do so in the view as
well so that the ``Vary: cookie`` header will be set.

View file

@ -153,13 +153,27 @@ F = t.TypeVar("F", bound=t.Callable[..., t.Any])
def copy_current_request_context(f: F) -> F:
"""A helper function that decorates a function to retain the current
request context. This is useful when working with greenlets. The moment
the function is decorated a copy of the request context is created and
then pushed when the function is called. The current session is also
included in the copied request context.
"""Decorate a function to run inside the current request context. This can
be used when starting a background task, otherwise it will not see the app
and request objects that were active in the parent.
Example::
.. warning::
Due to the following caveats, it is often safer (and simpler) to pass
the data you need when starting the task, rather than using this and
relying on the context objects.
In order to avoid execution switching partially though reading data, either
read the request body (access ``form``, ``json``, ``data``, etc) before
starting the task, or use a lock. This can be an issue when using threading,
but shouldn't be an issue when using greenlet/gevent or asyncio.
If the task will access ``session``, be sure to do so in the parent as well
so that the ``Vary: cookie`` header will be set. Modifying ``session`` in
the task should be avoided, as it may execute after the response cookie has
already been written.
.. code-block:: python
import gevent
from flask import copy_current_request_context

View file

@ -68,6 +68,18 @@ def stream_with_context(
available, even though at the point the generator runs the request context
will typically have ended.
.. warning::
Due to the following caveat, it is often safer to pass the data you
need as arguments to the generator, rather than relying on the context
objects.
More headers cannot be sent after the body has begun. Therefore, you must
make sure all headers are set before starting the response. In particular,
if the generator will access ``session``, be sure to do so in the view as
well so that the ``Vary: cookie`` header will be set. Do not modify the
session in the generator, as the ``Set-Cookie`` header will already be sent.
Use it as a decorator on a generator function:
.. code-block:: python