Hypercorn Documentation

repository·main·Indexed 23 days ago

https://github.com/pgjones/hypercorn

A high-performance ASGI and WSGI web server based on Hyper libraries and inspired by Gunicorn. Hypercorn supports HTTP/1, HTTP/2, and WebSockets, with optional HTTP/3 support. It provides versatile worker implementations including asyncio, uvloop, and trio, and includes built-in DoS mitigations for connection and memory exhaustion.

Tokens
12.5K
Snippets
30
Records
77
Agent score
79%

What's inside Hypercorn

  1. Overview of Hypercorn capabilities

    main

    Hypercorn is a standalone ASGI web server inspired by Gunicorn. It is built using the sans-io hyper, h11, h2, and wsproto libraries.

    Key features include:

    • Protocol Support: HTTP/1, HTTP/2, and WebSockets (over both HTTP/1 and HTTP/2).
    • ASGI Support: ASGI/2 and ASGI/3 specifications.
    • Worker Types: Can utilize asyncio, uvloop, or trio as the underlying event loop/worker type.
  2. Understand the typical HTTP/1 or HTTP/2 request/response flow

    main

    In a standard Hypercorn lifecycle using H11 (HTTP/1) or H2 (HTTP/2), the sequence of events follows a specific pattern of data transfer between the TCP server, the protocol implementation, the HTTP stream, and your application.

    1. Request Phase: The TCP server sends raw data to the protocol layer, which then passes the request and body chunks to the HTTPStream. The application receives http.request events, where more_body=True indicates more data is coming, and more_body=False indicates the end of the request body.
    2. Response Phase: The application initiates a response via http.response.start and sends data via http.response.body. The HTTPStream translates this back into protocol-specific data for the TCP server.
    3. Teardown Phase: Once the response is complete, the stream is closed (StreamClosed), the application is notified via http.disconnect, and the TCP connection is closed.
    sequenceDiagram
         TCPServer->>H11/H2: RawData
         H11/H2->>HTTPStream: Request
         H11/H2->>HTTPStream: Body
         HTTPStream->>App:  http.request[more_body=True]
         H11/H2->>HTTPStream: EndBody
         HTTPStream->>App: http.request[more_body=False]
         App->>HTTPStream: http.response.start
         App->>HTTPStream: http.response.body
         HTTPStream->>H11/H2: Response
         H11/H2->>TCPServer: RawData
         HTTPStream->>H11/H2: Body
         H11/H2->>TCPServer: RawData
         HTTPStream->>H11/H2: EndBody
         H11/H2->>TCPServer: RawData
         HTTPStream->>H11/H2: StreamClosed
         HTTPStream->>App: http.disconnect
         H11/H2->>TCPServer: Closed
  3. Handle early client cancellations

    main

    If a client closes the TCP connection before the server has finished processing or responding, Hypercorn follows an abbreviated flow. The TCP server detects the closure, which triggers a StreamClosed event in the HTTPStream, and finally notifies the application via http.disconnect. Developers should ensure their application logic can handle http.disconnect being called mid-request to clean up resources.

    sequenceDiagram
         TCPServer->>H11/H2: RawData
         H11/H2->>HTTPStream: Request
         H11/H2->>HTTPStream: Body
         HTTPStream->>App:  http.request[more_body=True]
         TCPServer->>H11/H2: Closed
         H11/H2->>HTTPStream: StreamClosed
         HTTPStream->>App: http.disconnect
  4. ASGI disconnect message behavior and race conditions

    main

    Hypercorn guarantees that it will send the ASGI disconnect message exactly once to each application instance upon connection closure (whether triggered by the client or the server).

    To prevent race conditions, Hypercorn allows ASGI applications to continue sending messages to the server even after the connection has closed and the disconnect message has been sent. Instead of raising an error, Hypercorn will simply perform a no-op on these subsequent messages.

  5. Understand Denial of Service (DoS) mitigations in Hypercorn

    main

    Hypercorn implements several strategies to mitigate Denial of Service attacks, which generally fall into two categories:

    1. Connection Exhaustion: Attacks that aim to open as many connections as possible without freeing them. Hypercorn mitigates this using timeouts (keep_alive_timeout, ssl_handshake_timeout) and by limiting requests per connection to counter HTTP/2 Rapid Reset attacks.
    2. Memory Exhaustion: Attacks that aim to force the server to write large amounts of data to memory. Hypercorn mitigates this by responding to backpressure (pausing/blocking coroutines) during 'No response consumption', 'Flood attacks', and 'Internal Data Buffering' scenarios.

    Framework Responsibilities: For certain attacks, the responsibility lies with the application framework rather than Hypercorn to allow for intentional long-running or large-payload behaviors:

    • Large request body: The framework must guard against large incoming bodies.
    • Slow request body: The framework must guard against slow incoming bodies.
    • Slow response consumption: The framework must guard against slow outgoing consumption.
    • Data Dribble (HTTP/2): The framework must guard against clients requesting responses while maintaining tiny window sizes.
  6. Understand the server startup mechanism: Callbacks vs Streaming

    main
    Hypercorn uses the asyncio create_server callback approach for starting the server rather than the streaming start_server approach. This design choice was made based on benchmarking and research (specifically regarding uvloop) to ensure higher performance and lower overhead during the server startup process.
  7. Configure ALPN protocols for HTTP/2 support

    main

    Hypercorn supports both h2 and http/1.1 via ALPN (Application-Layer Protocol Negotiation).

    • To support both protocols, ensure ALPN includes both h2 and http/1.1.
    • If ALPN is not set, most clients will default to treating the server as HTTP/1.1 only.
    • By default, Hypercorn automatically sets both h2 and http/1.1 as the ALPN protocols.
  8. How Hypercorn handles client backpressure

    main

    Hypercorn manages backpressure by pausing data transmission when a client is unable to process information quickly enough. When a client applies backpressure, Hypercorn propagates this signal to the ASGI application by blocking the send awaitable.

    Specifically, any call to await send(message) within your ASGI application will suspend the coroutine (without blocking the underlying event loop) until the client's backpressure abates or the connection is closed. This mechanism allows the application to 'catch up' by preventing it from overwhelming the client with more data than it can handle.

  9. How Hypercorn handles client disconnections

    main
    When a client disconnects unexpectedly (e.g., while the server is still reading or sending data), Hypercorn catches the resulting socket exception and sends a Closed event to the protocol. The protocol then responds by sending a StreamClosed event to each active stream and deleting those streams.
  10. Choose a worker implementation for Hypercorn

    main

    Hypercorn supports three different worker classes to run ASGI applications, allowing you to choose the event loop that best fits your application's requirements and environment:

    • Asyncio: The default implementation using the standard library. It offers the best compatibility with third-party libraries.
    • Uvloop: A high-performance event loop policy for asyncio. It is faster than the default asyncio loop but is not supported on Windows.
    • Trio: A third-party event loop implementation. It is not compatible with asyncio and has less third-party library support, but it provides a more robust API designed to prevent common concurrency mistakes.
  11. How Hypercorn handles server disconnections

    main

    In a standard lifecycle, a stream should send EndBody or EndData followed by a StreamClosed event. However, if an application error occurs, the stream might only be able to send a StreamClosed event. To accommodate this, the protocol only sends a StreamClosed event back to the stream upon receiving a StreamClosed event from the stream itself.

    The protocol sends a Closed event to the server only when the connection must be terminated, such as in HTTP/1 without keep-alive or due to an error.