pax_global_header 0000666 0000000 0000000 00000000064 15251720332 0014512 g ustar 00root root 0000000 0000000 52 comment=421708fbc1a704dac8bd4053a7c4c0bf4d3704ee
uvicorn-0.53.0/ 0000775 0000000 0000000 00000000000 15251720332 0013264 5 ustar 00root root 0000000 0000000 uvicorn-0.53.0/.github/ 0000775 0000000 0000000 00000000000 15251720332 0014624 5 ustar 00root root 0000000 0000000 uvicorn-0.53.0/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15251720332 0017007 5 ustar 00root root 0000000 0000000 uvicorn-0.53.0/.github/ISSUE_TEMPLATE/1-issue.yml 0000664 0000000 0000000 00000003623 15251720332 0021024 0 ustar 00root root 0000000 0000000 name: Issue
description: Report a bug or unexpected behavior. π
body:
- type: markdown
attributes:
value: Thank you for contributing to Uvicorn! β
- type: checkboxes
id: checks
attributes:
label: Initial Checks
description: Just making sure you open a discussion first. π
options:
- label: I confirm this was discussed, and the maintainers suggest I open an issue.
required: true
- label: I'm aware that if I created this issue without a discussion, it may be closed without a response.
required: true
- type: textarea
id: discussion
attributes:
label: Discussion Link
description: |
Please link to the discussion that led to this issue.
If you haven't discussed this issue yet, please do so before opening an issue.
render: Text
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: |
Please explain what you're seeing and what you would expect to see.
Please provide as much detail as possible to make understanding and solving your problem as quick as possible. π
validations:
required: true
- type: textarea
id: example
attributes:
label: Example Code
description: >
If applicable, please add a self-contained,
[minimal, reproducible, example](https://stackoverflow.com/help/minimal-reproducible-example)
demonstrating the bug.
render: Python
- type: textarea
id: version
attributes:
label: Python, Uvicorn & OS Version
description: |
Which version of Python & Uvicorn are you using, and which Operating System?
Please run the following command and copy the output below:
```bash
python -m uvicorn --version
```
render: Text
validations:
required: true
uvicorn-0.53.0/.github/ISSUE_TEMPLATE/config.yml 0000664 0000000 0000000 00000000676 15251720332 0021010 0 ustar 00root root 0000000 0000000 # Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
blank_issues_enabled: false
contact_links:
- name: Discussions
url: https://github.com/Kludex/uvicorn/discussions
about: The "Discussions" forum is where you want to start. π
- name: Chat
url: https://discord.com/invite/SWU73HffbV
about: Our community Discord server. π¬
uvicorn-0.53.0/.github/PULL_REQUEST_TEMPLATE.md 0000664 0000000 0000000 00000001041 15251720332 0020421 0 ustar 00root root 0000000 0000000
# Summary
# Checklist
- [ ] I understand that this PR may be closed in case there was no previous discussion. (This doesn't apply to typos!)
- [ ] I've added a test for each change that was introduced, and I tried as much as possible to make a single atomic change.
- [ ] I've updated the documentation accordingly.
uvicorn-0.53.0/.github/dependabot.yml 0000664 0000000 0000000 00000000635 15251720332 0017460 0 ustar 00root root 0000000 0000000 version: 2
updates:
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "monthly"
cooldown:
default-days: 7
groups:
python-packages:
patterns:
- "*"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: monthly
cooldown:
default-days: 7
groups:
github-actions:
patterns:
- "*"
uvicorn-0.53.0/.github/workflows/ 0000775 0000000 0000000 00000000000 15251720332 0016661 5 ustar 00root root 0000000 0000000 uvicorn-0.53.0/.github/workflows/benchmark.yml 0000664 0000000 0000000 00000001525 15251720332 0021341 0 ustar 00root root 0000000 0000000 name: CodSpeed
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
permissions:
id-token: write
contents: read
jobs:
benchmarks:
name: Run benchmarks
runs-on: ubuntu-latest
steps:
- uses: "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1" # v7.0.1
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.13"
enable-cache: true
- name: Install dependencies
run: scripts/install
shell: bash
- name: Run the benchmarks
uses: CodSpeedHQ/action@373d6868929f444bc08d901fd0eb0ad52a8875ea # v5.2.1
with:
mode: instrumentation
run: uv run pytest tests/benchmarks/ --codspeed -n 0
uvicorn-0.53.0/.github/workflows/main.yml 0000664 0000000 0000000 00000006267 15251720332 0020343 0 ustar 00root root 0000000 0000000 name: Test Suite
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
jobs:
tests:
name: "Python ${{ matrix.python-version }} ${{ matrix.os }}"
runs-on: "${{ matrix.os }}"
timeout-minutes: 10
permissions:
contents: read
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14", "3.14t"]
os: [windows-latest, ubuntu-latest, macos-latest]
steps:
- uses: "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1" # v7.0.1
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: ${{ matrix.python-version }}
enable-cache: true
- name: Install dependencies
run: scripts/install
shell: bash
- name: Run linting checks
run: scripts/check
if: "${{ matrix.os == 'ubuntu-latest'}}"
- name: "Build package & docs"
run: scripts/build
shell: bash
- name: "Run tests"
run: scripts/test
shell: bash
- name: "Enforce coverage"
run: scripts/coverage
shell: bash
docs-preview:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
env:
HAS_CLOUDFLARE_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN != '' }}
permissions:
contents: read
pull-requests: write
environment:
name: cloudflare
url: ${{ steps.deploy.outputs.deployment-url }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.12"
enable-cache: false
- name: Install dependencies
run: scripts/install
- name: Build docs
run: uv run zensical build
- name: Deploy preview to Cloudflare Workers
id: deploy
if: ${{ env.HAS_CLOUDFLARE_TOKEN == 'true' }}
uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: versions upload --tag pr-${{ github.event.pull_request.number }}
- name: Comment preview URL on PR
if: ${{ steps.deploy.outcome == 'success' }}
uses: thollander/actions-comment-pull-request@24bffb9b452ba05a4f3f77933840a6a841d1b32b # v3.0.1
with:
message: |
:book: Docs preview: ${{ steps.deploy.outputs.deployment-url }}
comment-tag: docs-preview
# https://github.com/marketplace/actions/alls-green#why
check:
if: always()
needs: [tests]
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@b5b5b37504aa4183270bd3d855c52a67f212be35 # v1.3.0
with:
jobs: ${{ toJSON(needs) }}
uvicorn-0.53.0/.github/workflows/publish.yml 0000664 0000000 0000000 00000005521 15251720332 0021055 0 ustar 00root root 0000000 0000000 name: Publish
on:
push:
tags:
- '*'
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: "3.11"
enable-cache: false
- name: Install dependencies
run: scripts/install
- name: Build package & docs
run: scripts/build
- name: Upload package distributions
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: package-distributions
path: dist/
- name: Upload documentation
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: documentation
path: site/
pypi-publish:
runs-on: ubuntu-latest
needs: build
if: success() && startsWith(github.ref, 'refs/tags/')
permissions:
id-token: write
environment:
name: pypi
url: https://pypi.org/project/uvicorn
steps:
- name: Download artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: package-distributions
path: dist/
- name: Publish distribution π¦ to PyPI
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
docs-publish:
runs-on: ubuntu-latest
needs: build
permissions:
contents: write
steps:
- name: Download artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: documentation
path: site/
- name: Publish documentation π to GitHub Pages
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453 # v4.1.0
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./site
docs-cloudflare:
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
environment:
name: cloudflare
url: https://uvicorn.dev
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Download artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: documentation
path: site/
- uses: cloudflare/wrangler-action@ebbaa1584979971c8614a24965b4405ff95890e0 # v4.0.0
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: deploy
uvicorn-0.53.0/.github/workflows/zizmor.yml 0000664 0000000 0000000 00000001121 15251720332 0020731 0 ustar 00root root 0000000 0000000 name: GitHub Actions Security Analysis
on:
push:
branches: ["main"]
pull_request:
branches: ["**"]
permissions: {}
jobs:
zizmor:
runs-on: ubuntu-latest
permissions:
security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
steps:
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Run zizmor π
uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2
uvicorn-0.53.0/.gitignore 0000664 0000000 0000000 00000000157 15251720332 0015257 0 ustar 00root root 0000000 0000000 .cache
.coverage
.coverage.*
.mypy_cache/
__pycache__/
uvicorn.egg-info/
venv/
htmlcov/
site/
dist/
.codspeed/
uvicorn-0.53.0/AI_POLICY.md 0000664 0000000 0000000 00000000747 15251720332 0015166 0 ustar 00root root 0000000 0000000 # AI Policy
You may use AI tools when contributing to Uvicorn. You remain responsible for every contribution you submit.
Before you open a pull request, search the open and closed pull requests, issues, and discussions. Do not open a pull
request when the same change has already been proposed or is already in progress.
Duplicate pull requests waste maintainer time and create unnecessary review work. Contributors who submit duplicate
pull requests will be banned from the project.
uvicorn-0.53.0/CHANGELOG.md 0000777 0000000 0000000 00000000000 15251720332 0021110 2docs/release-notes.md ustar 00root root 0000000 0000000 uvicorn-0.53.0/CITATION.cff 0000664 0000000 0000000 00000001164 15251720332 0015160 0 ustar 00root root 0000000 0000000 # This CITATION.cff file was generated with cffinit.
# Visit https://bit.ly/cffinit to generate yours today!
cff-version: 1.2.0
title: Uvicorn
message: >-
If you use this software, please cite it using the
metadata from this file.
type: software
authors:
- given-names: Marcelo
family-names: Trylesinski
email: marcelotryle@gmail.com
- given-names: Tom
family-names: Christie
email: tom@tomchristie.com
repository-code: "https://github.com/Kludex/uvicorn"
url: "https://uvicorn.dev/"
abstract: Uvicorn is an ASGI web server implementation for Python.
keywords:
- asgi
- server
license: BSD-3-Clause
uvicorn-0.53.0/LICENSE.md 0000664 0000000 0000000 00000002766 15251720332 0014703 0 ustar 00root root 0000000 0000000 Copyright Β© 2017-present, [Encode OSS Ltd](https://www.encode.io/).
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
uvicorn-0.53.0/README.md 0000664 0000000 0000000 00000011354 15251720332 0014547 0 ustar 00root root 0000000 0000000
An ASGI web server, for Python.
---
[](https://github.com/Kludex/uvicorn/actions)
[](https://pypi.python.org/pypi/uvicorn)
[](https://pypi.org/project/uvicorn)
[](https://discord.gg/RxKUF5JuHs)
---
**Documentation**: [https://uvicorn.dev](https://uvicorn.dev)
**Source Code**: [https://www.github.com/Kludex/uvicorn](https://www.github.com/Kludex/uvicorn)
---
Uvicorn is an ASGI web server implementation for Python.
Until recently Python has lacked a minimal low-level server/application interface for
async frameworks. The [ASGI specification][asgi] fills this gap, and means we're now able to
start building a common set of tooling usable across all async frameworks.
Uvicorn currently supports HTTP/1.1, HTTP/2 and WebSockets.
## Quickstart
Install using `pip`:
```shell
$ pip install uvicorn
```
This will install uvicorn with minimal (pure Python) dependencies.
```shell
$ pip install 'uvicorn[standard]'
```
This will install uvicorn with "Cython-based" dependencies (where possible) and other "optional extras".
In this context, "Cython-based" means the following:
- the event loop `uvloop` will be installed and used if possible.
- the http protocol will be handled by `httptools` if possible.
Moreover, "optional extras" means that:
- the websocket protocol will be handled by `websockets` (should you want to use `wsproto` you'd need to install it manually) if possible.
- the `--reload` flag in development mode will use `watchfiles`.
- `python-dotenv` will be installed should you want to use the `--env-file` option.
- `PyYAML` will be installed to allow you to provide a `.yaml` file to `--log-config`, if desired.
Create an application, in `example.py`:
```python
async def app(scope, receive, send):
assert scope['type'] == 'http'
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
(b'content-type', b'text/plain'),
],
})
await send({
'type': 'http.response.body',
'body': b'Hello, world!',
})
```
Run the server:
```shell
$ uvicorn example:app
```
---
## Why ASGI?
Most well established Python Web frameworks started out as WSGI-based frameworks.
WSGI applications are a single, synchronous callable that takes a request and returns a response.
This doesnβt allow for long-lived connections, like you get with long-poll HTTP or WebSocket connections,
which WSGI doesn't support well.
Having an async concurrency model also allows for options such as lightweight background tasks,
and can be less of a limiting factor for endpoints that have long periods being blocked on network
I/O such as dealing with slow HTTP requests.
---
## Alternative ASGI servers
A strength of the ASGI protocol is that it decouples the server implementation
from the application framework. This allows for an ecosystem of interoperating
webservers and application frameworks.
### Daphne
The first ASGI server implementation, originally developed to power Django Channels, is [the Daphne webserver][daphne].
It is run widely in production, and supports HTTP/1.1, HTTP/2, and WebSockets.
Any of the example applications given here can equally well be run using `daphne` instead.
```
$ pip install daphne
$ daphne app:App
```
### Hypercorn
[Hypercorn][hypercorn] was initially part of the Quart web framework, before
being separated out into a standalone ASGI server.
Hypercorn supports HTTP/1.1, HTTP/2, and WebSockets.
It also supports [the excellent `trio` async framework][trio], as an alternative to `asyncio`.
```
$ pip install hypercorn
$ hypercorn app:App
```
### Mangum
[Mangum][mangum] is an adapter for using ASGI applications with AWS Lambda & API Gateway.
### Granian
[Granian][granian] is an ASGI compatible Rust HTTP server which supports HTTP/2, TLS and WebSockets.
---
Uvicorn is BSD licensed code.
Designed & crafted with care.
— π¦ —
[asgi]: https://asgi.readthedocs.io/en/latest/
[daphne]: https://github.com/django/daphne
[hypercorn]: https://github.com/pgjones/hypercorn
[trio]: https://trio.readthedocs.io
[mangum]: https://github.com/jordaneremieff/mangum
[granian]: https://github.com/emmett-framework/granian
uvicorn-0.53.0/docs/ 0000775 0000000 0000000 00000000000 15251720332 0014214 5 ustar 00root root 0000000 0000000 uvicorn-0.53.0/docs/CNAME 0000664 0000000 0000000 00000000020 15251720332 0014752 0 ustar 00root root 0000000 0000000 www.uvicorn.org
uvicorn-0.53.0/docs/concepts/ 0000775 0000000 0000000 00000000000 15251720332 0016032 5 ustar 00root root 0000000 0000000 uvicorn-0.53.0/docs/concepts/asgi.md 0000664 0000000 0000000 00000017730 15251720332 0017307 0 ustar 00root root 0000000 0000000 ## ASGI
**Uvicorn** uses the [ASGI specification](https://asgi.readthedocs.io/en/latest/) for interacting with an application.
The application should expose an async callable which takes three arguments:
* `scope` - A dictionary containing information about the incoming connection.
* `receive` - A channel on which to receive incoming messages from the server.
* `send` - A channel on which to send outgoing messages to the server.
Two common patterns you might use are either function-based applications:
```python
async def app(scope, receive, send):
assert scope['type'] == 'http'
...
```
Or instance-based applications:
```python
class App:
async def __call__(self, scope, receive, send):
assert scope['type'] == 'http'
...
app = App()
```
It's good practice for applications to raise an exception on scope types
that they do not handle.
The content of the `scope` argument, and the messages expected by `receive` and `send` depend on the protocol being used.
The format for HTTP messages is described in the [ASGI HTTP Message format](https://asgi.readthedocs.io/en/latest/specs/www.html).
### HTTP Scope
An incoming HTTP request might have a connection `scope` like this:
```python
{
'type': 'http',
'scheme': 'http',
'root_path': '',
'server': ('127.0.0.1', 8000),
'http_version': '1.1',
'method': 'GET',
'path': '/',
'headers': [
(b'host', b'127.0.0.1:8000'),
(b'user-agent', b'curl/7.51.0'),
(b'accept', b'*/*')
]
}
```
### HTTP Messages
The instance coroutine communicates back to the server by sending messages to the `send` coroutine.
```python
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
[b'content-type', b'text/plain'],
]
})
await send({
'type': 'http.response.body',
'body': b'Hello, world!',
})
```
### Requests & responses
Here's an example that displays the method and path used in the incoming request:
```python
async def app(scope, receive, send):
"""
Echo the method and path back in an HTTP response.
"""
assert scope['type'] == 'http'
body = f'Received {scope["method"]} request to {scope["path"]}'
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
[b'content-type', b'text/plain'],
]
})
await send({
'type': 'http.response.body',
'body': body.encode('utf-8'),
})
```
### Reading the request body
You can stream the request body without blocking the asyncio task pool,
by fetching messages from the `receive` coroutine.
```python
async def read_body(receive):
"""
Read and return the entire body from an incoming ASGI message.
"""
body = b''
more_body = True
while more_body:
message = await receive()
body += message.get('body', b'')
more_body = message.get('more_body', False)
return body
async def app(scope, receive, send):
"""
Echo the request body back in an HTTP response.
"""
body = await read_body(receive)
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
(b'content-type', b'text/plain'),
(b'content-length', str(len(body)).encode())
]
})
await send({
'type': 'http.response.body',
'body': body,
})
```
### Streaming responses
You can stream responses by sending multiple `http.response.body` messages to
the `send` coroutine.
```python
import asyncio
async def app(scope, receive, send):
"""
Send a slowly streaming HTTP response back to the client.
"""
await send({
'type': 'http.response.start',
'status': 200,
'headers': [
[b'content-type', b'text/plain'],
]
})
for chunk in [b'Hello', b', ', b'world!']:
await send({
'type': 'http.response.body',
'body': chunk,
'more_body': True
})
await asyncio.sleep(1)
await send({
'type': 'http.response.body',
'body': b'',
})
```
---
## Why ASGI?
Most well established Python Web frameworks started out as WSGI-based frameworks.
WSGI applications are a single, synchronous callable that takes a request and returns a response.
This doesnβt allow for long-lived connections, like you get with long-poll HTTP or WebSocket connections,
which WSGI doesn't support well.
Having an async concurrency model also allows for options such as lightweight background tasks,
and can be less of a limiting factor for endpoints that have long periods being blocked on network
I/O such as dealing with slow HTTP requests.
---
## Alternative ASGI servers
A strength of the ASGI protocol is that it decouples the server implementation
from the application framework. This allows for an ecosystem of interoperating
webservers and application frameworks.
### Daphne
The first ASGI server implementation, originally developed to power Django Channels, is
[the Daphne webserver](https://github.com/django/daphne).
It is run widely in production, and supports HTTP/1.1, HTTP/2, and WebSockets.
Any of the example applications given here can equally well be run using `daphne` instead.
```shell
pip install daphne
daphne app:App
```
### Hypercorn
[Hypercorn](https://github.com/pgjones/hypercorn) was initially part of the Quart web framework,
before being separated out into a standalone ASGI server.
Hypercorn supports HTTP/1.1, HTTP/2, HTTP/3 and WebSockets.
```shell
pip install hypercorn
hypercorn app:App
```
---
## ASGI frameworks
You can use Uvicorn, Daphne, or Hypercorn to run any ASGI framework.
For small services you can also write ASGI applications directly.
### Starlette
[Starlette](https://github.com/Kludex/starlette) is a lightweight ASGI framework/toolkit.
It is ideal for building high performance asyncio services, and supports both HTTP and WebSockets.
### Django Channels
The ASGI specification was originally designed for use with [Django Channels](https://channels.readthedocs.io/en/latest/).
Channels is a little different to other ASGI frameworks in that it provides
an asynchronous frontend onto a threaded-framework backend. It allows Django
to support WebSockets, background tasks, and long-running connections,
with application code still running in a standard threaded context.
### Quart
[Quart](https://pgjones.gitlab.io/quart/) is a Flask-like ASGI web framework.
### FastAPI
[**FastAPI**](https://github.com/tiangolo/fastapi) is an API framework based on **Starlette** and **Pydantic**, heavily inspired by previous server versions of **APIStar**.
You write your API function parameters with Python 3.6+ type declarations and get automatic data conversion, data validation, OpenAPI schemas (with JSON Schemas) and interactive API documentation UIs.
### BlackSheep
[BlackSheep](https://www.neoteroi.dev/blacksheep/) is a web framework based on ASGI, inspired by Flask and ASP.NET Core.
Its most distinctive features are built-in support for dependency injection, automatic binding of parameters by request handler's type annotations, and automatic generation of OpenAPI documentation and Swagger UI.
### Falcon
[Falcon](https://falconframework.org) is a minimalist REST and app backend framework for Python, with a focus on reliability, correctness, and performance at scale.
### Muffin
[Muffin](https://github.com/klen/muffin) is a fast, lightweight and asynchronous ASGI web-framework for Python 3.
### Litestar
[Litestar](https://litestar.dev) is a powerful, lightweight and flexible ASGI framework.
It includes everything that's needed to build modern APIs - from data serialization and validation to websockets, ORM integration, session management, authentication and more.
### Panther
[Panther](https://PantherPy.github.io/) is a fast & friendly web framework for building async APIs with Python 3.10+.
It has built-in Document-oriented Database, Caching System, Authentication and Permission Classes, Visual API Monitoring and also supports Websocket, Throttling, Middlewares.
uvicorn-0.53.0/docs/concepts/event-loop.md 0000664 0000000 0000000 00000005753 15251720332 0020456 0 ustar 00root root 0000000 0000000 # Event Loop
Uvicorn provides three event loop implementations that you can choose from using the [`--loop`](../settings.md#implementation) option:
```bash
uvicorn main:app --loop
```
By default, Uvicorn uses `--loop auto`, which automatically selects:
1. **uvloop** - If [uvloop](https://github.com/MagicStack/uvloop) is installed, Uvicorn will use it for maximum performance
2. **asyncio** - If uvloop is not available, Uvicorn falls back to Python's built-in asyncio event loop
Since `uvloop` is not compatible with Windows or PyPy, it is not available on these platforms.
On Windows, the asyncio implementation uses the standard [`ProactorEventLoop`][asyncio.ProactorEventLoop] in single-process mode.
When running with `--reload` or multiple workers, it uses [`SelectorEventLoop`][asyncio.SelectorEventLoop] instead.
??? info "Why can `ProactorEventLoop` fail with multiple processes on Windows?"
If you want to know more about it, you can read the issue [#cpython/122240](https://github.com/python/cpython/issues/122240).
## zuvloop
[`zuvloop`](https://zuvloop.marcelotryle.com/) is an `asyncio` event loop powered by libuv and written in Zig.
It requires CPython 3.14 or newer.
Install `zuvloop` separately:
=== "pip"
```bash
pip install zuvloop
```
=== "uv"
```bash
uv add zuvloop
```
Then select it explicitly:
```bash
uvicorn main:app --loop zuvloop
```
Uvicorn does not include `zuvloop` in the `standard` extra or select it with `--loop auto`.
## Custom Event Loop
You can use custom event loop implementations by specifying a module path and function name using the colon notation:
```bash
uvicorn main:app --loop :
```
The function should return a callable that creates a new event loop instance.
### rloop
[rloop](https://github.com/gi0baro/rloop) is an experimental AsyncIO event loop implemented in Rust on top of the [mio](https://github.com/tokio-rs/mio) crate. It aims to provide high performance through Rust's systems programming capabilities.
You can install it with:
=== "pip"
```bash
pip install rloop
```
=== "uv"
```bash
uv add rloop
```
You can run `uvicorn` with `rloop` with the following command:
```bash
uvicorn main:app --loop rloop:new_event_loop
```
!!! warning "Experimental"
rloop is currently **experimental** and **not suited for production usage**. It is only available on **Unix systems**.
### Winloop
[Winloop](https://github.com/Vizonex/Winloop) is an alternative library that brings uvloop-like performance to Windows. Since uvloop is based on libuv and doesn't support Windows, Winloop provides a Windows-compatible implementation with significant performance improvements over the standard Windows event loop policies.
You can install it with:
=== "pip"
```bash
pip install winloop
```
=== "uv"
```bash
uv add winloop
```
You can run `uvicorn` with `Winloop` with the following command:
```bash
uvicorn main:app --loop winloop:new_event_loop
```
uvicorn-0.53.0/docs/concepts/http-protocols.md 0000664 0000000 0000000 00000003565 15251720332 0021366 0 ustar 00root root 0000000 0000000 # HTTP Protocols
Uvicorn provides several HTTP protocol implementations that you can choose from using the [`--http`](../settings.md#implementation) option:
```bash
uvicorn main:app --http
```
By default, Uvicorn uses `--http auto`, which automatically selects:
1. **httptools** - If [httptools](https://github.com/MagicStack/httptools) is installed, Uvicorn will use it for maximum performance
2. **h11** - If httptools is not available, Uvicorn falls back to [h11](https://github.com/python-hyper/h11)
## h11
[h11](https://github.com/python-hyper/h11) is a pure Python HTTP/1.1 implementation. It is a required dependency of Uvicorn, so it is always available, and it is the only implementation compatible with PyPy.
## httptools
[httptools](https://github.com/MagicStack/httptools) is a Python binding for the Node.js HTTP parser. It is installed as part of the `uvicorn[standard]` optional extras, and provides greater performance than h11, but is not compatible with PyPy.
## zttp
[zttp](https://zttp.marcelotryle.com/) is a sans-IO HTTP parser for Python with a core written in Zig. Prebuilt wheels are available for CPython on Linux, macOS and Windows.
You can install it with:
=== "pip"
```bash
pip install zttp
```
=== "uv"
```bash
uv add zttp
```
You can run `uvicorn` with `zttp` with the following command:
```bash
uvicorn main:app --http zttp
```
The `zttp` implementation serves HTTP/1.1 by default. This keeps experimental HTTP/2
support opt-in. You can enable HTTP/2 negotiation with:
```bash
uvicorn main:app --http zttp --http2
```
See the [HTTP/2 documentation](http2.md) for details.
!!! warning "Experimental"
zttp support is currently **experimental** and **not suited for production usage**. If you try it out, please report any issues or feedback on the [issue tracker](https://github.com/Kludex/uvicorn/issues).
uvicorn-0.53.0/docs/concepts/http2.md 0000664 0000000 0000000 00000017361 15251720332 0017425 0 ustar 00root root 0000000 0000000 **Uvicorn** has experimental support for HTTP/2, built on the [`zttp`](https://zttp.marcelotryle.com/)
parser.
!!! warning "Experimental Feature"
HTTP/2 support is currently **experimental** and is **not enabled by default**.
## Overview
HTTP/2 introduces several key features:
- **Multiplexing**: Multiple requests and responses can be sent simultaneously over a single TCP connection
- **Header compression**: HTTP headers are compressed using HPACK, reducing overhead
- **Binary protocol**: More efficient parsing compared to HTTP/1.1's text-based format
## Enabling HTTP/2
HTTP/2 support requires the `zttp` package:
```bash
pip install zttp
```
To enable it, select the `zttp` HTTP implementation and pass `--http2`:
=== "Command Line"
```bash
uvicorn main:app --http zttp --http2
```
=== "Programmatic"
```python
import uvicorn
uvicorn.run("main:app", http="zttp", http2=True)
```
The `--http2` option makes `zttp` serve both HTTP versions. Each connection is dispatched
to HTTP/1.1 or HTTP/2 depending on what the client speaks. Without this option, `zttp`
serves HTTP/1.1 only. This default keeps experimental HTTP/2 support opt-in.
## Connection Methods
### h2: HTTP/2 over TLS (Recommended)
When using HTTPS, HTTP/2 is negotiated via **ALPN** (Application-Layer Protocol Negotiation)
during the TLS handshake. This is the most common way to use HTTP/2, and the only one web
browsers support.
```mermaid
sequenceDiagram
participant Client
participant Server
Note over Client,Server: TLS Handshake with ALPN
Client->>Server: ClientHello
Note right of Client: ALPN: h2, http/1.1
Server->>Client: ServerHello
Note right of Server: ALPN: h2
Note over Client,Server: TLS Handshake Complete
Client->>Server: Connection Preface
Note right of Client: PRI * HTTP/2.0
Client->>Server: Client SETTINGS Frame
Server->>Client: Server SETTINGS Frame
Note over Client,Server: HTTP/2 Connection Established
Client->>Server: HEADERS (Stream 1)
Server->>Client: HEADERS + DATA (Stream 1)
```
For testing it locally, you can generate a self-signed certificate:
```bash
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"
```
Then create a simple ASGI application:
```python title="main.py"
async def app(scope, receive, send):
await send({"type": "http.response.start", "status": 200, "headers": []})
await send({"type": "http.response.body", "body": b"ok"})
```
Run Uvicorn with `--http zttp --http2` and the SSL certificate files:
```bash
uvicorn main:app --http zttp --http2 --ssl-keyfile key.pem --ssl-certfile cert.pem
```
You can test the connection using curl (`-k` skips certificate verification for self-signed certs):
```bash
curl -v --http2 -k https://localhost:8000/
```
### h2c: HTTP/2 Cleartext with Prior Knowledge
On cleartext connections, Uvicorn accepts clients that speak HTTP/2 directly - known as
"prior knowledge" h2c. The client opens the connection with the HTTP/2 preface instead of an
HTTP/1.1 request, and Uvicorn switches protocols on the spot.
```mermaid
sequenceDiagram
participant Client
participant Server
Note over Client,Server: Cleartext TCP Connection
Client->>Server: Connection Preface
Note right of Client: PRI * HTTP/2.0
Client->>Server: Client SETTINGS Frame
Server->>Client: Server SETTINGS Frame
Note over Client,Server: HTTP/2 Connection Established
Client->>Server: HEADERS + DATA (Stream 1)
Server->>Client: HEADERS + DATA (Stream 1)
```
Using the same `main.py`:
```bash
uvicorn main:app --http zttp --http2
```
```bash
curl -v --http2-prior-knowledge http://localhost:8000/
```
This is the mechanism proxies use for cleartext HTTP/2 upstreams, so HTTP/2 between a proxy
and Uvicorn works without TLS. How you enable it is proxy-specific: Traefik and Caddy use
`h2c://` upstream URLs, while Envoy configures HTTP/2 on the cluster.
!!! warning "h2c is cleartext"
Prior-knowledge h2c provides no transport encryption or peer authentication. Limit it to
trusted private networks (e.g. the proxy-to-Uvicorn hop); use HTTP/2 over TLS for any
untrusted hop.
!!! warning
The HTTP/1.1 `Upgrade: h2c` mechanism is **not** supported: an upgrade request is served
as plain HTTP/1.1, which RFC 7540 explicitly allows. Browsers do not support h2c at all -
they only speak HTTP/2 over TLS.
## ASGI Scope
When a request comes in over HTTP/2, the ASGI scope has `http_version` set to `"2"`:
```python
async def app(scope, receive, send):
assert scope["type"] == "http"
print(f"HTTP Version: {scope['http_version']}") # "2" for HTTP/2
```
## Using with Reverse Proxies
In production, Uvicorn is typically deployed behind a reverse proxy like Nginx, Caddy, or HAProxy.
The proxy can terminate TLS and serve HTTP/2 to clients, while talking either HTTP/1.1 or
HTTP/2 to Uvicorn.
```mermaid
flowchart LR
Client <-->|HTTP/2 over TLS| Proxy
Proxy <-->|HTTP/1.1 or HTTP/2| Uvicorn
style Client fill:#e1f5fe
style Proxy fill:#fff3e0
style Uvicorn fill:#e8f5e9
```
### Proxy HTTP/2 Upstream Support
**HTTP/2 upstream** refers to the protocol used between the proxy and Uvicorn. While all modern
proxies support HTTP/2 for client connections, support for HTTP/2 to backend servers varies.
**Multiplexing** is HTTP/2's ability to send multiple requests simultaneously over a single TCP
connection. Some proxies support HTTP/2 upstream but open a new connection per request, which
means they don't truly multiplex.
Here's the state of proxy support at the time of writing:
| Proxy | HTTP/2 Upstream | Multiplexing | Enabled by | Documentation |
|-------|-----------------|--------------|------------|---------------|
| **Envoy** | Yes | Yes | HTTP/2 protocol options on the cluster | [Connection Pooling Docs](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/upstream/connection_pooling) |
| **Caddy** | Yes | Yes | `h2c://` upstream (experimental), or `versions 2` in the transport for TLS | [reverse_proxy Docs](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy) |
| **HAProxy** | Yes | Yes | `proto h2` on the server line (cleartext), `ssl alpn h2,http/1.1` (TLS) | [HTTP/2 Docs](https://www.haproxy.com/documentation/hapee/latest/load-balancing/protocols/http-2/) |
| **Traefik** | Yes | Yes | `h2c://` service URL | [ServersTransport Docs](https://doc.traefik.io/traefik/routing/services/) |
| **Apache** | Yes (2.4.19+) | No | `h2://` / `h2c://` in `ProxyPass` (no HTTP/1.1 fallback) | [mod_proxy_http2 Docs](https://httpd.apache.org/docs/current/mod/mod_proxy_http2.html) |
| **Nginx** | Yes (1.29.4+) | No | `proxy_http_version 2;` (requires `ngx_http_v2_module`) | [ngx_http_proxy_module Docs](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version) |
Uvicorn supports both upstream flavors: cleartext prior-knowledge h2c, and HTTP/2 over TLS
via ALPN when the proxy-to-Uvicorn hop uses TLS.
### Performance Considerations
HTTP/2 provides the most benefit when:
- **High latency connections**: Multiplexing reduces round-trip overhead
- **Many concurrent requests**: Multiple streams share a single connection
- **Large headers**: HPACK compression reduces header overhead
For internal, low-latency connections (like proxy to backend), HTTP/1.1 with keepalive often
performs comparably to HTTP/2. It is simpler to configure and debug, which is why it remains the
recommended default for most deployments.
## Current Limitations
The implementation is young, and some protocol features are not complete yet:
- HTTP/2 server push and `Expect: 100-continue` are not supported.
- WebSockets over HTTP/2 (RFC 8441 extended `CONNECT`) are not supported. With `--http zttp --http2`,
WebSocket connections still work - they are served over HTTP/1.1.
uvicorn-0.53.0/docs/concepts/lifespan.md 0000664 0000000 0000000 00000010234 15251720332 0020155 0 ustar 00root root 0000000 0000000 Since Uvicorn is an ASGI server, it supports the
[ASGI lifespan protocol](https://asgi.readthedocs.io/en/latest/specs/lifespan.html).
This allows you to run **startup** and **shutdown** events for your application.
The lifespan protocol is useful for initializing resources that need to be available throughout
the lifetime of the application, such as database connections, caches, or other services.
Keep in mind that the lifespan is executed **only once per application instance**. If you have
multiple workers, each worker will execute the lifespan independently.
## Lifespan Architecture
The lifespan protocol runs as a sibling task alongside your main application, allowing both to execute concurrently.
Let's see how Uvicorn handles the lifespan and main application tasks:
```mermaid
sequenceDiagram
participant Server as Uvicorn Server
participant LifespanTask as Lifespan Task
participant AppTask as Application Task
participant UserApp as User Application
Note over Server: β
Server starts
Server->>+LifespanTask: spawn_task(lifespan_handler)
LifespanTask->>UserApp: {"type": "lifespan.startup"}
Note over UserApp: Initialize databases, caches, etc.
UserApp-->>LifespanTask: {"type": "lifespan.startup.complete"}
LifespanTask->>Server: β
Startup complete
Server->>+AppTask: spawn_task(application_handler)
Note over AppTask: β
Ready for requests
rect rgb(240, 248, 255)
Note over LifespanTask, AppTask: Both tasks running concurrently
par Lifespan maintains state
LifespanTask->>LifespanTask: Keep lifespan connection alive
and Application serves requests
AppTask->>UserApp: HTTP/WebSocket requests
UserApp-->>AppTask: Responses
end
end
Note over Server: Shutdown signal received
Server->>AppTask: Stop accepting new connections
AppTask->>AppTask: Complete pending requests
LifespanTask->>UserApp: {"type": "lifespan.shutdown"}
Note over UserApp: Cleanup databases, caches, etc.
UserApp-->>LifespanTask: {"type": "lifespan.shutdown.complete"}
LifespanTask->>-Server: Lifespan task complete
AppTask->>-Server: Application task complete
Note over Server: β
Server stopped
```
Having the lifespan task run as a sibling task is a deliberate design choice. It could have been implemented as a parent task that spawns the
application task. This decision has the implication that if you create a [`ContextVar`][contextvars.ContextVar]
in the lifespan task, it will not be available in the application task.
## Usage
Let's see an example of a minimal (but complete) ASGI application that implements the lifespan protocol:
```python title="ASGI application with lifespan" hl_lines="3-11"
async def app(scope, receive, send):
if scope['type'] == 'lifespan':
while True:
message = await receive()
if message['type'] == 'lifespan.startup':
print("Application is starting up...")
await send({'type': 'lifespan.startup.complete'})
elif message['type'] == 'lifespan.shutdown':
print("Application is shutting down...")
await send({'type': 'lifespan.shutdown.complete'})
return
elif scope['type'] == 'http':
await send({
'type': 'http.response.start',
'status': 200,
'headers': [(b'content-type', b'text/plain')],
})
await send({'type': 'http.response.body', 'body': b'Hello, World!'})
else:
raise RuntimeError("This server doesn't support WebSocket.")
```
You can run the above application with `uvicorn main:app`. Then you'll see the print statements when the
application starts. You can also try to send some HTTP requests to it, and it will respond with "Hello, World!".
And if you stop the server (`CTRL + C`), it will print `"Application is shutting down..."`.
## Disabling Lifespan
If you want to disable the lifespan protocol, you can do so by setting the `lifespan` option to `off` when running Uvicorn:
```bash
uvicorn main:app --lifespan off
```
By default, Uvicorn will automatically enable the lifespan protocol if the application supports it.
uvicorn-0.53.0/docs/concepts/logging.md 0000664 0000000 0000000 00000021024 15251720332 0020001 0 ustar 00root root 0000000 0000000 Uvicorn uses Python's built-in [`logging`](https://docs.python.org/3/library/logging.html)
module, and provides three loggers out of the box:
| Logger name | Purpose |
|------------------|----------------------------------------------------|
| `uvicorn` | Parent logger (rarely used directly) |
| `uvicorn.error` | Server-level messages (startup, shutdown, errors) |
| `uvicorn.access` | Per-request access log lines |
!!! note
Despite its name, `uvicorn.error` is **not** limited to error messages.
It is the general-purpose server logger, similar to how Gunicorn names its
main logger. See [#562](https://github.com/encode/uvicorn/issues/562) for
background.
## Default Configuration
By default, Uvicorn applies the following
[`dictConfig()`](https://docs.python.org/3/library/logging.config.html#logging.config.dictConfig)
configuration:
```python
LOGGING_CONFIG = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"default": {
"()": "uvicorn.logging.DefaultFormatter",
"fmt": "%(levelprefix)s %(message)s",
"use_colors": None,
},
"access": {
"()": "uvicorn.logging.AccessFormatter",
"fmt": '%(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s',
},
},
"handlers": {
"default": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr",
},
"access": {
"formatter": "access",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
},
},
"loggers": {
"uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False},
"uvicorn.error": {"level": "INFO"},
"uvicorn.access": {"handlers": ["access"], "level": "INFO", "propagate": False},
},
}
```
## Custom Logging Configuration
You can supply a custom logging configuration file with the `--log-config`
option (or `log_config` when calling `uvicorn.run()`).
Uvicorn supports three file formats:
| Extension | Loader | Notes |
|----------------|------------------------------|---------------------------------------------|
| `.json` | `logging.config.dictConfig` | Standard JSON `dictConfig` schema. |
| `.yaml`/`.yml` | `logging.config.dictConfig` | Requires **PyYAML** (`uvicorn[standard]`). |
| Any other | `logging.config.fileConfig` | Classic INI-style format. |
### YAML Example
Create a file named `log_config.yaml`:
```yaml
version: 1
disable_existing_loggers: false
formatters:
default:
"()": uvicorn.logging.DefaultFormatter
fmt: "%(asctime)s - %(levelprefix)s %(message)s"
datefmt: "%Y-%m-%d %H:%M:%S"
use_colors: null
access:
"()": uvicorn.logging.AccessFormatter
fmt: '%(asctime)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s'
datefmt: "%Y-%m-%d %H:%M:%S"
handlers:
default:
formatter: default
class: logging.StreamHandler
stream: ext://sys.stderr
access:
formatter: access
class: logging.StreamHandler
stream: ext://sys.stdout
loggers:
uvicorn:
handlers:
- default
level: INFO
propagate: false
uvicorn.error:
level: INFO
uvicorn.access:
handlers:
- access
level: INFO
propagate: false
```
Then pass it to Uvicorn:
=== "CLI"
```bash
uvicorn main:app --log-config log_config.yaml
```
=== "Programmatic"
```python
uvicorn.run("main:app", log_config="log_config.yaml")
```
### JSON Example
Create a file named `log_config.json`:
```json
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"default": {
"()": "uvicorn.logging.DefaultFormatter",
"fmt": "%(asctime)s - %(levelprefix)s %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
"use_colors": null
},
"access": {
"()": "uvicorn.logging.AccessFormatter",
"fmt": "%(asctime)s - %(levelprefix)s %(client_addr)s - \"%(request_line)s\" %(status_code)s",
"datefmt": "%Y-%m-%d %H:%M:%S"
}
},
"handlers": {
"default": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr"
},
"access": {
"formatter": "access",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
}
},
"loggers": {
"uvicorn": {
"handlers": ["default"],
"level": "INFO",
"propagate": false
},
"uvicorn.error": {
"level": "INFO"
},
"uvicorn.access": {
"handlers": ["access"],
"level": "INFO",
"propagate": false
}
}
}
```
### Programmatic `dictConfig`
You can also pass a dictionary directly when running programmatically:
```python
import uvicorn
log_config = {
"version": 1,
"disable_existing_loggers": False,
"formatters": {
"default": {
"()": "uvicorn.logging.DefaultFormatter",
"fmt": "%(asctime)s - %(levelprefix)s %(message)s",
"datefmt": "%Y-%m-%d %H:%M:%S",
},
"access": {
"()": "uvicorn.logging.AccessFormatter",
"fmt": '%(asctime)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s',
"datefmt": "%Y-%m-%d %H:%M:%S",
},
},
"handlers": {
"default": {
"formatter": "default",
"class": "logging.StreamHandler",
"stream": "ext://sys.stderr",
},
"access": {
"formatter": "access",
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout",
},
},
"loggers": {
"uvicorn": {"handlers": ["default"], "level": "INFO", "propagate": False},
"uvicorn.error": {"level": "INFO"},
"uvicorn.access": {"handlers": ["access"], "level": "INFO", "propagate": False},
},
}
uvicorn.run("main:app", log_config=log_config)
```
## Common Recipes
### Writing Logs to a File
To write Uvicorn's server logs to a file in addition to the console, add a `FileHandler` to the `uvicorn` logger:
```yaml
version: 1
disable_existing_loggers: false
formatters:
default:
"()": uvicorn.logging.DefaultFormatter
fmt: "%(asctime)s - %(levelprefix)s %(message)s"
datefmt: "%Y-%m-%d %H:%M:%S"
use_colors: false
access:
"()": uvicorn.logging.AccessFormatter
fmt: '%(asctime)s - %(levelprefix)s %(client_addr)s - "%(request_line)s" %(status_code)s'
datefmt: "%Y-%m-%d %H:%M:%S"
handlers:
default:
formatter: default
class: logging.StreamHandler
stream: ext://sys.stderr
access:
formatter: access
class: logging.StreamHandler
stream: ext://sys.stdout
file:
formatter: default
class: logging.FileHandler
filename: uvicorn.log
loggers:
uvicorn:
handlers:
- default
- file
level: INFO
propagate: false
uvicorn.error:
level: INFO
uvicorn.access:
handlers:
- access
level: INFO
propagate: false
```
In this example, `uvicorn.access` still writes to stdout only. To write access
logs to the file as well, add `file` to the `uvicorn.access.handlers` list.
### Disabling Access Logs
Use the `--no-access-log` CLI flag, or set `access_log=False` programmatically.
This removes all handlers from `uvicorn.access` without affecting the
`uvicorn.error` logger.
### Disabling Colors
Pass `--no-use-colors` on the command line, or set `use_colors=False`
programmatically. When using a custom `--log-config`, set `use_colors: false`
on each formatter that extends `uvicorn.logging.ColourizedFormatter`.
### Using a Standard Formatter
If you do not need Uvicorn's colorized output, you can use the standard
`logging.Formatter` instead:
```yaml
version: 1
disable_existing_loggers: false
formatters:
default:
format: "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
datefmt: "%Y-%m-%d %H:%M:%S"
handlers:
default:
formatter: default
class: logging.StreamHandler
stream: ext://sys.stderr
loggers:
uvicorn:
handlers:
- default
level: INFO
propagate: false
uvicorn.error:
level: INFO
uvicorn.access:
handlers:
- default
level: INFO
propagate: false
```
!!! warning
When using a standard `logging.Formatter` for the access logger, the
`%(client_addr)s`, `%(request_line)s`, and `%(status_code)s` placeholders
are **not** available. The access log line will be formatted using only the
standard `%(message)s` field.
uvicorn-0.53.0/docs/concepts/websockets.md 0000664 0000000 0000000 00000010234 15251720332 0020525 0 ustar 00root root 0000000 0000000 **Uvicorn** supports the WebSocket protocol as defined in [RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455).
## Upgrade Process
The WebSocket protocol starts as an HTTP connection that gets "upgraded" to a WebSocket connection
through a handshake process. Here's how it works:
```mermaid
sequenceDiagram
participant Client
participant Server
participant ASGI App
Note over Client,ASGI App: WebSocket Handshake Process
Client->>Server: HTTP GET Request
Note right of Client: Headers:
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: [key]
Sec-WebSocket-Version: 13
Server->>ASGI App: websocket.connect event
Note right of Server: Scope type: "websocket"
alt Connection Accepted
ASGI App->>Server: {"type": "websocket.accept"}
Server->>Client: HTTP 101 Switching Protocols
Note right of Server: Headers:
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: [hash]
Note over Client,ASGI App: WebSocket Connection Established
loop Message Exchange
Client->>Server: WebSocket Frame
Server->>ASGI App: websocket.receive event
ASGI App->>Server: {"type": "websocket.send", "text": "..."}
Server->>Client: WebSocket Frame
end
alt Client Closes
Client->>Server: Close Frame
Server->>ASGI App: websocket.disconnect event
else Server Closes
ASGI App->>Server: {"type": "websocket.close"}
Server->>Client: Close Frame
end
else Connection Rejected
ASGI App->>Server: {"type": "websocket.http.response.start", "status": 403}
Server->>Client: HTTP 403 Forbidden
end
```
1. **Initial HTTP Request**: The client sends a regular HTTP GET request with special headers indicating it wants to upgrade to WebSocket:
- `Upgrade: websocket`
- `Connection: Upgrade`
- `Sec-WebSocket-Key`: A base64-encoded random key
- `Sec-WebSocket-Version: 13`
2. **Server Processing**: Uvicorn receives the request and creates a WebSocket scope, sending a `websocket.connect` event to the ASGI application.
3. **Application Decision**: The ASGI app decides whether to accept or reject the connection based on authentication, authorization, or other logic.
4. **Handshake Completion**: If accepted, the server responds with HTTP 101 status and the computed `Sec-WebSocket-Accept` header.
5. **Full-Duplex Communication**: Once upgraded, both client and server can send messages at any time using WebSocket frames.
6. **Connection Termination**: Either side can initiate closing the connection with a close frame.
## ASGI WebSocket Events
**Uvicorn** translates WebSocket protocol messages into ASGI events:
- `websocket.connect`: Sent when a client requests a WebSocket upgrade
- `websocket.receive`: Sent when a message is received from the client
- `websocket.disconnect`: Sent when the connection is closed
The ASGI app can respond with:
- `websocket.accept`: Accept the connection upgrade with an optional subprotocol
- `websocket.send`: Send a message to the client
- `websocket.close`: Close the connection with an optional status code
You can read more about it on the [ASGI documentation](https://asgi.readthedocs.io/en/latest/specs/www.html#websocket).
## Protocol Implementations
**Uvicorn** has three implementations of the WebSocket protocol.
### WSProto Protocol
This implementation was the first implemented. It uses the
[`wsproto`](https://python-hyper.org/projects/wsproto/en/stable/) package underneath.
You can choose this protocol by setting the `--ws` option to `wsproto`.
### WebSocket Protocol
This implementation uses the [`websockets`](https://websockets.readthedocs.io/) package as dependency.
By default, if you have `websockets` installed, Uvicorn will use this protocol.
### WebSockets SansIO Protocol
Since `websockets` deprecated the API Uvicorn uses to run the previous protocol, we had to create this new
protocol that uses the `websockets` SansIO API.
You can choose this protocol by setting the `--ws` option to `websockets-sansio`.
!!! note
The SansIO implementation was released in Uvicorn version 0.35.0 in June 2025.
uvicorn-0.53.0/docs/contributing.md 0000664 0000000 0000000 00000013356 15251720332 0017255 0 ustar 00root root 0000000 0000000 # Contributing
Thank you for being interested in contributing to Uvicorn.
There are many ways you can contribute to the project:
- Using Uvicorn on your stack and [reporting bugs/issues you find](https://github.com/Kludex/uvicorn/issues/new)
- [Implementing new features and fixing bugs](https://github.com/Kludex/uvicorn/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
- [Review Pull Requests of others](https://github.com/Kludex/uvicorn/pulls)
- Write documentation
- Participate in discussions
## Reporting Bugs, Issues or Feature Requests
Found something that Uvicorn should support?
Stumbled upon some unexpected behaviour?
Need a missing functionality?
Contributions should generally start out from a previous discussion.
You can reach out someone at the [community chat](https://discord.com/invite/SWU73HffbV)
or at the [github discussions tab](https://github.com/Kludex/uvicorn/discussions).
When creating a new topic in the discussions tab, possible bugs may be raised
as a "Potential Issue" discussion, feature requests may be raised as an
"Ideas" discussion. We can then determine if the discussion needs
to be escalated into an "Issue" or not, or if we'd consider a pull request.
Try to be more descriptive as you can and in case of a bug report,
provide as much information as possible like:
- OS platform
- Python version
- Installed dependencies and versions (`python -m pip freeze`)
- Code snippet
- Error traceback
You should always try to reduce any examples to the *simplest possible case*
that demonstrates the issue.
Some possibly useful tips for narrowing down potential issues...
- Does the issue exist with a specific supervisor like `Multiprocess` or more than one?
- Does the issue exist on asgi, or wsgi, or both?
- Are you running Uvicorn in conjunction with Gunicorn, others, or standalone?
## Development
To start developing Uvicorn create a **fork** of the
[Uvicorn repository](https://github.com/Kludex/uvicorn) on GitHub.
Then clone your fork with the following command replacing `YOUR-USERNAME` with
your GitHub username:
```shell
$ git clone https://github.com/YOUR-USERNAME/uvicorn
```
You can now install the project and its dependencies using:
```shell
$ cd uvicorn
$ scripts/install
```
## Testing and Linting
We use custom shell scripts to automate testing, linting,
and documentation building workflow.
To run the tests, use:
```shell
$ scripts/test
```
Any additional arguments will be passed to `pytest`. See the [pytest documentation](https://docs.pytest.org/en/latest/how-to/usage.html) for more information.
For example, to run a single test script:
```shell
$ scripts/test tests/test_cli.py
```
To run the code auto-formatting:
```shell
$ scripts/lint
```
Lastly, to run code checks separately (they are also run as part of `scripts/test`), run:
```shell
$ scripts/check
```
## Documenting
Documentation pages are located under the `docs/` folder.
To run the documentation site locally (useful for previewing changes), use:
```shell
$ scripts/docs serve
```
## Resolving Build / CI Failures
Once you've submitted your pull request, the test suite will
automatically run, and the results will show up in GitHub.
If the test suite fails, you'll want to click through to the
"Details" link, and try to identify why the test suite failed.
Here are some common ways the test suite can fail:
### Check Job Failed
This job failing means there is either a code formatting issue or type-annotation issue.
You can look at the job output to figure out why it's failed or within a shell run:
```shell
$ scripts/check
```
It may be worth it to run `$ scripts/lint` to attempt auto-formatting the code
and if that job succeeds commit the changes.
### Docs Job Failed
This job failing means the documentation failed to build. This can happen for
a variety of reasons like invalid markdown or missing configuration within `mkdocs.yml`.
### Python 3.X Job Failed
This job failing means the unit tests failed or not all code paths are covered by unit tests.
If tests are failing you will see this message under the coverage report:
`=== 1 failed, 354 passed, 1 skipped, 1 xfailed in 37.08s ===`
If tests succeed but coverage doesn't reach 100%, you will see this
message under the coverage report:
`Coverage failure: total of 98 is less than fail-under=100`
## Releasing
*This section is targeted at Uvicorn maintainers.*
Before releasing a new version, create a pull request that includes:
- **An update to the changelog**:
- We follow the format from [keepachangelog](https://keepachangelog.com/en/1.0.0/).
- [Compare](https://github.com/Kludex/uvicorn/compare/) `main` with the tag of the latest release, and list all entries that are of interest to our users:
- Things that **must** go in the changelog: added, changed, deprecated or removed features, and bug fixes.
- Things that **should not** go in the changelog: changes to documentation, tests or tooling.
- Try sorting entries in descending order of impact / importance.
- Keep it concise and to-the-point. π―
- **A version bump**: see `__init__.py`.
For an example, see [#1107](https://github.com/Kludex/uvicorn/pull/1107).
Once the release PR is merged, create a
[new release](https://github.com/Kludex/uvicorn/releases/new) including:
- Tag version like `0.13.3`.
- Release title `Version 0.13.3`
- Description copied from the changelog.
Once created this release will be automatically uploaded to PyPI.
uvicorn-0.53.0/docs/css/ 0000775 0000000 0000000 00000000000 15251720332 0015004 5 ustar 00root root 0000000 0000000 uvicorn-0.53.0/docs/css/extra.css 0000664 0000000 0000000 00000002226 15251720332 0016643 0 ustar 00root root 0000000 0000000 .md-nav__sponsors {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
margin: 1.2rem 0.4rem 0.6rem;
padding: 0.9rem 0.6rem 0.8rem;
background-color: color-mix(in srgb, var(--md-primary-fg-color) 8%, transparent);
border-radius: 0.4rem;
}
.md-nav__sponsors-title {
margin: 0 0 0.1rem;
font-size: 0.6rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--md-default-fg-color--light);
}
.md-nav__sponsor {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
padding: 0.25rem;
border-radius: 0.2rem;
transition: opacity 0.15s;
}
.md-nav__sponsor:hover {
opacity: 0.75;
}
.md-nav__sponsor img {
max-width: 100%;
max-height: 1.6rem;
object-fit: contain;
}
.md-nav__sponsor-cta {
display: inline-block;
margin-top: 0.15rem;
padding: 0.25rem 0.6rem;
font-size: 0.65rem;
font-weight: 600;
color: var(--md-primary-bg-color);
background-color: var(--md-primary-fg-color);
border-radius: 0.2rem;
text-decoration: none;
transition: opacity 0.15s;
}
.md-nav__sponsor-cta:hover {
opacity: 0.85;
color: var(--md-primary-bg-color);
}
uvicorn-0.53.0/docs/deployment/ 0000775 0000000 0000000 00000000000 15251720332 0016374 5 ustar 00root root 0000000 0000000 uvicorn-0.53.0/docs/deployment/docker.md 0000664 0000000 0000000 00000010645 15251720332 0020173 0 ustar 00root root 0000000 0000000 # Dockerfile
**Docker** is a popular choice for modern application deployment. However, creating a good Dockerfile from scratch can be challenging. This guide provides a **solid foundation** that works well for most Python projects.
While the example below won't fit every use case, it offers an excellent starting point that you can adapt to your specific needs.
## Quickstart
For this example, we'll need to install [`docker`](https://docs.docker.com/get-docker/),
[docker-compose](https://docs.docker.com/compose/install/) and
[`uv`](https://docs.astral.sh/uv/getting-started/installation/).
Then, let's create a new project with `uv`:
```bash
uv init app
```
This will create a new project with a basic structure:
```bash
app/
βββ main.py
βββ pyproject.toml
βββ README.md
```
On `main.py`, let's create a simple ASGI application:
```python title="main.py"
async def app(scope, receive, send):
body = "Hello, world!"
await send(
{
"type": "http.response.start",
"status": 200,
"headers": [
[b"content-type", b"text/plain"],
[b"content-length", len(body)],
],
}
)
await send(
{
"type": "http.response.body",
"body": body.encode("utf-8"),
}
)
```
We need to include `uvicorn` in the dependencies:
```bash
uv add uvicorn
```
This will also create a `uv.lock` file. :sunglasses:
??? tip "What is `uv.lock`?"
`uv.lock` is a `uv` specific lockfile. A lockfile is a file that contains the exact versions of the dependencies
that were installed when the `uv.lock` file was created.
This allows for deterministic builds and consistent deployments.
Just to make sure everything is working, let's run the application:
```bash
uv run uvicorn main:app
```
You should see the following output:
```bash
INFO: Started server process [62727]
INFO: Waiting for application startup.
INFO: ASGI 'lifespan' protocol appears unsupported.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
```
## Dockerfile
We'll create a **cache-aware Dockerfile** that optimizes build times. The key strategy is to install dependencies first, then copy the project files. This approach leverages Docker's caching mechanism to significantly speed up rebuilds.
```dockerfile title="Dockerfile"
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
# Change the working directory to the `app` directory
WORKDIR /app
# Install dependencies
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project
# Copy the project into the image
ADD . /app
# Sync the project
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen
# Run with uvicorn
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
```
A common question is **"how many workers should I run?"**. The image above uses a single Uvicorn worker.
The recommended approach is to let your orchestration system manage the number of deployed containers rather than
relying on the process manager inside the container.
You can read more about this in the
[Decouple applications](https://docs.docker.com/build/building/best-practices/#decouple-applications) section
of the Docker documentation.
!!! warning "For production, create a non-root user!"
When running in production, you should create a non-root user and run the container as that user.
To make sure it works, let's build the image and run it:
```bash
docker build -t my-app .
docker run -p 8000:8000 my-app
```
For more information on using uv with Docker, refer to the
[official uv Docker integration guide](https://docs.astral.sh/uv/guides/integration/docker/).
## Docker Compose
When running in development, it's often useful to have a way to hot-reload the application when code changes.
Let's create a `docker-compose.yml` file to run the application:
```yaml title="docker-compose.yml"
services:
backend:
build: .
ports:
- "8000:8000"
environment:
- UVICORN_RELOAD=true
volumes:
- .:/app
tty: true
```
You can run the application with `docker compose up` and it will automatically rebuild the image when code changes.
Now you have a fully working development environment! :tada:
uvicorn-0.53.0/docs/deployment/index.md 0000664 0000000 0000000 00000031774 15251720332 0020041 0 ustar 00root root 0000000 0000000 Server deployment is a complex area, that will depend on what kind of service you're deploying Uvicorn onto.
As a general rule, you probably want to:
* Run `uvicorn --reload` from the command line for local development.
* Run `gunicorn -k uvicorn.workers.UvicornWorker` for production.
* Additionally run behind Nginx for self-hosted deployments.
* Finally, run everything behind a CDN for caching support, and serious DDOS protection.
## Running from the command line
Typically you'll run `uvicorn` from the command line.
```bash
$ uvicorn main:app --reload --port 5000
```
The ASGI application should be specified in the form `path.to.module:instance.path`.
When running locally, use `--reload` to turn on auto-reloading.
The `--reload` and `--workers` arguments are **mutually exclusive**.
To see the complete set of available options, use `uvicorn --help`.
See the [settings documentation](../settings.md) for more details on the supported options for running uvicorn.
## Running programmatically
To run directly from within a Python program, you should use `uvicorn.run(app, **config)`. For example:
```py title="main.py"
import uvicorn
class App:
...
app = App()
if __name__ == "__main__":
uvicorn.run("main:app", host="127.0.0.1", port=5000, log_level="info")
```
The set of configuration options is the same as for the command line tool.
Note that the application instance itself *can* be passed instead of the app
import string.
```python
uvicorn.run(app, host="127.0.0.1", port=5000, log_level="info")
```
However, this style only works if you are not using multiprocessing (`workers=NUM`)
or reloading (`reload=True`), so we recommend using the import string style.
Also note that in this case, you should put `uvicorn.run` into `if __name__ == '__main__'` clause in the main module.
!!! note
The `reload` and `workers` parameters are **mutually exclusive**.
## Using a process manager
Running Uvicorn using a process manager ensures that you can run multiple processes in a resilient manner, and allows you to perform server upgrades without dropping requests.
A process manager will handle the socket setup, start-up multiple server processes, monitor process aliveness, and listen for signals to provide for processes restarts, shutdowns, or dialing up and down the number of running processes.
### Built-in
Uvicorn includes a `--workers` option that allows you to run multiple worker processes.
```bash
$ uvicorn main:app --workers 4
```
Unlike gunicorn, uvicorn does not use pre-fork, but uses [`spawn`](https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods), which allows uvicorn's multiprocess manager to still work well on Windows.
The default process manager monitors the status of child processes and automatically restarts child processes that die unexpectedly. Not only that, it will also monitor the status of the child process through the pipeline. When the child process is accidentally stuck, the corresponding child process will be killed through an unstoppable system signal or interface.
You can also manage child processes by sending specific signals to the main process. (Not supported on Windows.)
- `SIGHUP`: Gracefully restart the workers one at a time with no dropped requests. Fresh workers pick up new code on disk.
- `SIGTTIN`: Increase the number of worker processes by one.
- `SIGTTOU`: Decrease the number of worker processes by one.
### Gunicorn
!!! warning
The `uvicorn.workers` module is deprecated and will be removed in a future release.
You should use the [`uvicorn-worker`](https://github.com/Kludex/uvicorn-worker) package instead.
```bash
python -m pip install uvicorn-worker
```
Gunicorn is probably the simplest way to run and manage Uvicorn in a production setting. Uvicorn includes a gunicorn worker class that means you can get set up with very little configuration.
The following will start Gunicorn with four worker processes:
`gunicorn -w 4 -k uvicorn.workers.UvicornWorker`
The `UvicornWorker` implementation uses the `uvloop` and `httptools` implementations. To run under PyPy you'll want to use pure-python implementation instead. You can do this by using the `UvicornH11Worker` class.
`gunicorn -w 4 -k uvicorn.workers.UvicornH11Worker`
Gunicorn provides a different set of configuration options to Uvicorn, so some options such as `--limit-concurrency` are not yet supported when running with Gunicorn.
If you need to pass uvicorn's config arguments to gunicorn workers then you'll have to subclass `UvicornWorker`:
```python
from uvicorn.workers import UvicornWorker
class MyUvicornWorker(UvicornWorker):
CONFIG_KWARGS = {"loop": "asyncio", "http": "h11", "lifespan": "off"}
```
### Supervisor
To use `supervisor` as a process manager you should either:
* Hand over the socket to uvicorn using its file descriptor, which supervisor always makes available as `0`, and which must be set in the `fcgi-program` section.
* Or use a UNIX domain socket for each `uvicorn` process.
A simple supervisor configuration might look something like this:
```ini title="supervisord.conf"
[supervisord]
[fcgi-program:uvicorn]
socket=tcp://localhost:8000
command=venv/bin/uvicorn --fd 0 main:App
numprocs=4
process_name=uvicorn-%(process_num)d
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
```
Then run with `supervisord -n`.
## Running behind Nginx
Using Nginx as a proxy in front of your Uvicorn processes may not be necessary, but is recommended for additional resilience. Nginx can deal with serving your static media and buffering slow requests, leaving your application servers free from load as much as possible.
In managed environments such as `Heroku`, you won't typically need to configure Nginx, as your server processes will already be running behind load balancing proxies.
The recommended configuration for proxying from Nginx is to use a UNIX domain socket between Nginx and whatever the process manager that is being used to run Uvicorn. If using Uvicorn directly you can bind it to a UNIX domain socket using `uvicorn --uds /path/to/socket.sock <...>`.
When running your application behind one or more proxies you will want to make sure that each proxy sets appropriate headers to ensure that your application can properly determine the client address of the incoming connection, and if the connection was over `http` or `https`. For more information see [Proxies and Forwarded Headers](#proxies-and-forwarded-headers) below.
Here's how a simple Nginx configuration might look. This example includes setting proxy headers, and using a UNIX domain socket to communicate with the application server.
It also includes some basic configuration to forward websocket connections.
For more info on this, check [Nginx recommendations](https://nginx.org/en/docs/http/websocket.html).
```conf
http {
server {
listen 80;
client_max_body_size 4G;
server_name example.com;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://uvicorn;
}
location /static {
# path for static files
root /path/to/app/static;
}
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream uvicorn {
server unix:/tmp/uvicorn.sock;
}
}
```
Uvicorn's `--proxy-headers` behavior may not be sufficient for more complex proxy configurations that use different combinations of headers, or where the application is running behind more than one intermediary proxying service.
In those cases, you might want to use an ASGI middleware to set the `client` and `scheme` dependent on the request headers.
## Running behind a CDN
Running behind a content delivery network, such as Cloudflare or Cloud Front, provides a serious layer of protection against DDoS attacks. Your service will be running behind huge clusters of proxies and load balancers that are designed for handling huge amounts of traffic, and have capabilities for detecting and closing off connections from DDoS attacks.
Proper usage of cache control headers can mean that a CDN is able to serve large amounts of data without always having to forward the request on to your server.
Content Delivery Networks can also be a low-effort way to provide HTTPS termination.
## Running with HTTPS
To run uvicorn with https, a certificate and a private key are required.
The recommended way to get them is using [Let's Encrypt](https://letsencrypt.org/).
For local development with https, it's possible to use [mkcert](https://github.com/FiloSottile/mkcert)
to generate a valid certificate and private key.
```bash
$ uvicorn main:app --port 5000 --ssl-keyfile=./key.pem --ssl-certfile=./cert.pem
```
### Running gunicorn worker
It's also possible to use certificates with uvicorn's worker for gunicorn.
```bash
$ gunicorn --keyfile=./key.pem --certfile=./cert.pem -k uvicorn.workers.UvicornWorker main:app
```
### Customizing the SSL context
For TLS scenarios that the `--ssl-*` flags don't cover (e.g., mutual TLS, custom `SSLContext.options`, bumping `minimum_version`, loading certificates from memory), pass an `ssl_context_factory` to `uvicorn.run()` or `Config`.
The factory receives the `Config` instance and a `default_ssl_context_factory` callable that builds the standard context from the `ssl_*` settings on `Config`. Use it to start from uvicorn's default and mutate it, or ignore it and build your own context from scratch - the `ssl_*` settings are only consumed by the default factory, so if you don't call it they're effectively unused.
```python
import ssl
from collections.abc import Callable
import uvicorn
from uvicorn.config import Config
def ssl_context_factory(config: Config, default_ssl_context_factory: Callable[[], ssl.SSLContext]) -> ssl.SSLContext:
context = default_ssl_context_factory()
context.minimum_version = ssl.TLSVersion.TLSv1_3
return context
uvicorn.run(
"main:app",
ssl_keyfile="key.pem",
ssl_certfile="cert.pem",
ssl_context_factory=ssl_context_factory,
)
```
The factory is called inside each worker process, so it works with `--reload` and `--workers > 1`. The factory itself must be picklable in those modes (a top-level function is fine; lambdas and local closures are not). The `ssl_*` settings on `Config` are only consumed by `default_ssl_context_factory()`; if you build the context yourself without calling it, those settings are ignored.
## Proxies and Forwarded Headers
When running an application behind one or more proxies, certain information about the request is lost.
To avoid this most proxies will add headers containing this information for downstream servers to read.
Uvicorn currently supports the following headers:
- `X-Forwarded-For` ([MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For))
- `X-Forwarded-Proto`([MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto))
Uvicorn can use these headers to correctly set the client and protocol in the request.
However as anyone can set these headers you must configure which "clients" you will trust to have set them correctly.
Uvicorn can be configured to trust IP Addresses (e.g. `127.0.0.1`), IP Networks (e.g. `10.100.0.0/16`),
or Literals (e.g. `/path/to/socket.sock`). When running from CLI these are configured using `--forwarded-allow-ips`.
!!! Warning "Only trust clients you can actually trust!"
Incorrectly trusting other clients can lead to malicious actors spoofing their apparent client address to your application.
A proxy chain may send a header once per hop rather than as a single comma-separated value. Repeated `X-Forwarded-For` fields are combined in order (as the equivalent comma-separated list, [RFC 9110, 5.3](https://www.rfc-editor.org/rfc/rfc9110#section-5.3)), while for `X-Forwarded-Proto` the last field is used.
For more information, check [`ProxyHeadersMiddleware`](https://github.com/Kludex/uvicorn/blob/main/uvicorn/middleware/proxy_headers.py).
### Client Port
Currently if the `ProxyHeadersMiddleware` is able to retrieve a trusted client value then the client's port will be set to `0`.
This is because port information is lost when using these headers.
### UNIX Domain Sockets (UDS)
Although it is common for UNIX Domain Sockets to be used for communicating between various HTTP servers, they can mess with some of the expected received values as they will be various non-address strings or missing values.
For example:
- when NGINX itself is running behind a UDS it will add the literal `unix:` as the client in the `X-Forwarded-For` header.
- When Uvicorn is running behind a UDS the initial client will be `None`.
### Trust Everything
Rather than specifying what to trust, you can instruct Uvicorn to trust all clients using the literal `"*"`.
You should only set this when you know you can trust all values within the forwarded headers (e.g. because
your proxies remove the existing headers before setting their own).
uvicorn-0.53.0/docs/img/ 0000775 0000000 0000000 00000000000 15251720332 0014770 5 ustar 00root root 0000000 0000000 uvicorn-0.53.0/docs/img/fastapi-logo.png 0000664 0000000 0000000 00000046603 15251720332 0020074 0 ustar 00root root 0000000 0000000 PNG
IHDR q ν[Ϊ@ pHYs Ϋ ΫΝ¬ωC tEXtSoftware www.inkscape.orgξ< IDATxμέyuέ�ρΧη:3
βΎη�hξνYyWΆθ]RΆh(:0Λ`Λ4w)sF3VIv«©ύά2½Υ\rIMMsO@Αs�ΟοM
αϊΉΞ9ΧϋωxΓηϋf80ηϊ|7swDDDDDDD€ΆEiςS@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ Ι 5 DDDDDDD2@
P@DDDDDD$Τ ΙΊ΄dsΞάάΚρh[mpίccάGβΐlγΎφύχΐ1Vΰώ2ΨKΰ/cΌHlOρOάψοήΤ²8ίH2ζξigY'vΞ;α~0ξ {{wTxγ>άξ oχΣ_*ο""""""Ι© UΓΞνΨ|? φ~`TΪΐxη&’ά΅Όn7ϊδΙKΣN%"""""ςvj HE³9ω=ΘΩΈμv5σ°[ΐ/#ΧsΉ7Ά.J;¨ Θζ΅9₯ξ±ΐQ`»₯'"ζ7ύξΛ}lkwΪDDDDD$»Τ ``ςΒ9γΣΤΪΖΛ8Νυ¦©O;d *=»ΊecΙΐξiη)?wn ?¦iW;θ/ 5 $6·m(₯Α ϊ6°IΪyRr7π?ή4νiΪ§(kkΜ¨ρΰSΐ6K;OeπΫ1~ΰZnH;Hv~Ηϊ¬GηS²
l8ξΓίό6[―χψ`a½�ΝΏ�ΐmζoά²΄¬|9ΖK8pω"<·/½@έΖOycγΚώ-T5 dΐΨΣ?NύΈΚφ+#»¨τm?ύ‘΄dςί}σ
Ό;οσζ_§CDδνμάΣ7eeέ€DErΕxγ©/T³μμ-¨w%f`;νΐΆΖΨg$0bΔ"ππ8ζ�ΐ£Η°ψ―ξφγ¦Ύb�²³�φ�Ζ£ρΈΫ[~&TΪ:hM*Ν±3QτS’άΗ�ΞRΩόγΔΡVh?₯u?τΙ�ωΧHΞovͺ`v1 TΉS0¨F©.¦ TύlώόΪcΆ?ΞΐξδΨΫ~AεΜ+Φ;;β8ΈAΗVΘ?άρb»Ι§>nΤΐbϋ$ζγΥ0[ό H©8j HΩX[[£κ'Eί�ΞS%}aΕ/ZgϋDonΉ.ν@""²vl^[Kξΰ§άv,±ΩmPΧΠΌ·³Ξ:έO9eIςZΥΝΊςM8ΐz`τpDμμs,ζX!�$p5pέoςI§Q€ΌΤ ²°sς{3ͺαηΐig©NΆ-ΖΦΩ~!uv7N{%νD""�ΛΪΪκΨlπΞΔΆΞ^νβXΰaTΦZ]}°aJPίΣ VusΆ ²°=n{`"0Λ¬ΏσσΠr£nkZ€e`ς'Vvͺgφ5Jώa?Φ'NϋSΪqDD²€χkZϋφΈνΩ{αΎ;£v#ξΫίόφ%Π2ΰl^[4|3XAηdΧφcΫΪ¬¦T‘ΐWqϋ*
όζv>1?χ§>v0PΤ `μ'§oΚΰά/Cτ($ΫΘ―΅B~ΊδqΪDDjuΆ5VΧ
v[―χKζ+ΦςϊcΝΥ36₯»αX ¬¦T£0�9Ύg]νηΪ½ω;Ο€J$©(ν R¬ΠΎs·€₯6Yψ.6\c]m£�N#"R³lΠΐ8ΰ ²±όΉͺΩόω9°ΙΑ;{kP[3VχςgΫμ�$ uζO »Ψ&ν,5Οω(^«fN;HκώγH`§2TήSΊR½κΤω£Vh¦T+5 duu|cιήυ1Ά-n²Bώ3i'IΩ²ΥvZVsΡΘ°v=qΣ±ΪaDϊK
Y'6kΦ
νΏΑύ{igΙ¨υω½«/DDD²ΗΞι8Ψ―CμMWώΏΚX_ͺΫ>ΔώΦΥρ=5€¨ ύf3g£aΕοΐH;KΖΥaΜ±Bώ΄΄ΈΨ[Κ>3μcH5«ΓύθμΈ�
ν₯Fdm¨ ύbg΅
gXρΐΗ�Ξ"o’&duΆΐΐΌ9ΔζΜ8h Ζjf~ΨΦuϊiGY5 dY‘}#κλ―;8ν,ς¦X!FΪ!DDDΩΐΝΜ[€U ²6vΒsξkNT,5 dΨΜΓΐ.Ϋ'ν,ςΎi]ν?H;H9Y‘cΰΏn@°sfμ>`γI5Ϋ³λt8 T²Ί΄Hε³Y³0¬τ{ΰig5p;Υ
νK½©E«DD€6ΉOΒtΛπθ q Η¬E―½
ώπjο7{εί?νΛΑΊWύ%Ύ¬<ΆΔC j ί lllέχγJ0Ψ―±ΒΜyΣδϋΣ#ςvj Θ»²ΆΆQυ}(ν,²Φ~d]�ς SvΉ|~Ϊ!Κ/z8ν"]6gΖ’θθΨωΊΝρ?iϊΒ»ΪγQεώΌΩόω9>Ί6x;Μ·Γ}{νύpvrε5F@ρχvή¬}}ά€xlw₯Ό»Mtλ΄�ͺbΗ]6ητη}β©Χ§&3|BK>ν""5-ΖCSyuΡxΰG)]έκG,)χ>fL x¦οΫ-oύ9λl[¨~<:όp`ηrηιy[Vτόάΰ1EΦLg Θ;²BϋΜON;¬Dϋ΅sΖNi' Αζ·
&¦‘ΩfΟ�Oq|YήάϊΊOhΉΙ¦Nυ¦i»`₯½p?ήmef§«ύ[εGdν© «e
Αf¦CN�Ξf·mvΔ5| Ψ"ΕS·ό¨Η |Β©zsΛIxχ8SΕεΠfΨά»u~P@ώu±_N;$Ά3uυηXΪADDD:%ν ΰABπζΦΧ½yΪLπΕ2
5RΤQ¦Ϊ"ύ¦¬ΒΪΪ",ώ%fivΨ%(ϋ<νi.IΔζδ?μ°Μs’μc¨#ΒZ{Σ΄oc~ Ζ=eζs6§γγeͺ-�/j ΘͺFΥO>v Μa
£Σ!""²N"&%¬P$β3ΐλ³'Ν"Θ'΄άΛέaήY"?S+2₯¨ o²Bϋ~ΐχΣΞ!ε`υPΊΐζ΅5€DDD€?lξ;KTΔΉΜΗO»Έ @€ΟθέΪδEΠ2η \~o
νZ ©Σ5΅΅
fTΓyΐ ΄³Γ°AΨuψͺλΊΎ°μ5_Ί4ΤΥ1{Π]�]ΰ»κ_±t2fΙξqχψΗ DΡOγ&MEΔρ7eεΝΣ~b+1ΆrΤόΏ°5EϊG
ι΅ιιΈοvrωζ^ϋG?8ΰΙή<°{(>ιΟ,-κφ-λjΏΤ'΄ά΄�HΨάό°Κ>qϊ >~ΚcVΘ_|:a΄±Vh�7΅χyI7Oν΄B~G ΰ5~~Έ{ϊ¦~ό©/«)�?Ϊ ΨόΈOI;GΉΤηrLΨγ½UσZ_�ΣΝ1Χ_Y{ΓU₯ΧV¬]Ύ§`mmjώHε+1X?Q
ηΗ«ώΨ¨^―aΈ T²έΣ»VΜΚθυDϊj€rόΎςο+οΩ-Ϊlθ°ͺXώΓsOω>σΟ/^όψΓ‘χ½
νΛ¦
zΣ"""ΝζΟΟ|ύl²ύ«ό?Ν-Χ'¬ΖΙ6wnMn^ήΪZΔνh ;\U;,\-ώS γμ#q>vr:i―ύ+ώuή],ςέ;�T:μͺωΕ{ώίσΫ6²μ㬫
Oώ7°}Β*³|ΜUΆΣ98ΖμuΆ$^όΕ u€yσΤ1
KΎίfΞ°HΏTόΝ58vrϊΤ6;Ψ^#6©θΩ�/ς^ώ«bΗ½wΔ±�ΩΏΟFλΏ7PτqJΒ
ρξ―φg’ξΦΨNN\C*_u ΛUΜΠβΥι7νΞ²ξ&ΆM;F92z�=ψΟέιόΫ½ρ΄Ϋo*(
=λovΞgϋψ)
όΰ"ύgsΫR²-ΞX<Θγ>’ΐΧλ]k>§lwcΡr<ξ[LδOσ,δρζ)�Jω·T5ln~Cάv’δcΎ9m3χαψP°nz―Νzβ"n/bώ<ζΟ=Ηη}LkπM€6YWϋ{Α>°Κ\on}}u?γΛ¬?lΪχσ¦ϋΔ₯ΒxσYgϋωΊω!ϊpMZ"ύ£@FΩΜΓ<Υ΄ςμ5b€²Ε69ϋ�μ�ΧόΈ―.έτάΣ6ε�,GOK/ΘͺΞφ!ω�νϋnΐΆΠ°ζ`}ι}ίσΖ_#_ε;p0{Λχ[ί―±B~9p/Ζ_pξΏέZίh³ΞΆυ ώ`ΑΨcwz?ΠϋΉοϋ$όΗη�xσcϋ>χb+δ�
ΫSOcό
βrχσΒ²'½΅΅gHΥνD}_I³ίυ#Μηΰ6 H6YΠϋPπ¦©x_j ο€Θ:P «OvrϊφήEfχόΩΖM7_[Zάπ<uζ_±Ή3:Όqϊ#i'μωχΓ>ϋaΡ~ΰϋϋb6όίσeν
ήσώ7Y‘γA<Ύ(ώO8υΑr^Iμμ-¨γHάΗ`
σΦχαώ"`Μ· ήχf§Γ¨Χ³γπϋ»©o©ξβ¬Πώ΄SΌ©Ψs§ΤϊjΪ1ΦΝ=s$ΖWω4υΩwϋ ΠςO+δ― Ύh$η({ζoόφ’Du€²½Έβ�Υπ°yj;¨!²NΤ Θ Ϋ6jzΟΪΓΩ;μRQg\Ό²’Sώ|}ιΒΗͺ Y.ΛQdΉbηΎ)+£Ο}
ψfυώLaVα{φ�:Θ΅Z!x'Kλ.τΙ¦,4£λΟ@|
9ΑS=h=ΜίΌRDοjΏΘ~ΟΚξkͺιμ³ΐgΣNρ¦\ύΐ_�±Φββρτ6θΤfέF?
8Y ϊ2·'¬#Μ[[c+δ―!Μ{¦lΦ¬!>i�ς ΅Dϊ₯’dλΗ#�QN'ν΅48W9Ϋ�ozξiίη+λαΏρ%λόρΦiΗΪfσηη¬�=+sΟύψ"°QΪΉΦ`o°sVzΦ
Vh―τΌkΕΪΪ"λκ8BώoxόΏ8§2ίlΩΧp~C]ΓBλΜ_ggΟLz"ΌT8kk«ΓiJXζF8ε΅ω@or3πΧγΣΤwm‘Τ4»?P‘�NjτK%~Α2κϋβtRΪ9ΚiύΑ·λθxmχz―χϋΤU[ϊz₯LqΎ
"ZθPΥλ»kΥ₯εΥc8ψ°Η¬hmmΥψ{ ΐ:σοgTΓνΈ�Ψ-ν<ύ0γP|εjt²iύαΐ6ΙΨYύϊp·w?+`νlΓK�ψ|:RΡβp[ΓΌTΣqKεͺ$@/?qf;¦£Ϋut4ΌΎ!ν<Όψ%�Πΐ_ο·nqΊV@΅ί818Q
wΩU΅ΣfΟ�·�όYH;Ο:zΒOΎ0νRftΒaδφWυλ,.o1 u@T�bξlcύ`΅DϊA
¬ύψ΄#SΞ{ξκλΪέ9οϋγχ]φΛβ_-¬π'�7mΐ°ψΘ΄CHΝ{<ν μMέe]ω/₯dmΨ;S·μvoBΒsΥΣugΪ€Ό¬0s4ΨGαg}+ΦZοvQ’q{bsς{¨#ͺ'Z¬ 5 2ΔΞνΨ
ψ―΄sΣvΨ9Ϊnύ
S{ϋβς₯~Δ5nΎΆ΄¬Έ2λ(v©qf΅� Xη7VΘ7ν οΖ
g|(ϊ?`΄³$gw€@ΚΜJIgΠWΟ]§_ΫΟ
`DLPG*ΥδΙΛ~5ή―¦H�¨%+βcΑjϊoή?΅Χτο|4ήη�πτΥ2λ�6v°3cχ΄SH-σjί°:?΄Ξο€buzW(ΔΧ§%w ¨avή¬qΎ¬Wxγ©/¬Λ/ν;4πξDγχ:Ϊζζ7PG*χ^YσZj
5 ²Δ¨εͺλκComΪbΐg�_]ΡΓΔ?]WϊςuW^κ�ςΫ\βθΛiGV[+ ώΝόGΦΩ>)νoeηΜ8ηW@}ΪYYIΟΰ{Σ!e΄’g04Q
χsύz#Δ*υ(26@©\aήkΖqMOΚIεR #μόή`»€£&ν}ΐΏοxρy?θ�?{ψΎΚ»ήoέ¨ εc5Ή ΩVΘ:ν 6gΖAΔΡ|`PΪY1Π}Ω΅«χ"kNXζQZ�OTaeχ―₯ sΡlΥ}ήΌ»03χj%H�¨15}ΐΫN·ΓΆΩaΐ^ΟΕ8ζGwίVϊθ�ρκ+UΊδ΅v°�φχ¦Bj7N{¨ΥSάsΐEVθH΅Ρjg5Θ~C�ΤJγh�-[τδηνΥ0Ϋ·<{ωI―'ΚΡkg:Ϋ? T5k½�ή(fΊΦTR‘@fxMίM{ΚήF
L³ύοK^ς^ώΛβ�άuk\ͺτλύΦIτΩ΄HM«έU 0όΦΦήΧΦϊXνέ-ύ�΅Ν=ιμ7%"
qb MAκHe©/;ίΑ΅@�Qv )ΏήΣ�m·΄sΛΖ
Cψκ{v+ϋnwηόΏ?OΎν��ΥvΒ?Έ
ψaΪ1€f=Ό?Pεΐb ϋ-?ξ¦wφ»ΨN¨Ω΅s £98&