* Add content-length header by default
* Add test for #1099
* Revert changes and add tests
* Check if is StreamingResponse or FileResponse before adding content-length headers
* Change conditional logic to check if body is present
* ensure TestClient requests run in the same EventLoop as lifespan
* for lifespan task verification, use native task identity rather than anyio.abc.TaskInfo equality
https://github.com/agronholm/anyio/issues/324
* remove redundant pragma: no cover
* it's now a loop_id not a threading.ident
* replace Protocol with plain Callable TypeAlias
* use lifespan_context to actually open a task group
trio should complain if used incorrectly here.
* assign self.portal once, schedule reset immediately after assignment
* inline apps into their tests
* make task/loop trackers nonlocals
as opposed to ClassVar assignment
Co-authored-by: Jamie Hewland <jhewland@gmail.com>
Co-authored-by: Jordan Speicher <jordan@jspeicher.com>
Co-authored-by: Jordan Speicher <uSpike@users.noreply.github.com>
* First whack at anyio integration
* Fix formatting
* Remove debug messages
* mypy fixes
* Update README.md
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
* Fix install_requires typo
* move_on_after blocks if deadline is too small
* Linter fixes
* Improve WSGI structured concurrency
* Tests use anyio
* Checkin progress on testclient
* Prep for anyio 3
* Remove debug backend option
* Use anyio 3.0.0rc1
* Remove old style executor from GraphQLApp
* Fix extra import
* Don't cancel task scope early
* Wait for wsgi sender to finish before exiting
* Use memory object streams in websocket tests
* Test on asyncio, asyncio+uvloop, and trio
* Formatting fixes
* run_until_first_complete doesn't need a return
* Fix middleware app call
* Simplify middleware exceptions
* Use anyio for websocket test
* Set STARLETTE_TESTCLIENT_ASYNC_BACKEND in tests
* Pass async backend to portal
* Formatting fixes
* Bump anyio
* Cleanup portals and add TestClient.async_backend
* Use anyio.run_async_from_thread to send from worker thread
* Use websocket_connect as context manager
* Document changes in TestClient
* Formatting fix
* Fix websocket raises coverage
* Update to anyio 3.0.0rc3 and replace aiofiles
* Apply suggestions from code review
Co-authored-by: Alex Grönholm <alex.gronholm@nextday.fi>
* Bump to require anyio 3.0.0 final
* Remove mention of aiofiles in README.md
* Pin jinja2 to releases before 3 due to DeprecationWarnings
* Add task_group as application attribute
* Remove run_until_first_complete
* Undo jinja pin
* Refactor anyio.sleep into an event
* Use one less task in test_websocket_concurrency_pattern
* Apply review suggestions
* Rename argument
* fix start_task_soon type
* fix BaseHTTPMiddleware when used without Starlette
* Testclient receive() is a non-trapping function if the response is already complete
This allows for a zero deadline when waiting for a disconnect message
* Use variable annotation for async_backend
* Update docs regarding dependency on anyio
* Use CancelScope instead of move_on_after in request.is_disconnected
* Cancel task group after returning middleware response
Add test for https://github.com/encode/starlette/issues/1022
* Add link to anyio backend options in testclient docs
* Add types-dataclasses
* Re-implement starlette.concurrency.run_until_first_complete and add a test
* Fix type on handler callable
* Apply review comments to clarify run_until_first_complete scope
Co-authored-by: Marcelo Trylesinski <marcelotryle@gmail.com>
Co-authored-by: Alex Grönholm <alex.gronholm@nextday.fi>
Co-authored-by: Thomas Grainger <tagrain@gmail.com>
* Convert comment type hints to annotations
* Update to python 3.6+ syntax
* Explicitly state response type annotation
* Explicitly define queue generic type
* use quote instead of quote_plus for RedirectResponse location header
adjust safe characters: rem. duplicate & symbol
add test for redirect quoting
* remove unused import
Co-authored-by: Jamie Hewland <jhewland@gmail.com>
* Set explicit Origin in CORS preflight response if allow_credentials is True and allow_origins is wildcard
When making a preflight request, the browser makes no indication as to whether the actual subsequent
request will pass up credentials. However, unless the preflight response explicitly allows the
request's `Origin` in the `Access-Control-Response-Header`, the browser will fail the CORS check and
prevent the actual follow-up CORS request. This means that responding with the `*` wildcard is not
sufficient to allow preflighted credentialed requests. The current workaround is to provide an
equivalently permissive `allow_origin_regex` pattern.
The `simple_response()` code already performs similar logic which currently only applies to
non-preflighted requests since the browser would never make a preflighted request that hits this
code due to this issue:
```
if self.allow_all_origins and has_cookie:
headers["Access-Control-Allow-Origin"] = origin
```
This just bring the two halves inline with each other.
* Add Vary header to preflight response if allow_credentials
* Use allow_explicit_origin() for preflight request_headers
This simplifies the code slightly by using this recently added method.
It has some trade-offs, though. We now construct a `MutableHeaders` instead of a simple `dict` when
copying the pre-computed preflight headers, and we move the `Vary` header construction out of the
pre-computation and into the call handler.
I think it makes the code more maintainable and the added per-call computation is minimal.
* Convert MutableHeaders to dict for PlainTextResponse
* Revert back to dict() for preflight headers
This also names and caches some of the boolean tests in __init__() which we use in later if-blocks.
This follows the existing pattern in order to better self-document the code.
* Clean up comments
* Remove unused self.allow_credentials attribute
* Add HEAD to CORS ALL_METHODS list
The HEAD method is conspicuously absent from the allowed methods list when `allow_methods="*"` is
used. This doesn't really affect CORS preflight requests, as HEAD requests aren't preflighted by the
browser, but it does prevent the actual cross-origin HEAD response from being read by the calling
app.
[This can catch people off-guard.](https://discuss.encode.io/t/for-cors-middleware-why-is-head-not-included-in-all-methods/939)
This simply adds HEAD to the `ALL_METHODS` list in the CORS middleware module and includes some
additional tests to validate the new behavior.
* Update tests/middleware/test_cors.py to use more explicit status code check
Co-authored-by: euri10 <euri10@users.noreply.github.com>
Co-authored-by: euri10 <euri10@users.noreply.github.com>
Co-authored-by: Jamie Hewland <jhewland@gmail.com>
* Add Origin to Vary header on credentialed CORS response
According to the [MDN CORS docs]
(https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Access-Control-Allow-Origin), the `Origin`
item should be added to the `Vary` header when the `Access-Control-Allow-Origin` is set to an
explicit origin value, as opposed to the `*` wildcard.
>If the server specifies a single origin (that may dynamically change based on the requesting origin
as part of a white-list) rather than the "*" wildcard, then the server should also include Origin in
the Vary response header — to indicate to clients that server responses will differ based on the
value of the Origin request header.
The existing code fails to update the `Vary` list when the server is configured to allow all
origins (`*`) and the request has a `Cookie` header (ie. credentialed). In that situation, the
`Access-Control-Allow-Origin` header will be set to the request's `Origin` value.
It appears this may have just been a simple oversight in the original implementation. This updates
the code to add `Origin` to the `Vary` header under these circumstancesIf it was intentionally
omitted, I'd be delighted to learn why.
* Add type annotations
* Add test to ensure that the vary header does not contain origin if request is non-credentialed
* Make session cookie use ASGI root path
* Check if ASGI root_path exists before using it
Co-authored-by: Tom Christie <tom@tomchristie.com>
* Remove comment
* Add test
Co-authored-by: Mahmoud Hanafy <mahmoud.hanafy@retresco.de>
Co-authored-by: Tom Christie <tom@tomchristie.com>
* StaticFiles: Fix cache validation bug for deleted files in html mode
Previously StaticFiles would return 304 for a deleted file if its
Last-Modified date was the same as that of 404.html
* Use black formatter
Co-authored-by: Jamie Hewland <jhewland@gmail.com>
* Add __init__ file for tests.middleware so Mypy 0.800 is happy
* testclient: Tie loop lifetime to thread
* ServerErrorMiddleware: Don't use undocumented TracebackException.exc_traceback attribute
* Allow usage of async partial methods
* Added test for partial async endpoint
* Double quotes vs single quotes
* Support multiple levels of partials, check Python < 3.8
* Skip coverage for py3.8 branch
Co-authored-by: Florimond Manca <florimond.manca@gmail.com>
* Use format_exception instead of format_tb
This gives much more information about the exception, including causes, and the exception message itself, in addition to the trackback
* Update test
Co-authored-by: Jamie Hewland <jhewland@gmail.com>
* Update CI scripts to match httpcore
* Run test suite on pushes to master
* Update scripts README
* Don't bother with flake8 extensions for now
* Remove unnecessary PYTHONPATH from build, publish
* test_routing: Use a stub app instead of ellipsis
* Add link to issue about type-checking tests
* Use os.PathLike in StaticFiles for directory
This allows using `pathlib.Path` in addition to `str` for configuring
the base directory of the static files in line with how python3.6+
handles filesystem operations.
Fixes#1004
* Fixed `mimetypes.guess_type` not supporting PathLike on py3.7 and below
* Updated staticfiles documentation with `PathLike` param
* Failing test for HEAD method when using a middleware
* Fixed by sending empty body if send_header_only
* Lint
* Respect asgi spec
* Be explicit in case we send headers only