Pārlūkot izejas kodu

Add support for SwiftNIO (default tests) (#3488)

Add support for SwiftNIO (default tests) also update Swift to stable 4.1
0xpablo 7 gadi atpakaļ
vecāks
revīzija
256359f60a

+ 1 - 0
.travis.yml

@@ -192,6 +192,7 @@ env:
      - "TESTDIR=Scala/fintrospect"
      - "TESTDIR=Scala/http4s"
      - "TESTDIR=Scala/play2-scala"
+     - "TESTDIR=Swift/swift-nio"
      - "TESTDIR=Swift/vapor"
      - "TESTDIR=Ur/urweb"
      - "TESTDIR=Vala/vsgi"

+ 67 - 0
frameworks/Swift/swift-nio/.gitignore

@@ -0,0 +1,67 @@
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+*.xcodeproj
+
+## Build generated
+build/
+DerivedData/
+
+## Various settings
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata/
+
+## Other
+*.moved-aside
+*.xcuserstate
+
+## Obj-C/Swift specific
+*.hmap
+*.ipa
+*.dSYM.zip
+*.dSYM
+
+## Playgrounds
+timeline.xctimeline
+playground.xcworkspace
+
+# Swift Package Manager
+#
+# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
+Packages/
+.build/
+
+# CocoaPods
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+# Pods/
+
+# Carthage
+#
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+# Carthage/Checkouts
+
+Carthage/Build
+
+# fastlane
+#
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
+# screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
+
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output

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

@@ -0,0 +1,14 @@
+// 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: "1.3.1"),
+    ],
+    targets: [
+        .target(name: "swift-nio-tfb-default", dependencies: ["NIO", "NIOHTTP1"]),
+    ]
+)

+ 15 - 0
frameworks/Swift/swift-nio/README.md

@@ -0,0 +1,15 @@
+# [SwiftNIO](https://github.com/apple/swift-nio) framework
+
+## Description
+
+SwiftNIO is an event-driven network application framework for high performance protocol servers & clients, non-blocking.
+
+## Test URLs
+
+### Test 1: JSON Encoding 
+
+    http://localhost:8080/json
+
+### Test 2: Plaintext
+
+    http://localhost:8080/plaintext

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

@@ -0,0 +1,98 @@
+
+// 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
+    }
+}

+ 126 - 0
frameworks/Swift/swift-nio/Sources/swift-nio-tfb-default/main.swift

@@ -0,0 +1,126 @@
+
+import Foundation
+import NIO
+import NIOHTTP1
+
+struct JSONTestResponse: Encodable {
+    let message = "Hello, World!"
+}
+
+enum Constants {
+    static let httpVersion = HTTPVersion(major: 1, minor: 1)
+    static let serverName = "SwiftNIO"
+
+    static let plainTextResponse: StaticString = "Hello, World!"
+    static let plainTextResponseLength = plainTextResponse.count
+    static let plainTextResponseLengthString = String(plainTextResponseLength)
+
+    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!
+
+    init() {
+        let allocator = ByteBufferAllocator()
+
+        plaintextBuffer = allocator.buffer(capacity: Constants.plainTextResponseLength)
+        plaintextBuffer.write(staticString: Constants.plainTextResponse)
+
+        jsonBuffer = allocator.buffer(capacity: Constants.jsonResponseLength)
+    }
+
+    func channelRead(ctx: ChannelHandlerContext, data: NIOAny) {
+        let reqPart = self.unwrapInboundIn(data)
+
+        switch reqPart {
+        case .head(let request):
+            switch request.uri {
+            case "/plaintext":
+                processPlaintext(ctx: ctx)
+            case "/json":
+                processJSON(ctx: ctx)
+            default:
+                _ = ctx.close()
+            }
+        case .body:
+            break
+        case .end:
+            _ = ctx.write(self.wrapOutboundOut(.end(nil)))
+        }
+    }
+
+    func channelReadComplete(ctx: ChannelHandlerContext) {
+        ctx.flush()
+        ctx.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 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.write(bytes: 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 responseHead(contentType: String, contentLength: String) -> HTTPResponseHead {
+        var headers = HTTPHeaders()
+        headers.replaceOrAdd(name: "content-type", value: contentType)
+        headers.replaceOrAdd(name: "content-length", value: contentLength)
+        headers.replaceOrAdd(name: "server", value: Constants.serverName)
+        headers.replaceOrAdd(name: "date", value: dateFormatter.getDate())
+
+        return HTTPResponseHead(version: Constants.httpVersion,
+                                status: .ok,
+                                headers: headers)
+    }
+}
+
+let group = MultiThreadedEventLoopGroup(numThreads: System.coreCount)
+let bootstrap = ServerBootstrap(group: group)
+    .serverChannelOption(ChannelOptions.backlog, value: 8192)
+    .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
+    .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_TCP), TCP_NODELAY), value: 1)
+
+    .childChannelInitializer { channel in
+        channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: false).then {
+            channel.pipeline.add(handler: HTTPHandler())
+        }
+    }
+
+    .childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
+    .childChannelOption(ChannelOptions.maxMessagesPerRead, value: 16)
+    .childChannelOption(ChannelOptions.recvAllocator, value: AdaptiveRecvByteBufferAllocator())
+
+defer {
+    try! group.syncShutdownGracefully()
+}
+
+let channel = try! bootstrap.bind(host: "0.0.0.0", port: 8080).wait()
+
+try! channel.closeFuture.wait()

+ 24 - 0
frameworks/Swift/swift-nio/benchmark_config.json

@@ -0,0 +1,24 @@
+{
+  "framework": "swift-nio",
+  "tests": [{
+    "default": {
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "None",
+      "framework": "None",
+      "language": "Swift",
+      "flavor": "None",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "SwiftNIO",
+      "notes": "",
+      "versus": ""
+    }
+  }]
+}

+ 7 - 0
frameworks/Swift/swift-nio/swift-nio.dockerfile

@@ -0,0 +1,7 @@
+FROM techempower/swift:0.1
+
+ADD ./ /swift-nio
+WORKDIR /swift-nio
+RUN swift build -c release
+
+CMD .build/release/swift-nio-tfb-default

+ 2 - 2
toolset/setup/docker/languages/swift.dockerfile

@@ -4,7 +4,7 @@ RUN apt-get install -y clang
 
 RUN mkdir /swift
 WORKDIR /swift
-RUN curl -sL https://swift.org/builds/swift-4.0.3-release/ubuntu1604/swift-4.0.3-RELEASE/swift-4.0.3-RELEASE-ubuntu16.04.tar.gz | tar xz
+RUN curl -sL https://swift.org/builds/swift-4.1-release/ubuntu1604/swift-4.1-RELEASE/swift-4.1-RELEASE-ubuntu16.04.tar.gz | tar xz
 
-ENV SWIFT_HOME /swift/swift-4.0.3-RELEASE-ubuntu16.04
+ENV SWIFT_HOME /swift/swift-4.1-RELEASE-ubuntu16.04
 ENV PATH ${SWIFT_HOME}/usr/bin:${PATH}

+ 3 - 4
toolset/setup/linux/languages/swift.sh

@@ -11,10 +11,9 @@ sudo apt-add-repository --yes ppa:george-edison55/cmake-3.x
 sudo apt-get update -qq
 sudo apt-get install -qqy cmake
 
-# TODO: Use Swift 4.1 stable once it's released.
-fw_get -O https://swift.org/builds/development/ubuntu1404/swift-DEVELOPMENT-SNAPSHOT-2018-01-30-a/swift-DEVELOPMENT-SNAPSHOT-2018-01-30-a-ubuntu14.04.tar.gz
-fw_untar swift-DEVELOPMENT-SNAPSHOT-2018-01-30-a-ubuntu14.04.tar.gz
-mv swift-DEVELOPMENT-SNAPSHOT-2018-01-30-a-ubuntu14.04 swift
+fw_get -O https://swift.org/builds/swift-4.1-release/ubuntu1404/swift-4.1-RELEASE/swift-4.1-RELEASE-ubuntu14.04.tar.gz
+fw_untar swift-4.1-RELEASE-ubuntu14.04.tar.gz
+mv swift-4.1-RELEASE-ubuntu14.04 swift
 
 echo -e "export PATH=${IROOT}/swift/usr/bin:\$PATH" >> $IROOT/swift.installed