Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions stubs/gunicorn/@tests/stubtest_allowlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ gunicorn.__main__
# Internal API stuff:
gunicorn.config.Config.forwarded_allow_networks
gunicorn.config.Config.proxy_allow_networks

# Set to None on class, but initialized to non-None value in __init__
gunicorn.http.message.Message.scheme
gunicorn.http2.request.HTTP2Request.scheme
gunicorn.http2.request.HTTP2Request.version
2 changes: 1 addition & 1 deletion stubs/gunicorn/METADATA.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version = "26.1.0"
version = "26.2.0"
upstream-repository = "https://github.com/benoitc/gunicorn"
optional-dependencies = ["types-gevent"]

Expand Down
3 changes: 3 additions & 0 deletions stubs/gunicorn/gunicorn/asgi/parser.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ class PythonProtocol:
proxy_protocol: Literal["off", "v1", "v2", "auto"] = "off",
) -> None: ...
def feed(self, data: Iterable[SupportsIndex]) -> None: ...
def remaining(self) -> bytes: ...
@property
def remaining_truncated(self) -> Literal[False]: ...
@property
def proxy_protocol_info(self) -> _ProxyProtocolInfo | _ProxyProtocolInfoUnknown | None: ...
def reset(self) -> None: ...
Expand Down
13 changes: 13 additions & 0 deletions stubs/gunicorn/gunicorn/config.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ _ASGILifespanValidatorType: TypeAlias = Callable[[str | None], str]
_HTTP2FrameSizeValidatorType: TypeAlias = Callable[[ConvertibleToInt], int]
_HTTPProtocolsValidatorType: TypeAlias = Callable[[str | None], list[str]]
_HttpParserValidatorType: TypeAlias = Callable[[str | None], str]
_HTTP2CleartextValidatorType: TypeAlias = Callable[[str | bool | None], str]

_ValidatorType: TypeAlias = ( # noqa: Y047
_BoolValidatorType
Expand All @@ -93,6 +94,7 @@ _ValidatorType: TypeAlias = ( # noqa: Y047
| _HTTP2FrameSizeValidatorType
| _HTTPProtocolsValidatorType
| _HttpParserValidatorType
| _HTTP2CleartextValidatorType
)

KNOWN_SETTINGS: list[Setting]
Expand Down Expand Up @@ -169,6 +171,8 @@ class Setting(metaclass=SettingMeta):

__cmp__ = __lt__

def validate_http2_cleartext(val: str | bool | None) -> str: ...

@overload
def validate_bool(val: bool) -> bool: ...
@overload
Expand Down Expand Up @@ -1054,6 +1058,15 @@ class HTTPProtocols(Setting):
default: ClassVar[str]
desc: ClassVar[str]

class HTTP2Cleartext(Setting):
name: ClassVar[str]
section: ClassVar[str]
cli: ClassVar[list[str]]
meta: ClassVar[str]
validator: ClassVar[_HTTP2CleartextValidatorType]
default: ClassVar[str]
desc: ClassVar[str]

class HTTP2MaxConcurrentStreams(Setting):
name: ClassVar[str]
section: ClassVar[str]
Expand Down
4 changes: 4 additions & 0 deletions stubs/gunicorn/gunicorn/http/errors.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,7 @@ class ForbiddenProxyRequest(ParseException):
def __init__(self, host: str) -> None: ...

class InvalidSchemeHeaders(ParseException): ...

class InvalidH2CPreface(ParseException):
data: bytes
def __init__(self, data: bytes) -> None: ...
10 changes: 7 additions & 3 deletions stubs/gunicorn/gunicorn/http/message.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import io
import re
from enum import IntEnum
from typing import Final
from typing import Final, Literal

from gunicorn.config import Config
from gunicorn.http.body import Body
Expand Down Expand Up @@ -38,7 +38,11 @@ RFC9110_5_5_INVALID_AND_DANGEROUS: Final[re.Pattern[str]]
RFC9110_6_5_1_FORBIDDEN_TRAILER: Final[frozenset[str]]
RFC9110_5_3_SINGLETON_FIELDS: Final[frozenset[str]]

class Message:
class HeaderPolicy:
scheme: str | None
version: tuple[int, int] | None

class Message(HeaderPolicy):
cfg: Config
unreader: Unreader
peer_addr: _AddressType
Expand All @@ -47,7 +51,7 @@ class Message:
headers: list[tuple[str, str]]
trailers: list[tuple[str, str]]
body: Body | None
scheme: str
scheme: Literal["https", "http"]
must_close: bool
limit_request_fields: int
limit_request_field_size: int
Expand Down
1 change: 1 addition & 0 deletions stubs/gunicorn/gunicorn/http/unreader.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class Unreader:

def __init__(self) -> None: ...
def chunk(self) -> bytes: ...
def take_buffered(self) -> bytes: ...
def read(self, size: int | None = None) -> bytes: ...
def unread(self, data: ReadableBuffer) -> None: ...

Expand Down
24 changes: 21 additions & 3 deletions stubs/gunicorn/gunicorn/http/wsgi.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import io
import logging
import re
import socket
from _typeshed import ReadableBuffer, Unused
from _typeshed import Incomplete, ReadableBuffer, Unused
from collections.abc import Callable
from typing import Any, Final, Protocol, type_check_only
from typing import Any, Final, Protocol, overload, type_check_only
from typing_extensions import Self

from gunicorn.config import Config
Expand Down Expand Up @@ -44,9 +44,27 @@ class WSGIErrorsWrapper(io.RawIOBase):
def base_environ(cfg: Config) -> _EnvironType: ...
def default_environ(req: Request, sock: socket.socket, cfg: Config) -> _EnvironType: ...
def proxy_environ(req: Request) -> _EnvironType: ...

@overload
def create(
req: Request, sock: socket.socket, client: _AddressType, server: _AddressType, cfg: Config
req: Request,
sock: socket.socket,
client: _AddressType,
server: _AddressType,
cfg: Config,
response_class: type[Response] | None = None,
response_args: tuple[Incomplete, ...] = (),
) -> tuple[Response, _EnvironType]: ...
@overload
def create(
req: Request,
sock: socket.socket,
client: _AddressType,
server: _AddressType,
cfg: Config,
response_class: type[Incomplete],
response_args: tuple[Incomplete, ...] = (),
) -> tuple[Incomplete, _EnvironType]: ...

class Response:
req: Request
Expand Down
4 changes: 4 additions & 0 deletions stubs/gunicorn/gunicorn/http2/async_connection.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ from collections.abc import Iterable
from typing import ClassVar

from gunicorn.config import Config
from gunicorn.http.message import Request
from gunicorn.http2.connection import _H2Connection
from gunicorn.http2.request import HTTP2Request
from gunicorn.http2.stream import HTTP2Stream
Expand All @@ -25,6 +26,9 @@ class AsyncHTTP2Connection:

def __init__(self, cfg: Config, reader: StreamReader, writer: StreamWriter, client_addr: _AddressType) -> None: ...
async def initiate_connection(self) -> None: ...
async def initiate_upgrade(
self, settings_header: bytes | None, http1_req: Request, body: bytes | None = b""
) -> HTTP2Request: ...
async def receive_data(self, timeout: float | None = None) -> list[HTTP2Request]: ...
async def send_informational(self, stream_id: int, status: int, headers: Iterable[tuple[str, Incomplete]]) -> None: ...
async def send_response(
Expand Down
6 changes: 6 additions & 0 deletions stubs/gunicorn/gunicorn/http2/connection.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ from ssl import SSLSocket
from typing import Any, ClassVar, TypeAlias

from gunicorn.config import Config
from gunicorn.http.message import Request
from gunicorn.http2.request import HTTP2Request
from gunicorn.http2.stream import HTTP2Stream

Expand All @@ -25,8 +26,13 @@ class HTTP2ServerConnection:

def __init__(self, cfg: Config, sock: SSLSocket, client_addr: _AddressType) -> None: ...
def initiate_connection(self) -> None: ...
def initiate_upgrade(self, settings_header: bytes | None, http1_req: Request, body: bytes | None = None) -> HTTP2Request: ...
def receive_data(self, data: bytes | None = None) -> list[HTTP2Request]: ...
def send_informational(self, stream_id: int, status: int, headers: Iterable[tuple[str, Incomplete]]) -> None: ...
def send_response_headers(
self, stream_id: int, status: int, headers: Iterable[tuple[str, Incomplete]], end_stream: bool = False
) -> bool: ...
def end_stream(self, stream_id: int, trailers: Iterable[tuple[str, Incomplete]] | None = None) -> bool: ...
def send_response(
self, stream_id: int, status: int, headers: Iterable[tuple[str, Incomplete]], body: bytes | None = None
) -> bool: ...
Expand Down
27 changes: 27 additions & 0 deletions stubs/gunicorn/gunicorn/http2/negotiation.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import socket
from typing import Final, Literal, Protocol, type_check_only

from gunicorn.config import Config

from .._types import _AddressType

@type_check_only
class _HasHeaders(Protocol):
headers: list[tuple[str, str]]

H2C_PREFACE: Final = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"
H2C_PREFACE_TIMEOUT: Final = 1.0
MATCH: Final = "match"
PARTIAL: Final = "partial"
MISMATCH: Final = "mismatch"

def preface_match(buf: bytes) -> Literal["match", "mismatch", "partial"]: ...
def peer_trusted_for_h2c(cfg: Config, peer_addr: _AddressType) -> bool: ...
def prior_knowledge_allowed(cfg: Config, peer_addr: _AddressType) -> bool: ...
def mismatch_is_error(cfg: Config) -> bool: ...
def upgrade_allowed(cfg: Config, peer_addr: _AddressType) -> bool: ...
def read_preface_blocking(sock: socket.socket, timeout: float | None = None) -> tuple[bool, bytes]: ...

UPGRADE_101: Final[bytes]

def upgrade_settings(req: _HasHeaders) -> bytes | None: ...
3 changes: 2 additions & 1 deletion stubs/gunicorn/gunicorn/http2/request.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ from collections.abc import Iterator
from typing import Literal

from gunicorn.config import Config
from gunicorn.http.message import HeaderPolicy
from gunicorn.http2.stream import HTTP2Stream

from .._types import _AddressType
Expand All @@ -17,7 +18,7 @@ class HTTP2Body:
def __len__(self) -> int: ...
def close(self) -> None: ...

class HTTP2Request:
class HTTP2Request(HeaderPolicy):
stream: HTTP2Stream
cfg: Config
peer_addr: _AddressType
Expand Down
17 changes: 17 additions & 0 deletions stubs/gunicorn/gunicorn/http2/response.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import socket

from gunicorn.config import Config
from gunicorn.http import Request
from gunicorn.http.wsgi import Response
from gunicorn.http2.connection import HTTP2ServerConnection

class HTTP2Response(Response):
h2_conn: HTTP2ServerConnection
stream_id: int
def __init__(
self, req: Request, sock: socket.socket, cfg: Config, h2_conn: HTTP2ServerConnection, stream_id: int
) -> None: ...
def is_chunked(self) -> bool: ...
def can_sendfile(self) -> bool: ...
def send_headers(self) -> None: ...
def close(self) -> None: ...
9 changes: 8 additions & 1 deletion stubs/gunicorn/gunicorn/workers/base_async.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ class AsyncWorker(base.Worker):
def timeout_ctx(self) -> None: ...
def is_already_handled(self, respiter: object) -> bool: ...
def handle(self, listener: socket.socket, client: socket.socket, addr: _AddressType) -> None: ...
def handle_http2(self, listener: socket.socket, client: socket.socket, addr: _AddressType) -> None: ...
def handle_http2(
self,
listener: socket.socket,
client: socket.socket,
addr: _AddressType,
preface: bytes | None = b"",
upgrade: tuple[bytes | None, Request, bytes | None] | None = None,
) -> None: ...
def handle_http2_request(
self, listener_name: _AddressType, req: Request, sock: socket.socket, addr: _AddressType, h2_conn: HTTP2ServerConnection
) -> None: ...
Expand Down
1 change: 1 addition & 0 deletions stubs/gunicorn/gunicorn/workers/gthread.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class ThreadWorker(base.Worker):
def run(self) -> None: ...
def finish_request(self, conn: TConn, fs: Future[bool]) -> None: ...
def handle(self, conn: TConn) -> bool: ...
def handle_h2c_upgrade(self, conn: TConn, req: Request, settings: bytes | None) -> bool: ...
def handle_http2(self, conn: TConn) -> bool: ...
def handle_http2_request(self, req: Request, conn: TConn, h2_conn: HTTP2ServerConnection) -> None: ...
def handle_request(self, req: Request, conn: TConn) -> bool: ...