Skip to content

Commit

Permalink
Add RequestID to generate unique Identifier for each request
Browse files Browse the repository at this point in the history
This will also generate unique identifiers across multiple instances of a Humminbird application
  • Loading branch information
adam-fowler committed Dec 29, 2023
1 parent 8b6590a commit 1cafa1d
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 10 deletions.
7 changes: 2 additions & 5 deletions Sources/Hummingbird/Server/Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2021-2022 the Hummingbird authors
// Copyright (c) 2021-2023 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
Expand All @@ -12,7 +12,6 @@
//
//===----------------------------------------------------------------------===//

import Atomics
import HummingbirdCore
import Logging
import NIOConcurrencyHelpers
Expand Down Expand Up @@ -106,7 +105,7 @@ public struct HBRequest: Sendable, HBSendableExtensible {
context: context
)
self.body = body
self.logger = application.logger.with(metadataKey: "hb_id", value: .stringConvertible(Self.globalRequestID.loadThenWrappingIncrement(by: 1, ordering: .relaxed)))
self.logger = application.logger.with(metadataKey: "hb_id", value: .stringConvertible(RequestID()))
self.extensions = .init()
}

Expand Down Expand Up @@ -215,8 +214,6 @@ public struct HBRequest: Sendable, HBSendableExtensible {
}

private var _internal: _Internal

private static let globalRequestID = ManagedAtomic(0)
}

extension Logger {
Expand Down
42 changes: 42 additions & 0 deletions Sources/Hummingbird/Server/RequestID.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2023 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Atomics

/// Generate Unique ID for each request
struct RequestID: CustomStringConvertible {
let low: UInt64
let high: UInt64

init() {
self.low = Self.globalRequestID.loadThenWrappingIncrement(by: 1, ordering: .relaxed)
self.high = Self.instanceIdentifier
}

var description: String {
self.formatAsHexWithLeadingZeros(self.high) + self.formatAsHexWithLeadingZeros(self.low)
}

func formatAsHexWithLeadingZeros(_ value: UInt64) -> String {
let string = String(value, radix: 16)
if string.count < 16 {
return String(repeating: "0", count: 16 - string.count) + string
} else {
return string
}
}

private static let instanceIdentifier = UInt64.random(in: .min ... .max)
private static let globalRequestID = ManagedAtomic<UInt64>(UInt64.random(in: .min ... .max))
}
39 changes: 34 additions & 5 deletions Tests/HummingbirdTests/RouterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -331,25 +331,54 @@ final class RouterTests: XCTestCase {
}
}

/// Test we have a request id and that it increments with each request
/// Test we have a request id and that it is unique for each request
func testRequestId() throws {
let app = HBApplication(testing: .embedded)
app.router.get("id") { $0.id }
try app.XCTStart()
defer { app.XCTStop() }

let idString = try app.XCTExecute(uri: "/id", method: .GET) { response -> String in
let id = try app.XCTExecute(uri: "/id", method: .GET) { response -> String in
let body = try XCTUnwrap(response.body)
return String(buffer: body)
}
let id = try XCTUnwrap(Int(idString))
try app.XCTExecute(uri: "/id", method: .GET) { response in
let body = try XCTUnwrap(response.body)
let id2 = Int(String(buffer: body))
XCTAssertEqual(id2, id + 1)
let id2 = String(buffer: body)
XCTAssertNotEqual(id2, id)
}
}

/// Test we have a request id and that it is unique for each request even across instances
/// of running applications
func testRequestIdAcrossInstances() throws {
let id: String?
do {
let app = HBApplication(testing: .embedded)
app.router.get("id") { $0.id }
try app.XCTStart()
defer { app.XCTStop() }

id = try app.XCTExecute(uri: "/id", method: .GET) { response -> String in
let body = try XCTUnwrap(response.body)
return String(buffer: body)
}
}
let id2: String?
do {
let app = HBApplication(testing: .embedded)
app.router.get("id") { $0.id }
try app.XCTStart()
defer { app.XCTStop() }

id2 = try app.XCTExecute(uri: "/id", method: .GET) { response -> String in
let body = try XCTUnwrap(response.body)
return String(buffer: body)
}
}
XCTAssertNotEqual(id, id2)
}

// Test redirect response
func testRedirect() throws {
let app = HBApplication(testing: .embedded)
Expand Down

0 comments on commit 1cafa1d

Please sign in to comment.