Hummingbird Web Framework

repository·main·Indexed 23 days ago

https://github.com/hummingbird-project/hummingbird

A lightweight, flexible, and modern web application framework written in Swift and built on top of SwiftNIO. It features a minimal core with a modular architecture, supporting extensions for authentication, database integration, WebSockets, and AWS Lambda. The framework includes tools for routing, TLS, HTTP2, and integrated security measures to mitigate memory exhaustion and DoS attacks.

Tokens
2.4K
Snippets
6
Records
15
Agent score
84%

What's inside Hummingbird

  1. Available Hummingbird extensions

    main

    Hummingbird's core is minimal. Additional functionality is provided through official built-in extensions and community-maintained external extensions.

    Built-in Extensions

    • HummingbirdRouter: An alternative router using result builders.
    • HummingbirdTLS: TLS support.
    • HummingbirdHTTP2: HTTP2 upgrade support.
    • HummingbirdTesting: Helper functions for testing Hummingbird projects.

    Additional Extensions (External Repositories)

    • HummingbirdAuth: Authentication framework.
    • HummingbirdFluent: Integration with Vapor's FluentKit ORM.
    • HummingbirdRedis: Redis support via RediStack.
    • HummingbirdWebSocket: WebSocket support.
    • HummingbirdLambda: Run Hummingbird on AWS Lambda.
    • Jobs: Job Queue Framework.
    • Mustache: Mustache templating engine.
  2. Understand the Hummingbird threat model and security objectives

    main

    The Hummingbird threat model focuses on the network-facing elements of a server deployed in production. The primary security objectives are:

    1. Confidentiality: Protecting request/response data and secrets handled by the application.
    2. Availability: Preserving service availability even under malicious or malformed traffic.

    Hummingbird is built on swift-nio. The typical data flow involves swift-nio consuming untrusted client traffic to generate an HTTP request, which is then processed by the Hummingbird Router and a middleware chain before being passed to an application handler. The response then travels back through the middleware to swift-nio to be written to the network.

  3. Quickstart: Create a basic Hummingbird application

    main

    Hummingbird is a lightweight, flexible web application framework built on SwiftNIO. To create a basic application, you define a Router, add your endpoints, initialize an Application with a configuration (such as a hostname and port), and then run the service using await app.runService().

    import Hummingbird
    
    // create router and add a single GET /hello route
    let router = Router()
    router.get("hello") { request, _ -> String in
        return "Hello"
    }
    // create application using router
    let app = Application(
        router: router,
        configuration: .init(address: .hostname("127.0.0.1", port: 8080))
    )
    // run hummingbird application
    try await app.runService()
  4. Install Hummingbird via Swift Package Manager

    main

    You can install Hummingbird by adding it to your Package.swift file or by using the SwiftPM CLI.

    Using Package.swift

    Add the package to your dependencies and the Hummingbird product to your target's dependencies.

    Using SwiftPM CLI

    Run the following commands, replacing MyApp with your target name:

    swift package add-dependency https://github.com/hummingbird-project/hummingbird.git --from 2.0.0
    swift package add-target-dependency Hummingbird MyApp --package hummingbird
    dependencies: [
        .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "2.0.0")
    ],
    targets: [
      .executableTarget(
        name: "MyApp",
        dependencies: [
            .product(name: "Hummingbird", package: "hummingbird"),
        ]),
    ]
  5. Mitigate memory exhaustion from large HTTP payloads

    main

    To prevent memory exhaustion, Hummingbird is designed to stream request payloads and use backpressure. This ensures that in-transit payload chunks do not consume excessive memory while waiting for processing.

    Note for Developers: While Hummingbird streams payloads, an application can still cause memory exhaustion if it manually collates an unbounded request payload into a single buffer. You should apply limits to reject large payloads at the application level.

  6. Avoid exposing sensitive data in logs, metrics, or traces

    main

    Hummingbird provides middleware for generating logging information, metrics, and trace spans.

    Security Best Practice: Ensure that these middleware components are not configured to expose credentials, PII (Personally Identifiable Information), or other sensitive data in the output.

  7. Prevent CPU/Memory exhaustion from connection floods and Slowloris attacks

    main

    Hummingbird provides mechanisms to protect against resource exhaustion attacks:

    • Connection Floods: Use Hummingbird's built-in mechanisms to limit the total number of connections the server will accept.
    • Slowloris Attacks: Use Hummingbird's mechanisms to close idle connections, which limits the number of connections a single machine can keep open.

    Limitation: Hummingbird does not currently support closing connections that are 'drip feeding' bytes (sending data extremely slowly to keep the connection alive).

  8. Prevent Denial of Service (DoS) on upstream services via untrusted data

    main

    When sending data to upstream services (like metrics backends), avoid using raw untrusted data directly as dimensions or keys.

    Example: Do not use the raw request uri as a dimension in a metric, as an attacker could send thousands of random URIs to overwhelm the metrics backend. Instead, use the matched route path provided by the router.

  9. Protect against Cross-Site Scripting (XSS)

    main

    While a server framework cannot directly prevent all XSS attacks, you can use the following tools provided within the Hummingbird ecosystem:

    1. Content-Security-Policy (CSP): Use Hummingbird's support for building CSP headers to restrict the sources from which scripts can be loaded.
    2. swift-mustache: If using the swift-mustache package (part of the Hummingbird framework), it will automatically neutralize HTML grammar to prevent scripts from being inserted into web pages.
  10. Run integration tests using Docker Compose

    main

    You can run the Hummingbird integration test suite using Docker Compose. The integration-tests service uses a Swift 5.7 image and executes the test runner script located at ./IntegrationTests/run-tests.sh.

    To pass arguments to the test runner, you can use the INTEGRATION_TESTS_ARG environment variable, which is interpolated into the command.

  11. Initialize and run the Hummingbird Server

    main

    The Server actor is the primary entrypoint for starting a Hummingbird web server. It conforms to Service (from ServiceLifecycle), meaning it is designed to be managed within a structured concurrency lifecycle.

    To use it, you must provide:

    1. A childChannelSetup conforming to ServerChildChannel to handle incoming connections.
    2. A ServerConfiguration defining the address and socket options.
    3. An EventLoopGroup for networking.
    4. A Logger for observability.

    You start the server by calling await server.run(). To stop the server gracefully, call await server.shutdownGracefully().

  12. Configure integration test allocation limits

    main

    The integration test suite uses specific environment variables to set maximum allocation thresholds for different test routes. These limits are used to validate performance or memory constraints during testing.

    Available environment variables for controlling allocation limits:

    • MAX_ALLOCS_ALLOWED_1000_basicRoute
    • MAX_ALLOCS_ALLOWED_1000_bodyInRequestRoute
    • MAX_ALLOCS_ALLOWED_1000_bodyInResponseRoute
    • MAX_ALLOCS_ALLOWED_1000_coreBasicRoute
    • MAX_ALLOCS_ALLOWED_1000_largeBodyInRequest
    environment:
      - MAX_ALLOCS_ALLOWED_1000_basicRoute=56000
      - MAX_ALLOCS_ALLOWED_1000_bodyInRequestRoute=59000
      - MAX_ALLOCS_ALLOWED_1000_bodyInResponseRoute=55000
      - MAX_ALLOCS_ALLOWED_1000_coreBasicRoute=42000
      - MAX_ALLOCS_ALLOWED_1000_largeBodyInRequest=112000