فهرست منبع

update Swift/NIO test (#5423)

* update Swift/NIO test

* fix implicit optional
Tanner 5 سال پیش
والد
کامیت
89d24ec52c

+ 3 - 0
frameworks/Swift/swift-nio/.dockerignore

@@ -0,0 +1,3 @@
+**/.swiftpm
+**/.build
+

+ 0 - 14
frameworks/Swift/swift-nio/Package.swift

@@ -1,14 +0,0 @@
-// swift-tools-version:4.0
-// The swift-tools-version declares the minimum version of Swift required to build this package.
-
-import PackageDescription
-
-let package = Package(
-    name: "SwiftNIOTFB",
-    dependencies: [
-        .package(url: "https://github.com/apple/swift-nio.git", from: "2.7.0"),
-    ],
-    targets: [
-        .target(name: "swift-nio-tfb-default", dependencies: ["NIO", "NIOHTTP1"]),
-    ]
-)

+ 0 - 98
frameworks/Swift/swift-nio/Sources/swift-nio-tfb-default/RFC1123DateFormatter.swift

@@ -1,98 +0,0 @@
-
-// Borrowed from Vapor (Date Middleware)
-// https://github.com/vapor/vapor/blob/master/Sources/Vapor/Middleware/DateMiddleware.swift
-
-import Foundation
-#if os(Linux)
-import Glibc
-#else
-import Darwin.C
-#endif
-
-fileprivate let DAY_NAMES = [
-    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
-]
-
-fileprivate let MONTH_NAMES = [
-    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-]
-
-fileprivate let NUMBERS = [
-    "00", "01", "02", "03", "04", "05", "06", "07", "08", "09",
-    "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
-    "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
-    "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
-    "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
-    "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
-    "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
-    "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
-    "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
-    "90", "91", "92", "93", "94", "95", "96", "97", "98", "99"
-]
-
-fileprivate var cachedTimeComponents: (key: time_t, components: tm)?
-
-let secondsInDay = 60 * 60 * 24
-let accuracy = 1 // seconds
-
-final class RFC1123DateFormatter {
-    var cachedTimestamp: (timestamp: String, createdAt: time_t)?
-
-    /// Gets the current RFC 1123 date string.
-    func getDate() -> String {
-        var date = time(nil)
-
-        if let (timestamp, createdAt) = cachedTimestamp, (createdAt...(createdAt + accuracy)).contains(date) {
-            return timestamp
-        }
-
-        // generate a key used for caching.
-        // this key is a unique id for each day
-        let key = date / secondsInDay
-
-        // get time components
-        let dateComponents: tm
-        if let cached = cachedTimeComponents, cached.key == key {
-            dateComponents = cached.components
-        } else {
-            let tc = gmtime(&date).pointee
-            dateComponents = tc
-            cachedTimeComponents = (key: key, components: tc)
-        }
-
-        // parse components
-        let year: Int = numericCast(dateComponents.tm_year) + 1900 // years since 1900
-        let month: Int = numericCast(dateComponents.tm_mon) // months since January [0-11]
-        let monthDay: Int = numericCast(dateComponents.tm_mday) // day of the month [1-31]
-        let weekDay: Int = numericCast(dateComponents.tm_wday) // days since Sunday [0-6]
-
-        // get basic time info
-        let timeX: Int = date % secondsInDay
-        let hours: Int = numericCast(timeX / 3600)
-        let minutes: Int = numericCast((timeX / 60) % 60)
-        let seconds: Int = numericCast(timeX % 60)
-
-        var rfc1123 = ""
-        rfc1123.reserveCapacity(30)
-
-        rfc1123.append(DAY_NAMES[weekDay])
-        rfc1123.append(", ")
-        rfc1123.append(NUMBERS[monthDay])
-        rfc1123.append(" ")
-        rfc1123.append(MONTH_NAMES[month])
-        rfc1123.append(" ")
-        rfc1123.append(NUMBERS[year / 100])
-        rfc1123.append(NUMBERS[year % 100])
-        rfc1123.append(" ")
-        rfc1123.append(NUMBERS[hours])
-        rfc1123.append(":")
-        rfc1123.append(NUMBERS[minutes])
-        rfc1123.append(":")
-        rfc1123.append(NUMBERS[seconds])
-        rfc1123.append(" GMT")
-
-        cachedTimestamp = (rfc1123, date)
-
-        return rfc1123
-    }
-}

+ 21 - 0
frameworks/Swift/swift-nio/app/Package.swift

@@ -0,0 +1,21 @@
+// swift-tools-version:5.1
+import PackageDescription
+
+let package = Package(
+    name: "app",
+    platforms: [
+        .macOS(.v10_15)
+    ],
+    products: [
+        .executable(name: "app", targets: ["App"])
+    ],
+    dependencies: [
+        .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"),
+    ],
+    targets: [
+        .target(name: "App", dependencies: [
+            "NIO",
+            "NIOHTTP1",
+        ], path: "Sources")
+    ]
+)

+ 120 - 0
frameworks/Swift/swift-nio/app/Sources/RFC1123DateFormatter.swift

@@ -0,0 +1,120 @@
+import NIO
+import Foundation
+
+/// Performant method for generating RFC1123 date headers.
+internal final class RFC1123DateCache {
+    static func on(_ eventLoop: EventLoop) -> RFC1123DateCache {
+        assert(eventLoop.inEventLoop)
+
+        if let existing = thread.currentValue {
+            return existing
+        } else {
+            let new = RFC1123DateCache()
+            let fracSeconds = 1.0 - Date().timeIntervalSince1970.truncatingRemainder(dividingBy: 1)
+            let msToNextSecond = Int64(fracSeconds * 1000) + 1
+            eventLoop.scheduleRepeatedTask(initialDelay: .milliseconds(msToNextSecond), delay: .seconds(1)) { task in
+                new.updateTimestamp()
+            }
+            self.thread.currentValue = new
+            return new
+        }
+    }
+
+    /// Thread-specific RFC1123
+    private static let thread: ThreadSpecificVariable<RFC1123DateCache> = .init()
+
+    /// Currently cached time components.
+    private var cachedTimeComponents: (key: time_t, components: tm)?
+
+    /// Currently cached timestamp.
+    private var timestamp: String
+
+    /// Creates a new `RFC1123DateCache`.
+    private init() {
+        self.timestamp = ""
+        self.updateTimestamp()
+    }
+
+    func currentTimestamp() -> String {
+        return self.timestamp
+    }
+
+    /// Updates the current RFC 1123 date string.
+    func updateTimestamp() {
+        // get the current time
+        var date = time(nil)
+
+        // generate a key used for caching
+        // this key is a unique id for each day
+        let key = date / secondsInDay
+
+        // get time components
+        let dateComponents: tm
+        if let cached = self.cachedTimeComponents, cached.key == key {
+            dateComponents = cached.components
+        } else {
+            var tc = tm.init()
+            gmtime_r(&date, &tc)
+            dateComponents = tc
+            self.cachedTimeComponents = (key: key, components: tc)
+        }
+
+        // parse components
+        let year: Int = numericCast(dateComponents.tm_year) + 1900 // years since 1900
+        let month: Int = numericCast(dateComponents.tm_mon) // months since January [0-11]
+        let monthDay: Int = numericCast(dateComponents.tm_mday) // day of the month [1-31]
+        let weekDay: Int = numericCast(dateComponents.tm_wday) // days since Sunday [0-6]
+
+        // get basic time info
+        let t: Int = date % secondsInDay
+        let hours: Int = numericCast(t / 3600)
+        let minutes: Int = numericCast((t / 60) % 60)
+        let seconds: Int = numericCast(t % 60)
+
+        // generate the RFC 1123 formatted string
+        var rfc1123 = ""
+        rfc1123.reserveCapacity(30)
+        rfc1123.append(dayNames[weekDay])
+        rfc1123.append(", ")
+        rfc1123.append(stringNumbers[monthDay])
+        rfc1123.append(" ")
+        rfc1123.append(monthNames[month])
+        rfc1123.append(" ")
+        rfc1123.append(stringNumbers[year / 100])
+        rfc1123.append(stringNumbers[year % 100])
+        rfc1123.append(" ")
+        rfc1123.append(stringNumbers[hours])
+        rfc1123.append(":")
+        rfc1123.append(stringNumbers[minutes])
+        rfc1123.append(":")
+        rfc1123.append(stringNumbers[seconds])
+        rfc1123.append(" GMT")
+
+        // cache the new timestamp
+        self.timestamp = rfc1123
+    }
+}
+
+// MARK: Private
+private let dayNames = [
+    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+]
+
+private let monthNames = [
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+]
+
+private let stringNumbers = [
+    "00", "01", "02", "03", "04", "05", "06", "07", "08", "09",
+    "10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
+    "20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
+    "30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
+    "40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
+    "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
+    "60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
+    "70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
+    "80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
+    "90", "91", "92", "93", "94", "95", "96", "97", "98", "99"
+]
+
+private let secondsInDay = 60 * 60 * 24

+ 31 - 46
frameworks/Swift/swift-nio/Sources/swift-nio-tfb-default/main.swift → frameworks/Swift/swift-nio/app/Sources/main.swift

@@ -14,47 +14,43 @@ enum Constants {
     static let plainTextResponseLength = plainTextResponse.utf8CodeUnitCount
     static let plainTextResponseLengthString = String(plainTextResponseLength)
 
+    static let jsonResponse = try! JSONEncoder().encode(JSONTestResponse())
     static let jsonResponseLength = try! JSONEncoder().encode(JSONTestResponse()).count
     static let jsonResponseLengthString = String(jsonResponseLength)
 }
 
 private final class HTTPHandler: ChannelInboundHandler {
-
     public typealias InboundIn = HTTPServerRequestPart
     public typealias OutboundOut = HTTPServerResponsePart
 
-    let dateFormatter = RFC1123DateFormatter()
-    let jsonEncoder = JSONEncoder()
-
-    var plaintextBuffer: ByteBuffer!
-    var jsonBuffer: ByteBuffer!
+    let dateCache: RFC1123DateCache
+    var plaintextBuffer: ByteBuffer
+    var jsonBuffer: ByteBuffer
 
-    init() {
+    init(channel: Channel) {
         let allocator = ByteBufferAllocator()
-
-        plaintextBuffer = allocator.buffer(capacity: Constants.plainTextResponseLength)
-        plaintextBuffer.writeStaticString(Constants.plainTextResponse)
-
-        jsonBuffer = allocator.buffer(capacity: Constants.jsonResponseLength)
+        self.plaintextBuffer = allocator.buffer(capacity: Constants.plainTextResponseLength)
+        self.plaintextBuffer.writeStaticString(Constants.plainTextResponse)
+        self.jsonBuffer = allocator.buffer(capacity: Constants.jsonResponseLength)
+        self.jsonBuffer.writeBytes(Constants.jsonResponse)
+        self.dateCache = .on(channel.eventLoop)
     }
 
     func channelRead(context: ChannelHandlerContext, data: NIOAny) {
-        let reqPart = self.unwrapInboundIn(data)
-
-        switch reqPart {
+        switch self.unwrapInboundIn(data) {
         case .head(let request):
             switch request.uri {
-            case "/plaintext":
-                processPlaintext(ctx: context)
-            case "/json":
-                processJSON(ctx: context)
+            case "/p":
+                self.processPlaintext(context: context)
+            case "/j":
+                self.processJSON(context: context)
             default:
                 _ = context.close()
             }
         case .body:
             break
         case .end:
-            _ = context.write(self.wrapOutboundOut(.end(nil)), promise: nil)
+            context.write(self.wrapOutboundOut(.end(nil)), promise: nil)
         }
     }
 
@@ -63,28 +59,16 @@ private final class HTTPHandler: ChannelInboundHandler {
         context.fireChannelReadComplete()
     }
 
-    private func processPlaintext(ctx: ChannelHandlerContext) {
-        let responseHead = plainTextResponseHead(contentLength: Constants.plainTextResponseLengthString)
-        ctx.write(self.wrapOutboundOut(.head(responseHead)), promise: nil)
-        ctx.write(self.wrapOutboundOut(.body(.byteBuffer(plaintextBuffer))), promise: nil)
+    private func processPlaintext(context: ChannelHandlerContext) {
+        let responseHead = self.responseHead(contentType: "text/plain", contentLength: Constants.plainTextResponseLengthString)
+        context.write(self.wrapOutboundOut(.head(responseHead)), promise: nil)
+        context.write(self.wrapOutboundOut(.body(.byteBuffer(self.plaintextBuffer))), promise: nil)
     }
 
-    private func processJSON(ctx: ChannelHandlerContext) {
-        let responseHead = jsonResponseHead(contentLength: Constants.jsonResponseLengthString)
-        ctx.write(self.wrapOutboundOut(.head(responseHead)), promise: nil)
-
-        let responseData = try! jsonEncoder.encode(JSONTestResponse())
-        jsonBuffer.clear()
-        jsonBuffer.writeBytes(responseData)
-        ctx.write(self.wrapOutboundOut(.body(.byteBuffer(jsonBuffer))), promise: nil)
-    }
-
-    private func jsonResponseHead(contentLength: String) -> HTTPResponseHead {
-        return responseHead(contentType: "application/json", contentLength: contentLength)
-    }
-
-    private func plainTextResponseHead(contentLength: String) -> HTTPResponseHead {
-        return responseHead(contentType: "text/plain", contentLength: contentLength)
+    private func processJSON(context: ChannelHandlerContext) {
+        let responseHead = self.responseHead(contentType: "application/json", contentLength: Constants.jsonResponseLengthString)
+        context.write(self.wrapOutboundOut(.head(responseHead)), promise: nil)
+        context.write(self.wrapOutboundOut(.body(.byteBuffer(self.jsonBuffer))), promise: nil)
     }
 
     private func responseHead(contentType: String, contentLength: String) -> HTTPResponseHead {
@@ -92,11 +76,12 @@ private final class HTTPHandler: ChannelInboundHandler {
         headers.add(name: "content-type", value: contentType)
         headers.add(name: "content-length", value: contentLength)
         headers.add(name: "server", value: Constants.serverName)
-        headers.add(name: "date", value: dateFormatter.getDate())
-
-        return HTTPResponseHead(version: Constants.httpVersion,
-                                status: .ok,
-                                headers: headers)
+        headers.add(name: "date", value: self.dateCache.currentTimestamp())
+        return HTTPResponseHead(
+            version: Constants.httpVersion,
+            status: .ok,
+            headers: headers
+        )
     }
 }
 
@@ -106,7 +91,7 @@ let bootstrap = ServerBootstrap(group: group)
     .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
     .childChannelInitializer { channel in
         channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: false).flatMap {
-            channel.pipeline.addHandler(HTTPHandler())
+            channel.pipeline.addHandler(HTTPHandler(channel: channel))
         }
     }
     .childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)

+ 2 - 2
frameworks/Swift/swift-nio/benchmark_config.json

@@ -2,8 +2,8 @@
   "framework": "swift-nio",
   "tests": [{
     "default": {
-      "json_url": "/json",
-      "plaintext_url": "/plaintext",
+      "json_url": "/j",
+      "plaintext_url": "/p",
       "port": 8080,
       "approach": "Realistic",
       "classification": "Platform",

+ 30 - 5
frameworks/Swift/swift-nio/swift-nio.dockerfile

@@ -1,7 +1,32 @@
-FROM swift:5.0.1
+# ================================
+# Build image
+# ================================
+FROM swift:5.1 as build
+WORKDIR /build
 
-ADD ./ /swift-nio
-WORKDIR /swift-nio
-RUN swift build -c release
+# Copy entire repo into container
+COPY ./app .
 
-CMD .build/release/swift-nio-tfb-default
+# Compile with optimizations
+RUN swift build \
+	--enable-test-discovery \
+	-c release
+
+# ================================
+# Run image
+# ================================
+FROM ubuntu:18.04
+WORKDIR /run
+
+# Install Swift dependencies
+RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ 
+  libatomic1 \
+  && rm -r /var/lib/apt/lists/*
+
+# Copy build artifacts
+COPY --from=build /build/.build/release /run
+
+# Copy Swift runtime libraries
+COPY --from=build /usr/lib/swift/ /usr/lib/swift/
+
+ENTRYPOINT ["./app"]