Skip to content
Open
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
33 changes: 33 additions & 0 deletions Sources/NetworkTypes/CapsuleProtocol/Capsule.swift

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we split each type into a file and can we move all capsule files into a subfolder please

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift HTTP API Proposal open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift HTTP API Proposal project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

/// A single capsule containing a type code and its opaque value, per RFC 9297, Section 3.2.
///
/// The Capsule protocol can be used through HTTP upgrade tokens that support it. Both
/// endpoints must agree to use it, e.g., using Extended CONNECT in H2 and H3.
///
/// A capsule is a TLV type. `value` contains opaque bytes, with a meaning defined
/// by `type`. The length is `value.count`.
public struct Capsule: Sendable {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the name Capsule too generic? Should it be HTTPCapsule?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was wondering about that as well but it wasn't apparent to me in the RFC that capsules are limited to HTTP.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe NetworkCapsule / NetworkDataCapsule? There is another framework where capsule is a thing:

https://developer.apple.com/documentation/scenekit/scncapsule

/// The capsule type.
public var type: CapsuleType

/// The value is an opaque byte sequence.
public var value: [UInt8]

/// Creates a capsule from a type and a value.
public init(type: CapsuleType, value: [UInt8]) {
self.type = type
self.value = value
}
}
64 changes: 64 additions & 0 deletions Sources/NetworkTypes/CapsuleProtocol/CapsuleType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift HTTP API Proposal open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift HTTP API Proposal project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

/// A capsule type, as defined in RFC 9297, Section 3.2.
///
/// Capsule types occupy an open 62-bit IANA registry. They define the meaning of the value carried
/// in the capsule. As an example, a `DATAGRAM` capsule (0x00, RFC 9297) simply carries the payload
/// of an HTTP datagram with meaning left to the application. In contrast, an `ADDRESS_ASSIGN`
/// capsule (0x01, RFC 9484) defines the structure of the value and the inforamtion it carries.
///
/// The capsule types defined here are not exhaustive. New standards can define new ones.
public struct CapsuleType: Sendable, Hashable {

/// The largest representable value, `2^62 - 1`.
static var maxRawValue: UInt64 { 0x3FFF_FFFF_FFFF_FFFF }

/// The numeric type rawValue.
///
/// Must be in the range `0 ... 2^62 - 1`.
public var rawValue: UInt64 {
didSet {
precondition(rawValue <= Self.maxRawValue, "capsule type \(rawValue) exceeds the allowed maximum value")
}
}

/// Creates a capsule type from its numeric rawValue.
///
/// - Precondition: `rawValue` is less than or equal to ``VariableLengthInteger/max``.
public init(_ rawValue: UInt64) {
precondition(rawValue <= Self.maxRawValue, "capsule type \(rawValue) exceeds the variable-length integer maximum")
self.rawValue = rawValue
}
}

// Enable direct initialization from integer literals
extension CapsuleType: ExpressibleByIntegerLiteral {
public init(integerLiteral value: UInt64) {
self.init(value)
}
}

extension CapsuleType {
/// The `DATAGRAM` capsule type (RFC 9297, Section 3.5).
public static var datagram: Self { 0x00 }

/// The `ADDRESS_ASSIGN` capusle type (RFC 9484, Section 4.7.1).
public static var addressAssign: Self { 0x01 }

/// The `ADDRESS_REQUEST` capusle type (RFC 9484, Section 4.7.2).
public static var addressRequest: Self { 0x02 }

/// The `ROUTE_ADVERTISEMENT` capsule type (RFC 9484, Section 4.7.3).
public static var routeAdvertisement: Self { 0x03 }
}
26 changes: 26 additions & 0 deletions Tests/NetworkTypesTests/CapsuleTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift HTTP API Proposal open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift HTTP API Proposal project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import NetworkTypes
import Testing

@Suite
struct CapsuleTests {
@Test
func initialization() {
let capsule = Capsule(type: .datagram, value: [0x01, 0x02])
#expect(capsule.type == .datagram)
#expect(capsule.value == [0x01, 0x02])
#expect(capsule.value.count == 2)
}
}
44 changes: 44 additions & 0 deletions Tests/NetworkTypesTests/CapsuleTypeTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift HTTP API Proposal open source project
//
// Copyright (c) 2026 Apple Inc. and the Swift HTTP API Proposal project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import NetworkTypes
import Testing

@Suite
struct CapsuleTypeTests {
@Test
func datagramTypeIsZero() {
#expect(CapsuleType.datagram.rawValue == 0)
#expect(CapsuleType.datagram == 0)
}

@Test
func capsuleTypeEqualityIsByCode() {
#expect(CapsuleType(5) == CapsuleType(5))
#expect(CapsuleType(5) != CapsuleType(6))
}

@Test
func hashableConformance() {
let capsuleA = CapsuleType.addressAssign
let capsuleD = CapsuleType.datagram

// Test that different types have different hash values
// Note: This is not guaranteed by Hashable but is expected in practice
#expect(capsuleA.hashValue != capsuleD.hashValue)

// Test that same version has same hash value
#expect(capsuleA.hashValue == CapsuleType.addressAssign.hashValue)
#expect(capsuleD.hashValue == CapsuleType.datagram.hashValue)
}
}
Loading