SwiftNIO HTTP/2

repository·main·Indexed 19 days ago

https://github.com/apple/swift-nio-http2

Provides HTTP/2 protocol support for Swift projects built on SwiftNIO. It features a modern pure-Swift implementation (1.x) and a legacy implementation (0.x) that wraps nghttp2. The library includes core handlers like NIOHTTP2Handler and HTTP2StreamMultiplexer, support for HPACK interoperability testing, and tools for configuring HTTP/2 server pipelines with TLS and ALPN.

Tokens
2.8K
Snippets
8
Records
17
Agent score
66%

What's inside swift-nio-http2

  1. Overview of NIOHTTP2 core components

    main

    The NIOHTTP2 library provides several categories of types to manage HTTP/2 connections:

    • Core Handlers: Includes NIOHTTP2Handler and HTTP2StreamMultiplexer for managing the connection and its streams.
    • Compatibility: Codecs like HTTP2FramePayloadToHTTP1ClientCodec and HTTP2ToHTTP1ServerCodec allow for interoperability between HTTP/2 and HTTP/1.1.
    • Frames: Low-level types for handling HTTP2Frame, HTTP2PingData, HTTP2Settings, and HTTP2ErrorCode.
    • Events: Inbound events such as NIOHTTP2StreamCreatedEvent, NIOHTTP2WindowUpdatedEvent, and StreamClosedEvent.
    • Options: Stream-specific configuration via HTTP2StreamChannelOptions.
  2. How stream multiplexing works in NIOHTTP2

    main

    NIOHTTP2 uses two different approaches for stream multiplexing: Legacy and Inline.

    • Legacy Multiplexing: Implemented as a separate ChannelHandler called HTTP2StreamMultiplexer. It maintains open child channels and relies on user-inbound events to pass information between the NIOHTTP2Handler and the multiplexer. This approach is generally more expensive due to the out-of-band passing of information.
    • Inline Multiplexing: The modern approach implemented directly within the NIOHTTP2Handler using NIOHTTP2Handler.InlineStreamMultiplexer. It reduces overhead by removing the need for user-inbound events, instead propagating events directly to the stream channels.

    Both methods are abstracted behind the HTTP2StreamMultiplexer protocol, allowing NIOHTTP2Handler to support either implementation via an internal HTTP2InboundStreamMultiplexer enum.

  3. Use legacy swift-nio-http 0.x with nghttp2

    main
    The legacy 0.x versions of swift-nio-http are part of the SwiftNIO 1 family. They support Swift 4.1 and newer but require the nghttp2 library to be installed on your system. The source code for this version is maintained on the nghttp2-support-branch branch.
  4. Configure an HTTP/2 server pipeline

    main

    The simplest way to implement HTTP/2 is to use the configureHTTP2Pipeline helper on a Channel. When configuring a server, set the mode to .server. This method provides a closure that is executed for every new HTTP/2 stream created on the connection, allowing you to configure the streamChannel specifically for that stream.

    channel.configureHTTP2Pipeline(mode: .server) { streamChannel -> EventLoopFuture<Void> in
        // This closure will be called once for each new HTTP/2 stream on a given connection
    }
  5. Handling discovered fuzzing issues

    main

    When the fuzz testing infrastructure identifies an issue, follow these steps to ensure it is tracked and prevented from recurring:

    1. Add a FailCase: Add a file to the FailCases subdirectory so that the do_build.sh --run-regressions command can automatically watch for this specific regression.
    2. Add Unit Tests: Consider adding the failing case to the project's unit tests to provide more robust regression testing.
  6. Build fuzz testing binaries on Linux

    main

    To build binaries for fuzz testing on Linux, use the following command. The resulting binaries will be located in .build/debug.

    Note: You can use -c release instead of -c debug to build in release mode, which may help identify different types of issues.

    swift build -c debug -Xswiftc -sanitize=fuzzer,address -Xswiftc -parse-as-library
  7. Use the do_build.sh script for fuzz testing

    main

    The do_build.sh script in the FuzzTesting directory automates the build process. By default, it builds for both _debug_ and _release_ configurations. You can pass the --run-regressions flag to run the build against previously identified failcases to check for regressions.

    ./do_build.sh --run-regressions
  8. How to use hpack-test-case for HPACK interoperability testing

    main

    The hpack-test-case suite provides JSON-formatted test stories to verify the correctness of HPACK (RFC 7541) encoder and decoder implementations. Each story file represents a sequence of requests or responses that share a continuous compression context.

    Testing a Decoder

    1. Iterate through the cases array in a story JSON file in the exact order they appear.
    2. For each case, decode the compressed header block provided in the wire field (hex string).
    3. Verify that the decoded result matches the HTTP headers provided in the headers field.

    Testing an Encoder

    1. Use the raw-data directory as a source of header sets.
    2. Encode the header sets found in the headers field of the JSON files.
    3. Generate new JSON story files containing your encoded wire data.
    4. Verify your encoder by passing the resulting wire data through a known-good HPACK decoder. If the decoded headers do not match your original input, there is a bug in the encoder or decoder.
    {
      "description": "Encoded request headers with Literal without index only.",
      "cases": [
        {
          "seqno": 0,
          "header_table_size": 4096,
          "wire": "1234567890abcdef",
          "headers": [
            { ":method": "GET" },
            { ":scheme": "http" },
            { ":authority": "example.com" },
            { ":path": "/" },
            { "x-my-header": "value1,value2" }
          ]
        }
      ]
    }
  9. Add swift-nio-http2 to your Swift project

    main

    To use the pure-Swift implementation of HTTP/2 (version 1.x), add swift-nio-http2 as a dependency in your Package.swift file. This version is part of the SwiftNIO 2 family and only requires swift-nio and a compatible Swift version.

    Note that the minimum required Swift version depends on the specific version of swift-nio-http2 you are using. For example, versions 1.43.0 and newer require Swift 6.1 or later.

    .package(url: "https://github.com/apple/swift-nio-http2.git", from: "1.19.2"),
  10. Set up HTTP/2 with TLS and ALPN

    main

    HTTP/2 typically requires a TLS handshake using ALPN (Application-Layer Protocol Negotiation). To set this up using swift-nio-ssl, you must add NIOHTTP2SupportedALPNProtocols to your TLSConfiguration.applicationProtocols.

    After configuring the SSL context and adding a NIOSSLServerHandler to the channel, you can then call configureHTTP2Pipeline to complete the HTTP/2 setup.

    var serverConfig = TLSConfiguration.makeServerConfiguration(certificateChain: certificateChain, privateKey: sslPrivateKey)
    serverConfig.applicationProtocols = NIOHTTP2SupportedALPNProtocols
    // Configure the SSL context that is used by all SSL handlers.
    
    let sslContext = try! NIOSSLContext(configuration: serverConfig)
    channel.addHandler(NIOSSLServerHandler(context: sslContext).flatMap { 
        channel.configureHTTP2Pipeline(mode: .server) { streamChannel -> EventLoopFuture<Void> in
            // This closure will be called once for each new HTTP/2 stream on a given connection
        }
    })
  11. Build fuzz testing binaries on macOS

    main

    To build binaries for fuzz testing on macOS, you must use the swift.org toolchain because the Swift toolchain distributed with Xcode does not include fuzzing support. Use xcrun --toolchain swift to ensure the correct toolchain is used.

    Note: You can use -c release instead of -c debug to build in release mode, which may help identify different types of issues.

    xcrun \
      --toolchain swift \
      swift build -c debug -Xswiftc -sanitize=fuzzer,address -Xswiftc -parse-as-library