diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/jaegertracing/thrift/lib/swift | |
parent | Initial commit. (diff) | |
download | ceph-upstream/16.2.11+ds.tar.xz ceph-upstream/16.2.11+ds.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
38 files changed, 4897 insertions, 0 deletions
diff --git a/src/jaegertracing/thrift/lib/swift/Makefile.am b/src/jaegertracing/thrift/lib/swift/Makefile.am new file mode 100644 index 000000000..6b88b06a7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Makefile.am @@ -0,0 +1,46 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +SUBDIRS = . + +all-local: + swift build --configuration release + +install-exec-hook: + swift install + +clean-local: + swift package clean + rm -rf .build + +precross: + swift + +check-local: + swift test + +EXTRA_DIST = \ + Package.swift \ + Sources \ + Tests \ + README.md + +MAINTAINERCLEANFILES = \ + Makefile \ + Makefile.in diff --git a/src/jaegertracing/thrift/lib/swift/Package.swift b/src/jaegertracing/thrift/lib/swift/Package.swift new file mode 100644 index 000000000..b533f6086 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Package.swift @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import PackageDescription + +let package = Package( + name: "Thrift" +) diff --git a/src/jaegertracing/thrift/lib/swift/README.md b/src/jaegertracing/thrift/lib/swift/README.md new file mode 100644 index 000000000..6f1096184 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/README.md @@ -0,0 +1,215 @@ +Thrift Swift Library +========================= + +License +------- +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. + + +## Build + swift build + +## Test + swift test + +## Install Library +##### Cocoapods +Add the following to your podfile +```ruby + pod 'Thrift-swift3', :git => 'git@github.com:apache/thrift.git', :branch => 'master' +``` + +##### SPM +Unfortunately due to some limitations in SPM, the Package manifest and Sources directory must be at the root of the project. +To get around that for the time being, you can use this mirrored repo. +Add the following to your Package.swift +```swift +dependencies: [ + .Package(url: "https://github.com/apocolipse/Thrift-Swift.git", majorVersion: 1) +] +``` + +## Thrift Compiler + +You can compile IDL sources for Swift 3 with the following command: + + thrift --gen swift thrift_file + +## Client Example +```swift +let transport = TSocketTransport(hostname: "localhost", port: 9090)! + +// var proto = TCompactProtocol(transport: transport) +let proto = TBinaryProtocol(on: transport) +// var client = HermesClient(inoutProtocol: proto) +let client = ThriftTestClient(inoutProtocol: proto) +do { + try client.testVoid() +} catch let error { + print("\(error)") +} +``` + +## Library Notes +- Eliminated Protocol Factories, They were only used in async clients and server implementations, where Generics provide a better alternative. +- Swifty Errors, All `TError` types have a nested `ErrorCode` Enum as well as some extra flavor where needed. +- Value typed everything. `TTransport` operates on value typed `Data` rather than reference typed `NSData` or `UnsafeBufferPointer`s +- Swift 3 Named protocols. Swift 3 naming conventions suggest the elimination of redundant words that can be inferred from variable/function signatures. This renaming is applied throughout the Swift 3 library converting most naming conventions used in the Swift2/Cocoa library to Swift 3-esque naming conventions. eg. +```swift +func readString() throws -> String +func writeString(_ val: String) throws +``` +have been renamed to eliminate redundant words: +```swift +func read() throws -> String +func write(_ val: String) throws +``` + +- Eliminated `THTTPTransport` that uses `NSURLConnection` due to it being deprecated and not available at all in Swift 3 for Linux. `THTTPSessionTransport` from the Swift2/Cocoa library that uses `NSURLSession` has been renamed to `THTTPTransport` for this library and leverages `URLSession`, providing both synchronous (with semaphores) and asynchronous behavior. +- Probably some More things I've missed here. + +## Generator Notes +#### Generator Flags +| Flag | Description | +| ------------- |:-------------:| +| async_clients | Generate clients which invoke asynchronously via block syntax. Asynchronous classes are appended with `_Async` | +| no_strict* | Generates non-strict structs | +| debug_descriptions | Allow use of debugDescription so the app can add description via a cateogory/extension | +| log_unexpected | Log every time an unexpected field ID or type is encountered. | +| safe_enums | Generate enum types with an unknown case to handle unspecified values rather than throw a serialization error | + + + +*Most thrift libraries allow empty initialization of Structs, initializing `required` fields with nil/null/None (Python and Node generators). Swift on the other hand requires initializers to initialize all non-Optional fields, and thus the Swift 3 generator does not provide default values (unlike the Swift 2/Cocoa generator). In other languages, this allows the sending of NULL values in fields that are marked `required`, and thus will throw an error in Swift clients attempting to validate fields. The `no_strict` option here will ignore the validation check, as well as behave similar to the Swift2/Cocoa generator and initialize required fields with empty initializers (where possible). + + +## Whats implemented +#### Library +##### Transports +- [x] TSocketTransport - CFSocket and PosixSocket variants available. CFSocket variant only currently available for Darwin platforms +- [x] THTTPTransport - Currently only available for Darwin platforms, Swift Foundation URLSession implementation needs completion on linux. +- [x] TSocketServer - Uses CFSockets only for binding, should be working on linux +- [x] TFramedTransport +- [x] TMemoryBufferTransport +- [x] TFileTransport - A few variants using File handles and file descriptors. +- [x] TStreamTransport - Fully functional in Darwin, Foundation backing not yet completed in Linux (This limits TCFSocketTransport to Darwin) +- [ ] HTTPServer - Currently there is no lightweight HTTPServer implementation the Swift Standard Library, so other 3rd party alternatives are required and out of scope for the Thrift library. Examples using Perfect will be provided. +- [ ] Other (gz, etc) + +##### Protocols +- [x] TBinaryProtocol +- [x] TCompactProtocol +- [ ] TJSONProtocol - This will need to be implemented + +##### Generator +- [x] Code Complete Generator +- [x] Async clients +- [x] Documentation Generation - Generator will transplant IDL docs to Swift code for easy lookup in Xcode +- [ ] Default Values - TODO +- [ ] no_strict mode - TODO +- [ ] Namespacing - Still haven't nailed down a good paradigm for namespacing. It will likely involve creating subdirectories for different namespaces and expecting the developer to import each subdirectory as separate modules. It could extend to creating SPM Package manifests with sub-modules within the generated module + + + +## Example HTTP Server with Perfect +```swift +import PerfectLib +import PerfectHTTP +import PerfectHTTPServer +import Dispatch + +let logQueue = DispatchQueue(label: "log", qos: .background, attributes: .concurrent) +let pQueue = DispatchQueue(label: "log", qos: .userInitiated, attributes: .concurrent) + + +class TPerfectServer<InProtocol: TProtocol, OutProtocol: TProtocol> { + + private var server = HTTPServer() + private var processor: TProcessor + + init(address: String? = nil, + path: String? = nil, + port: Int, + processor: TProcessor, + inProtocol: InProtocol.Type, + outProtocol: OutProtocol.Type) throws { + + self.processor = processor + + if let address = address { + server.serverAddress = address + } + server.serverPort = UInt16(port) + + var routes = Routes() + var uri = "/" + if let path = path { + uri += path + } + routes.add(method: .post, uri: uri) { request, response in + pQueue.async { + response.setHeader(.contentType, value: "application/x-thrift") + + let itrans = TMemoryBufferTransport() + if let bytes = request.postBodyBytes { + let data = Data(bytes: bytes) + itrans.reset(readBuffer: data) + } + + let otrans = TMemoryBufferTransport(flushHandler: { trans, buff in + let array = buff.withUnsafeBytes { + Array<UInt8>(UnsafeBufferPointer(start: $0, count: buff.count)) + } + response.status = .ok + response.setBody(bytes: array) + response.completed() + }) + + let inproto = InProtocol(on: itrans) + let outproto = OutProtocol(on: otrans) + + do { + try processor.process(on: inproto, outProtocol: outproto) + try otrans.flush() + } catch { + response.status = .badRequest + response.completed() + } + } + } + server.addRoutes(routes) + } + + func serve() throws { + try server.start() + } +} +``` + +#### Example Usage +```swift +class ServiceHandler : Service { + ... +} +let server = try? TPerfectServer(port: 9090, + processor: ServiceProcessor(service: ServiceHandler()), + inProtocol: TBinaryProtocol.self, + outProtocol: TBinaryProtocol.self) + +try? server?.serve() +``` diff --git a/src/jaegertracing/thrift/lib/swift/Sources/LinuxHelper.swift b/src/jaegertracing/thrift/lib/swift/Sources/LinuxHelper.swift new file mode 100644 index 000000000..66d92bb4d --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/LinuxHelper.swift @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +import Foundation +import CoreFoundation + +#if os(Linux) +/// Extensions for Linux for incomplete Foundation API's. +/// swift-corelibs-foundation is not yet 1:1 with OSX/iOS Foundation + +extension CFSocketError { + public static let success = kCFSocketSuccess +} + +extension UInt { + public static func &(lhs: UInt, rhs: Int) -> UInt { + let cast = unsafeBitCast(rhs, to: UInt.self) + return lhs & cast + } +} + +#else +extension CFStreamPropertyKey { + static let shouldCloseNativeSocket = CFStreamPropertyKey(kCFStreamPropertyShouldCloseNativeSocket) + // Exists as Stream.PropertyKey.socketSecuritylevelKey but doesn't work with CFReadStreamSetProperty + static let socketSecurityLevel = CFStreamPropertyKey(kCFStreamPropertySocketSecurityLevel) + static let SSLSettings = CFStreamPropertyKey(kCFStreamPropertySSLSettings) +} +#endif diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TApplicationError.swift b/src/jaegertracing/thrift/lib/swift/Sources/TApplicationError.swift new file mode 100644 index 000000000..bc3939680 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TApplicationError.swift @@ -0,0 +1,157 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +public struct TApplicationError : TError { + public enum Code : TErrorCode { + case unknown + case unknownMethod(methodName: String?) + case invalidMessageType + case wrongMethodName(methodName: String?) + case badSequenceId + case missingResult(methodName: String?) + case internalError + case protocolError + case invalidTransform + case invalidProtocol + case unsupportedClientType + + + /// Initialize a TApplicationError with a Thrift error code + /// Normally this would be achieved with RawRepresentable however + /// by doing this we can allow for associated properties on enum cases for + /// case specific context data in a Swifty, type-safe manner. + /// + /// - parameter thriftErrorCode: Integer TApplicationError(exception) error code. + /// Default to 0 (.unknown) + public init(thriftErrorCode: Int) { + switch thriftErrorCode { + case 1: self = .unknownMethod(methodName: nil) + case 2: self = .invalidMessageType + case 3: self = .wrongMethodName(methodName: nil) + case 4: self = .badSequenceId + case 5: self = .missingResult(methodName: nil) + case 6: self = .internalError + case 7: self = .protocolError + case 8: self = .invalidProtocol + case 9: self = .invalidTransform + case 10: self = .unsupportedClientType + default: self = .unknown + } + } + public var thriftErrorCode: Int { + switch self { + case .unknown: return 0 + case .unknownMethod: return 1 + case .invalidMessageType: return 2 + case .wrongMethodName: return 3 + case .badSequenceId: return 4 + case .missingResult: return 5 + case .internalError: return 6 + case .protocolError: return 7 + case .invalidProtocol: return 8 + case .invalidTransform: return 9 + case .unsupportedClientType: return 10 + } + } + + public var description: String { + /// Output "for #methodName" if method is not nil else empty + let methodUnwrap: (String?) -> String = { method in + return "\(method == nil ? "" : " for \(method ?? "")")" + } + switch self { + case .unknown: return "Unknown TApplicationError" + case .unknownMethod(let method): return "Unknown Method\(methodUnwrap(method))" + case .invalidMessageType: return "Invalid Message Type" + case .wrongMethodName(let method): return "Wrong Method Name\(methodUnwrap(method))" + case .badSequenceId: return "Bad Sequence ID" + case .missingResult(let method): return "Missing Result\(methodUnwrap(method))" + case .internalError: return "Internal Error" + case .protocolError: return "Protocol Error" + case .invalidProtocol: return "Invalid Protocol" + case .invalidTransform: return "Invalid Transform" + case .unsupportedClientType: return "Unsupported Client Type" + } + } + } + + public init() { } + + public init(thriftErrorCode code: Int, message: String? = nil) { + self.error = Code(thriftErrorCode: code) + self.message = message + } + + public var error: Code = .unknown + public var message: String? = nil + public static var defaultCase: Code { return .unknown } +} + +extension TApplicationError : TSerializable { + public static var thriftType: TType { return .struct } + + public static func read(from proto: TProtocol) throws -> TApplicationError { + var errorCode: Int = 0 + var message: String? = nil + _ = try proto.readStructBegin() + fields: while true { + let (_, fieldType, fieldID) = try proto.readFieldBegin() + + switch (fieldID, fieldType) { + case (_, .stop): + break fields + case (1, .string): + message = try proto.read() + case (2, .i32): + errorCode = Int(try proto.read() as Int32) + + case let (_, unknownType): + try proto.skip(type: unknownType) + } + + try proto.readFieldEnd() + } + try proto.readStructEnd() + return TApplicationError(thriftErrorCode: errorCode, message: message) + } + + public func write(to proto: TProtocol) throws { + try proto.writeStructBegin(name: "TApplicationException") + + try proto.writeFieldBegin(name: "message", type: .string, fieldID: 1) + try proto.write(message ?? "") + try proto.writeFieldEnd() + + try proto.writeFieldBegin(name: "type", type: .i32, fieldID: 2) + let val = Int32(error.thriftErrorCode) + try proto.write(val) + try proto.writeFieldEnd() + try proto.writeFieldStop() + try proto.writeStructEnd() + } + + public var hashValue: Int { + return error.thriftErrorCode &+ (message?.hashValue ?? 0) + } +} + +public func ==(lhs: TApplicationError, rhs: TApplicationError) -> Bool { + return lhs.error.thriftErrorCode == rhs.error.thriftErrorCode && lhs.message == rhs.message +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TBinary.swift b/src/jaegertracing/thrift/lib/swift/Sources/TBinary.swift new file mode 100644 index 000000000..4be56441a --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TBinary.swift @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Foundation + +extension Data : TSerializable { + public static var thriftType: TType { return .string } + + public static func read(from proto: TProtocol) throws -> Data { + return try proto.read() as Data + } + + public func write(to proto: TProtocol) throws { + try proto.write(self) + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TBinaryProtocol.swift b/src/jaegertracing/thrift/lib/swift/Sources/TBinaryProtocol.swift new file mode 100644 index 000000000..85acce045 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TBinaryProtocol.swift @@ -0,0 +1,384 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Foundation + +public struct TBinaryProtocolVersion { + static let version1 = Int32(bitPattern: 0x80010000) + static let versionMask = Int32(bitPattern: 0xffff0000) +} + +public class TBinaryProtocol: TProtocol { + public var messageSizeLimit: UInt32 = 0 + + public var transport: TTransport + + // class level properties for setting global config (useful for server in lieu of Factory design) + public static var strictRead: Bool = false + public static var strictWrite: Bool = true + + private var strictRead: Bool + private var strictWrite: Bool + + var currentMessageName: String? + var currentFieldName: String? + + + public convenience init(transport: TTransport, strictRead: Bool, strictWrite: Bool) { + self.init(on: transport) + self.strictRead = strictRead + self.strictWrite = strictWrite + } + + public required init(on transport: TTransport) { + self.transport = transport + self.strictWrite = TBinaryProtocol.strictWrite + self.strictRead = TBinaryProtocol.strictRead + } + + func readStringBody(_ size: Int) throws -> String { + + var data = Data() + try ProtocolTransportTry(error: TProtocolError(message: "Transport read failed")) { + data = try self.transport.readAll(size: size) + } + + return String(data: data, encoding: String.Encoding.utf8) ?? "" + } + + /// Mark: - TProtocol + + public func readMessageBegin() throws -> (String, TMessageType, Int32) { + let size: Int32 = try read() + var messageName = "" + var type = TMessageType.exception + + if size < 0 { + let version = size & TBinaryProtocolVersion.versionMask + if version != TBinaryProtocolVersion.version1 { + throw TProtocolError(error: .badVersion(expected: "\(TBinaryProtocolVersion.version1)", + got: "\(version)")) + } + type = TMessageType(rawValue: Int32(size) & 0x00FF) ?? type + messageName = try read() + } else { + if strictRead { + let errorMessage = "Missing message version, old client? Message Name: \(currentMessageName ?? "")" + throw TProtocolError(error: .invalidData, + message: errorMessage) + } + if messageSizeLimit > 0 && size > Int32(messageSizeLimit) { + throw TProtocolError(error: .sizeLimit(limit: Int(messageSizeLimit), got: Int(size))) + } + + messageName = try readStringBody(Int(size)) + type = TMessageType(rawValue: Int32(try read() as UInt8)) ?? type + } + + let seqID: Int32 = try read() + return (messageName, type, seqID) + } + + public func readMessageEnd() throws { + return + } + + public func readStructBegin() throws -> String { + return "" + } + + public func readStructEnd() throws { + return + } + + public func readFieldBegin() throws -> (String, TType, Int32) { + + let fieldType = TType(rawValue: Int32(try read() as UInt8)) ?? TType.stop + var fieldID: Int32 = 0 + + if fieldType != .stop { + fieldID = Int32(try read() as Int16) + } + + return ("", fieldType, fieldID) + } + + public func readFieldEnd() throws { + return + } + + public func readMapBegin() throws -> (TType, TType, Int32) { + var raw = Int32(try read() as UInt8) + guard let keyType = TType(rawValue: raw) else { + throw TProtocolError(message: "Unknown value for keyType TType: \(raw)") + } + + raw = Int32(try read() as UInt8) + guard let valueType = TType(rawValue: raw) else { + throw TProtocolError(message: "Unknown value for valueType TType: \(raw)") + } + let size: Int32 = try read() + + return (keyType, valueType, size) + } + + public func readMapEnd() throws { + return + } + + public func readSetBegin() throws -> (TType, Int32) { + let raw = Int32(try read() as UInt8) + guard let elementType = TType(rawValue: raw) else { + throw TProtocolError(message: "Unknown value for elementType TType: \(raw)") + } + + let size: Int32 = try read() + + return (elementType, size) + } + + public func readSetEnd() throws { + return + } + + public func readListBegin() throws -> (TType, Int32) { + let raw = Int32(try read() as UInt8) + guard let elementType = TType(rawValue: raw) else { + throw TProtocolError(message: "Unknown value for elementType TType: \(raw)") + } + let size: Int32 = try read() + + return (elementType, size) + } + + public func readListEnd() throws { + return + } + + public func read() throws -> String { + let data: Data = try read() + guard let str = String.init(data: data, encoding: .utf8) else { + throw TProtocolError(error: .invalidData, message: "Couldn't encode UTF-8 from data read") + } + return str + } + + public func read() throws -> Bool { + return (try read() as UInt8) == 1 + } + + public func read() throws -> UInt8 { + var buff = Data() + try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { + buff = try self.transport.readAll(size: 1) + } + return buff[0] + } + + public func read() throws -> Int16 { + var buff = Data() + try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { + buff = try self.transport.readAll(size: 2) + } + var ret = Int16(buff[0] & 0xff) << 8 + ret |= Int16(buff[1] & 0xff) + return ret + } + + public func read() throws -> Int32 { + var buff = Data() + try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { + buff = try self.transport.readAll(size: 4) + } + var ret = Int32(buff[0] & 0xff) << 24 + ret |= Int32(buff[1] & 0xff) << 16 + ret |= Int32(buff[2] & 0xff) << 8 + ret |= Int32(buff[3] & 0xff) + + return ret + } + + public func read() throws -> Int64 { + var buff = Data() + try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { + buff = try self.transport.readAll(size: 8) + } + var ret = Int64(buff[0] & 0xff) << 56 + ret |= Int64(buff[1] & 0xff) << 48 + ret |= Int64(buff[2] & 0xff) << 40 + ret |= Int64(buff[3] & 0xff) << 32 + ret |= Int64(buff[4] & 0xff) << 24 + ret |= Int64(buff[5] & 0xff) << 16 + ret |= Int64(buff[6] & 0xff) << 8 + ret |= Int64(buff[7] & 0xff) + + return ret + } + + public func read() throws -> Double { + let val = try read() as Int64 + return Double(bitPattern: UInt64(bitPattern: val)) + } + + public func read() throws -> Data { + let size = Int(try read() as Int32) + var data = Data() + try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { + data = try self.transport.readAll(size: size) + } + + return data + } + + // Write methods + + public func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws { + if strictWrite { + let version = TBinaryProtocolVersion.version1 | Int32(messageType.rawValue) + try write(version) + try write(name) + try write(sequenceID) + } else { + try write(name) + try write(UInt8(messageType.rawValue)) + try write(sequenceID) + } + currentMessageName = name + } + + public func writeMessageEnd() throws { + currentMessageName = nil + } + + public func writeStructBegin(name: String) throws { + return + } + + public func writeStructEnd() throws { + return + } + + public func writeFieldBegin(name: String, type fieldType: TType, fieldID: Int32) throws { + try write(UInt8(fieldType.rawValue)) + try write(Int16(fieldID)) + } + + public func writeFieldStop() throws { + try write(UInt8(TType.stop.rawValue)) + } + + public func writeFieldEnd() throws { + return + } + + public func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws { + try write(UInt8(keyType.rawValue)) + try write(UInt8(valueType.rawValue)) + try write(size) + } + + public func writeMapEnd() throws { + return + } + + public func writeSetBegin(elementType: TType, size: Int32) throws { + try write(UInt8(elementType.rawValue)) + try write(size) + } + + public func writeSetEnd() throws { + return + } + + public func writeListBegin(elementType: TType, size: Int32) throws { + try write(UInt8(elementType.rawValue)) + try write(size) + } + + public func writeListEnd() throws { + return + } + + public func write(_ value: String) throws { + try write(value.data(using: .utf8)!) + } + + public func write(_ value: Bool) throws { + let byteVal: UInt8 = value ? 1 : 0 + try write(byteVal) + } + + public func write(_ value: UInt8) throws { + let buff = Data([value]) + + try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { + try self.transport.write(data: buff) + } + } + + public func write(_ value: Int16) throws { + var buff = Data() + buff.append(Data([UInt8(0xff & (value >> 8))])) + buff.append(Data([UInt8(0xff & (value))])) + try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { + try self.transport.write(data: buff) + } + } + + public func write(_ value: Int32) throws { + var buff = Data() + buff.append(Data([UInt8(0xff & (value >> 24))])) + buff.append(Data([UInt8(0xff & (value >> 16))])) + buff.append(Data([UInt8(0xff & (value >> 8))])) + buff.append(Data([UInt8(0xff & (value))])) + + try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { + try self.transport.write(data: buff) + } + } + + public func write(_ value: Int64) throws { + var buff = Data() + buff.append(Data([UInt8(0xff & (value >> 56))])) + buff.append(Data([UInt8(0xff & (value >> 48))])) + buff.append(Data([UInt8(0xff & (value >> 40))])) + buff.append(Data([UInt8(0xff & (value >> 32))])) + buff.append(Data([UInt8(0xff & (value >> 24))])) + buff.append(Data([UInt8(0xff & (value >> 16))])) + buff.append(Data([UInt8(0xff & (value >> 8))])) + buff.append(Data([UInt8(0xff & (value))])) + + try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { + try self.transport.write(data: buff) + } + } + + public func write(_ value: Double) throws { + // Notably unsafe, since Double and Int64 are the same size, this should work fine + try self.write(Int64(bitPattern: value.bitPattern)) + } + + public func write(_ data: Data) throws { + try write(Int32(data.count)) + + try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { + try self.transport.write(data: data) + } + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TClient.swift b/src/jaegertracing/thrift/lib/swift/Sources/TClient.swift new file mode 100644 index 000000000..cc3288a8c --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TClient.swift @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +open class TClient { + public let inProtocol: TProtocol + public let outProtocol: TProtocol + + required public init(inoutProtocol: TProtocol) { + self.inProtocol = inoutProtocol + self.outProtocol = inoutProtocol + } + + required public init(inProtocol: TProtocol, outProtocol: TProtocol) { + self.inProtocol = inProtocol + self.outProtocol = outProtocol + } +} + + +open class TAsyncClient<Protocol: TProtocol, Factory: TAsyncTransportFactory> { + public var factory: Factory + public init(with protocol: Protocol.Type, factory: Factory) { + self.factory = factory + } +} + + +public enum TAsyncResult<T> { + case success(T) + case error(Swift.Error) + + public func value() throws -> T { + switch self { + case .success(let t): return t + case .error(let e): throw e + } + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TCompactProtocol.swift b/src/jaegertracing/thrift/lib/swift/Sources/TCompactProtocol.swift new file mode 100644 index 000000000..5b302d38f --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TCompactProtocol.swift @@ -0,0 +1,571 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import Foundation +import CoreFoundation + +public enum TCType: UInt8 { + case stop = 0x00 + case boolean_TRUE = 0x01 + case boolean_FALSE = 0x02 + case i8 = 0x03 + case i16 = 0x04 + case i32 = 0x05 + case i64 = 0x06 + case double = 0x07 + case binary = 0x08 + case list = 0x09 + case set = 0x0A + case map = 0x0B + case `struct` = 0x0C + + public static let typeMask: UInt8 = 0xE0 // 1110 0000 + public static let typeBits: UInt8 = 0x07 // 0000 0111 + public static let typeShiftAmount = 5 + +} + + +public class TCompactProtocol: TProtocol { + public static let protocolID: UInt8 = 0x82 + public static let version: UInt8 = 1 + public static let versionMask: UInt8 = 0x1F // 0001 1111 + + public var transport: TTransport + + var lastField: [UInt8] = [] + var lastFieldId: UInt8 = 0 + + var boolFieldName: String? + var boolFieldType: TType? + var boolFieldId: Int32? + var booleanValue: Bool? + + var currentMessageName: String? + + public required init(on transport: TTransport) { + self.transport = transport + } + + + /// Mark: - TCompactProtocol helpers + + func writebyteDirect(_ byte: UInt8) throws { + let byte = Data([byte]) + try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) { + try self.transport.write(data: byte) + } + } + + func writeVarint32(_ val: UInt32) throws { + var val = val + var i32buf = [UInt8](repeating: 0, count: 5) + var idx = 0 + while true { + if (val & ~0x7F) == 0 { + i32buf[idx] = UInt8(val) + idx += 1 + break + } else { + i32buf[idx] = UInt8((val & 0x7F) | 0x80) + idx += 1 + val >>= 7 + } + } + + try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) { + try self.transport.write(data: Data(i32buf[0..<idx])) + } + } + + func writeVarint64(_ val: UInt64) throws { + var val = val + var varint64out = [UInt8](repeating: 0, count: 10) + var idx = 0 + while true { + if (val & ~0x7F) == 0{ + varint64out[idx] = UInt8(val) + idx += 1 + break + } else { + varint64out[idx] = UInt8(val & 0x7F) | 0x80 + idx += 1 + val >>= 7 + } + } + + try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) { + try self.transport.write(data: Data(varint64out[0..<idx])) + } + } + + func writeCollectionBegin(_ elementType: TType, size: Int32) throws { + let ctype = compactType(elementType).rawValue + if size <= 14 { + try writebyteDirect(UInt8(size << 4) | ctype) + } else { + try writebyteDirect(0xF0 | ctype) + try writeVarint32(UInt32(size)) + } + } + + func readBinary(_ size: Int) throws -> Data { + var result = Data() + if size != 0 { + try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { + result = try self.transport.readAll(size: size) + } + } + return result + } + + func readVarint32() throws -> UInt32 { + var result: UInt32 = 0 + var shift: UInt32 = 0 + while true { + let byte: UInt8 = try read() + + result |= UInt32(byte & 0x7F) << shift + if (byte & 0x80) == 0 { + break + } + + shift += 7 + } + + return result + } + + func readVarint64() throws -> UInt64 { + var result: UInt64 = 0 + var shift: UInt64 = 0 + + while true { + let byte: UInt8 = try read() + + result |= UInt64(byte & 0x7F) << shift + if (byte & 0x80) == 0 { + break + } + + shift += 7 + } + return result + } + + + func ttype(_ compactTypeVal: UInt8) throws -> TType { + guard let compactType = TCType(rawValue: compactTypeVal) else { + throw TProtocolError(message: "Unknown TCType value: \(compactTypeVal)") + } + + switch compactType { + case .stop: return .stop; + case .boolean_FALSE, .boolean_TRUE: return .bool; + case .i8: return .i8; + case .i16: return .i16; + case .i32: return .i32; + case .i64: return .i64; + case .double: return .double; + case .binary: return .string; + case .list: return .list; + case .set: return .set; + case .map: return .map; + case .struct: return .struct; + } + } + + func compactType(_ ttype: TType) -> TCType { + switch ttype { + case .stop: return .stop + case .void: return .i8 + case .bool: return .boolean_FALSE + case .i8: return .i8 + case .double: return .double + case .i16: return .i16 + case .i32: return .i32 + case .i64: return .i64 + case .string: return .binary + case .struct: return .struct + case .map: return .map + case .set: return .set + case .list: return .list + case .utf8: return .binary + case .utf16: return .binary + } + } + + /// ZigZag encoding maps signed integers to unsigned integers so that + /// numbers with a small absolute value (for instance, -1) have + /// a small varint encoded value too. It does this in a way that + /// "zig-zags" back and forth through the positive and negative integers, + /// so that -1 is encoded as 1, 1 is encoded as 2, -2 is encoded as 3, and so + /// + /// - parameter n: number to zigzag + /// + /// - returns: zigzaged UInt32 + func i32ToZigZag(_ n : Int32) -> UInt32 { + return UInt32(bitPattern: Int32(n << 1) ^ Int32(n >> 31)) + } + + func i64ToZigZag(_ n : Int64) -> UInt64 { + return UInt64(bitPattern: Int64(n << 1) ^ Int64(n >> 63)) + } + + func zigZagToi32(_ n: UInt32) -> Int32 { + return Int32(n >> 1) ^ (-Int32(n & 1)) + } + + func zigZagToi64(_ n: UInt64) -> Int64 { + return Int64(n >> 1) ^ (-Int64(n & 1)) + } + + + + /// Mark: - TProtocol + + public func readMessageBegin() throws -> (String, TMessageType, Int32) { + let protocolId: UInt8 = try read() + + if protocolId != TCompactProtocol.protocolID { + let expected = String(format:"%2X", TCompactProtocol.protocolID) + let got = String(format:"%2X", protocolId) + throw TProtocolError(message: "Wrong Protocol ID \(got)", + extendedError: .mismatchedProtocol(expected: expected, got: got)) + } + + let versionAndType: UInt8 = try read() + let version: UInt8 = versionAndType & TCompactProtocol.versionMask + if version != TCompactProtocol.version { + throw TProtocolError(error: .badVersion(expected: "\(TCompactProtocol.version)", + got:"\(version)")) + } + + let type = (versionAndType >> UInt8(TCType.typeShiftAmount)) & TCType.typeBits + guard let mtype = TMessageType(rawValue: Int32(type)) else { + throw TProtocolError(message: "Unknown TMessageType value: \(type)") + } + let sequenceId = try readVarint32() + let name: String = try read() + + return (name, mtype, Int32(sequenceId)) + } + + public func readMessageEnd() throws { } + + public func readStructBegin() throws -> String { + lastField.append(lastFieldId) + lastFieldId = 0 + return "" + } + + public func readStructEnd() throws { + lastFieldId = lastField.last ?? 0 + lastField.removeLast() + } + + public func readFieldBegin() throws -> (String, TType, Int32) { + let byte: UInt8 = try read() + guard let type = TCType(rawValue: byte & 0x0F) else { + throw TProtocolError(message: "Unknown TCType \(byte & 0x0F)") + } + + // if it's a stop, then we can return immediately, as the struct is over + if type == .stop { + return ("", .stop, 0) + } + + var fieldId: Int16 = 0 + + // mask off the 4MSB of the type header. it could contain a field id delta + let modifier = (byte & 0xF0) >> 4 + if modifier == 0 { + // not a delta. look ahead for the zigzag varint field id + fieldId = try read() + } else { + // has a delta. add the delta to the last Read field id. + fieldId = Int16(lastFieldId + modifier) + } + + let fieldType = try ttype(type.rawValue) + + // if this happens to be a boolean field, the value is encoded in the type + if type == .boolean_TRUE || type == .boolean_FALSE { + // save the boolean value in a special instance variable + booleanValue = type == .boolean_TRUE + } + + // push the new field onto the field stack so we can keep the deltas going + lastFieldId = UInt8(fieldId) + return ("", fieldType, Int32(fieldId)) + } + + public func readFieldEnd() throws { } + + public func read() throws -> String { + let length = try readVarint32() + + var result: String + + if length != 0 { + let data = try readBinary(Int(length)) + result = String(data: data, encoding: String.Encoding.utf8) ?? "" + } else { + result = "" + } + + return result + } + + public func read() throws -> Bool { + if let val = booleanValue { + self.booleanValue = nil + return val + } else { + let result = try read() as UInt8 + return TCType(rawValue: result) == .boolean_TRUE + } + } + + public func read() throws -> UInt8 { + var buff: UInt8 = 0 + try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { + buff = try self.transport.readAll(size: 1)[0] + } + return buff + } + + public func read() throws -> Int16 { + let v = try readVarint32() + return Int16(zigZagToi32(v)) + } + + public func read() throws -> Int32 { + let v = try readVarint32() + return zigZagToi32(v) + } + + public func read() throws -> Int64 { + let v = try readVarint64() + return zigZagToi64(v) + } + + public func read() throws -> Double { + var buff = Data() + try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { + buff = try self.transport.readAll(size: 8) + } + + let i64: UInt64 = buff.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> UInt64 in + return UnsafePointer<UInt64>(OpaquePointer(ptr)).pointee + } + let bits = CFSwapInt64LittleToHost(i64) + return Double(bitPattern: bits) + } + + public func read() throws -> Data { + let length = try readVarint32() + return try readBinary(Int(length)) + } + + public func readMapBegin() throws -> (TType, TType, Int32) { + var keyAndValueType: UInt8 = 8 + let size = try readVarint32() + if size != 0 { + keyAndValueType = try read() + } + + let keyType = try ttype(keyAndValueType >> 4) + let valueType = try ttype(keyAndValueType & 0xF) + + return (keyType, valueType, Int32(size)) + } + + public func readMapEnd() throws { } + + public func readSetBegin() throws -> (TType, Int32) { + return try readListBegin() + } + + public func readSetEnd() throws { } + + public func readListBegin() throws -> (TType, Int32) { + let sizeAndType: UInt8 = try read() + var size: UInt32 = UInt32(sizeAndType >> 4) & 0x0f + if size == 15 { + size = try readVarint32() + } + let elementType = try ttype(sizeAndType & 0x0F) + + return (elementType, Int32(size)) + } + + public func readListEnd() throws { } + + public func writeMessageBegin(name: String, + type messageType: TMessageType, + sequenceID: Int32) throws { + try writebyteDirect(TCompactProtocol.protocolID) + let nextByte: UInt8 = (TCompactProtocol.version & TCompactProtocol.versionMask) | + (UInt8((UInt32(messageType.rawValue) << UInt32(TCType.typeShiftAmount))) & + TCType.typeMask) + try writebyteDirect(nextByte) + try writeVarint32(UInt32(sequenceID)) + try write(name) + + currentMessageName = name + } + + public func writeMessageEnd() throws { + currentMessageName = nil + } + + public func writeStructBegin(name: String) throws { + lastField.append(lastFieldId) + lastFieldId = 0 + } + + public func writeStructEnd() throws { + lastFieldId = lastField.last ?? 0 + lastField.removeLast() + } + + public func writeFieldBegin(name: String, + type fieldType: TType, + fieldID: Int32) throws { + if fieldType == .bool { + boolFieldName = name + boolFieldType = fieldType + boolFieldId = fieldID + return + } else { + try writeFieldBeginInternal(name: name, + type: fieldType, + fieldID: fieldID, + typeOverride: 0xFF) + } + } + + func writeFieldBeginInternal(name: String, + type fieldType: TType, + fieldID: Int32, + typeOverride: UInt8) throws { + + let typeToWrite = typeOverride == 0xFF ? compactType(fieldType).rawValue : typeOverride + + // check if we can use delta encoding for the field id + let diff = UInt8(fieldID) - lastFieldId + if (UInt8(fieldID) > lastFieldId) && (diff <= 15) { + // Write them together + try writebyteDirect((UInt8(fieldID) - lastFieldId) << 4 | typeToWrite) + + } else { + // Write them separate + try writebyteDirect(typeToWrite) + try write(Int16(fieldID)) + } + + lastFieldId = UInt8(fieldID) + } + + public func writeFieldStop() throws { + try writebyteDirect(TCType.stop.rawValue) + } + + public func writeFieldEnd() throws { } + + public func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws { + if size == 0 { + try writebyteDirect(0) + } else { + try writeVarint32(UInt32(size)) + + let compactedTypes = compactType(keyType).rawValue << 4 | compactType(valueType).rawValue + try writebyteDirect(compactedTypes) + } + } + + public func writeMapEnd() throws { } + + public func writeSetBegin(elementType: TType, size: Int32) throws { + try writeCollectionBegin(elementType, size: size) + } + + public func writeSetEnd() throws { } + + public func writeListBegin(elementType: TType, size: Int32) throws { + try writeCollectionBegin(elementType, size: size) + } + + public func writeListEnd() throws { } + + public func write(_ value: String) throws { + try write(value.data(using: String.Encoding.utf8)!) + } + + public func write(_ value: Bool) throws { + if let boolFieldId = boolFieldId, let boolFieldType = boolFieldType, + let boolFieldName = boolFieldName { + + // we haven't written the field header yet + let compactType: TCType = value ? .boolean_TRUE : .boolean_FALSE + try writeFieldBeginInternal(name: boolFieldName, type: boolFieldType, fieldID: boolFieldId, + typeOverride: compactType.rawValue) + self.boolFieldId = nil + self.boolFieldType = nil + self.boolFieldName = nil + } else { + // we're not part of a field, so just write the value. + try writebyteDirect(value ? TCType.boolean_TRUE.rawValue : TCType.boolean_FALSE.rawValue) + } + } + + public func write(_ value: UInt8) throws { + try writebyteDirect(value) + } + + public func write(_ value: Int16) throws { + try writeVarint32(i32ToZigZag(Int32(value))) + } + + public func write(_ value: Int32) throws { + try writeVarint32(i32ToZigZag(value)) + } + + public func write(_ value: Int64) throws { + try writeVarint64(i64ToZigZag(value)) + } + + public func write(_ value: Double) throws { + var bits = CFSwapInt64HostToLittle(value.bitPattern) + let data = withUnsafePointer(to: &bits) { + return Data(bytes: UnsafePointer<UInt8>(OpaquePointer($0)), count: MemoryLayout<UInt64>.size) + } + try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) { + try self.transport.write(data: data) + } + } + + public func write(_ data: Data) throws { + try writeVarint32(UInt32(data.count)) + try ProtocolTransportTry(error: TProtocolError(message: "Transport Write Failed")) { + try self.transport.write(data: data) + } + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TEnum.swift b/src/jaegertracing/thrift/lib/swift/Sources/TEnum.swift new file mode 100644 index 000000000..fedfdb124 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TEnum.swift @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +public protocol TEnum : TSerializable, Hashable { + var rawValue: Int32 { get } +} + +extension TEnum { + public static var thriftType: TType { return .i32 } + public var hashValue: Int { return rawValue.hashValue } + + public func write(to proto: TProtocol) throws { + try proto.write(rawValue) + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TError.swift b/src/jaegertracing/thrift/lib/swift/Sources/TError.swift new file mode 100644 index 000000000..79edba602 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TError.swift @@ -0,0 +1,77 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +/// TErrorCode +/// +/// Protocol for TError conformers' enum's to conform to. +/// Generic Int Thrift error code to allow error cases to have +/// associated values. +public protocol TErrorCode : CustomStringConvertible { + var thriftErrorCode: Int { get } +} + +/// TError +/// +/// Base protocol for all Thrift Error(Exception) types to conform to +public protocol TError : Error, CustomStringConvertible { + + /// Enum for error cases. Can be typealiased to any conforming enum + /// or defined nested. + associatedtype Code: TErrorCode + + /// Error Case, value from internal enum + var error: Code { get set } + + /// Optional additional message + var message: String? { get set } + + /// Default error case for the error type, used for generic init() + static var defaultCase: Code { get } + + init() +} + +extension TError { + /// Human readable description of error. Default provided for you in the + /// format \(Self.self): \(error.errorDescription) \n message + /// eg: + /// + /// TApplicationError (1): Invalid Message Type + /// An unknown Error has occured. + public var description: String { + var out = "\(Self.self) (\(error.thriftErrorCode)): " + error.description + "\n" + if let message = message { + out += "Message: \(message)" + } + return out + } + + /// Simple default Initializer for TError's + /// + /// - parameter error: ErrorCode value. Default: defaultCase + /// - parameter message: Custom message with error. Optional + /// + /// - returns: <#return value description#> + public init(error: Code, message: String? = nil) { + self.init() + self.error = error + self.message = message + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TFileHandleTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TFileHandleTransport.swift new file mode 100644 index 000000000..f315fefda --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TFileHandleTransport.swift @@ -0,0 +1,56 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import Foundation + +public class TFileHandleTransport: TTransport { + var inputFileHandle: FileHandle + var outputFileHandle: FileHandle + + public init(inputFileHandle: FileHandle, outputFileHandle: FileHandle) { + self.inputFileHandle = inputFileHandle + self.outputFileHandle = outputFileHandle + } + + public convenience init(fileHandle: FileHandle) { + self.init(inputFileHandle: fileHandle, outputFileHandle: fileHandle) + } + + public func read(size: Int) throws -> Data { + var data = Data() + while data.count < size { + let read = inputFileHandle.readData(ofLength: size - data.count) + data.append(read) + if read.count == 0 { + break + } + } + return data + } + + public func write(data: Data) throws { + outputFileHandle.write(data) + } + + public func flush() throws { + return + } +} + + diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TFileTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TFileTransport.swift new file mode 100644 index 000000000..fe2253da4 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TFileTransport.swift @@ -0,0 +1,101 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import Foundation + +#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS) + import Darwin +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) + import Glibc +#endif + +/// TFileTransport +/// Foundation-less Swift File transport. +/// Uses C fopen/fread/fwrite, +/// provided by Glibc in linux and Darwin on OSX/iOS +public class TFileTransport: TTransport { + var fileHandle: UnsafeMutablePointer<FILE>? = nil + + public init (fileHandle: UnsafeMutablePointer<FILE>) { + self.fileHandle = fileHandle + } + + public convenience init(filename: String) throws { + var fileHandle: UnsafeMutablePointer<FILE>? + filename.withCString({ cFilename in + "rw".withCString({ cMode in + fileHandle = fopen(cFilename, cMode) + }) + }) + if let fileHandle = fileHandle { + self.init(fileHandle: fileHandle) + } else { + throw TTransportError(error: .notOpen) + } + } + + deinit { + fclose(self.fileHandle) + } + + public func readAll(size: Int) throws -> Data { + let read = try self.read(size: size) + + if read.count != size { + throw TTransportError(error: .endOfFile) + } + return read + } + + public func read(size: Int) throws -> Data { + // set up read buffer, position 0 + var read = Data(capacity: size) + var position = 0 + + // read character buffer + var nextChar: UInt8 = 0 + + // continue until we've read size bytes + while read.count < size { + if fread(&nextChar, 1, 1, self.fileHandle) == 1 { + read[position] = nextChar + + // Increment output byte pointer + position += 1 + + } else { + throw TTransportError(error: .endOfFile) + } + } + return read + } + + public func write(data: Data) throws { + let bytesWritten = data.withUnsafeBytes { + fwrite($0, 1, data.count, self.fileHandle) + } + if bytesWritten != data.count { + throw TTransportError(error: .unknown) + } + } + + public func flush() throws { + return + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TFramedTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TFramedTransport.swift new file mode 100644 index 000000000..59855eb9c --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TFramedTransport.swift @@ -0,0 +1,123 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import Foundation + +public class TFramedTransport: TTransport { + public static let headerSize = 4 + public static let initFrameSize = 1024 + private static let defaultMaxLength = 16384000 + + public var transport: TTransport + private var writeBuffer = Data() + + private var maxSize = TFramedTransport.defaultMaxLength + private var remainingBytes = 0 + + + public init(transport: TTransport, maxSize: Int) { + self.transport = transport + self.maxSize = maxSize + } + + public convenience init(transport: TTransport) { + self.init(transport: transport, maxSize: TFramedTransport.defaultMaxLength) + } + + func readHeader() throws { + let read = try transport.readAll(size: TFramedTransport.headerSize) + remainingBytes = Int(decodeFrameSize(data: read)) + } + + /// Mark: - TTransport + + public func read(size: Int) throws -> Data { + while (remainingBytes <= 0) { + try readHeader() + } + + let toRead = min(size, remainingBytes) + + if toRead < 0 { + try close() + throw TTransportError(error: .negativeSize, + message: "Read a negative frame size (\(toRead))!") + } + + if toRead > maxSize { + try close() + throw TTransportError(error: .sizeLimit(limit: maxSize, got: toRead)) + } + + return try transport.readAll(size: toRead) + } + + public func flush() throws { + // copy buffer and reset + let buff = writeBuffer + writeBuffer = Data() + + if buff.count - TFramedTransport.headerSize < 0 { + throw TTransportError(error: .unknown) + } + + let frameSize = encodeFrameSize(size: UInt32(buff.count)) + + try transport.write(data: frameSize) + try transport.write(data: buff) + try transport.flush() + } + + public func write(data: Data) throws { + writeBuffer.append(data) + } + + + + private func encodeFrameSize(size: UInt32) -> Data { + var data = Data() + data.append(Data([UInt8(0xff & (size >> 24))])) + data.append(Data([UInt8(0xff & (size >> 16))])) + data.append(Data([UInt8(0xff & (size >> 8))])) + data.append(Data([UInt8(0xff & (size))])) + + return data + } + + private func decodeFrameSize(data: Data) -> UInt32 { + var size: UInt32 + size = (UInt32(data[0] & 0xff) << 24) + size |= (UInt32(data[1] & 0xff) << 16) + size |= (UInt32(data[2] & 0xff) << 8) + size |= (UInt32(data[3] & 0xff)) + return size + } + + public func close() throws { + try transport.close() + } + + public func open() throws { + try transport.open() + } + + public func isOpen() throws -> Bool { + return try transport.isOpen() + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/THTTPSessionTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/THTTPSessionTransport.swift new file mode 100644 index 000000000..3c0af8eb8 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/THTTPSessionTransport.swift @@ -0,0 +1,184 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import Foundation +import Dispatch + + +public class THTTPSessionTransport: TAsyncTransport { + public class Factory : TAsyncTransportFactory { + public var responseValidate: ((HTTPURLResponse?, Data?) throws -> Void)? + + var session: URLSession + var url: URL + + public class func setupDefaultsForSessionConfiguration(_ config: URLSessionConfiguration, withProtocolName protocolName: String?) { + var thriftContentType = "application/x-thrift" + + if let protocolName = protocolName { + thriftContentType += "; p=\(protocolName)" + } + + config.requestCachePolicy = .reloadIgnoringLocalCacheData + config.urlCache = nil + + config.httpShouldUsePipelining = true + config.httpShouldSetCookies = true + config.httpAdditionalHeaders = ["Content-Type": thriftContentType, + "Accept": thriftContentType, + "User-Agent": "Thrift/Swift (Session)"] + + + } + + public init(session: URLSession, url: URL) { + self.session = session + self.url = url + } + + public func newTransport() -> THTTPSessionTransport { + return THTTPSessionTransport(factory: self) + } + + func validateResponse(_ response: HTTPURLResponse?, data: Data?) throws { + try responseValidate?(response, data) + } + + func taskWithRequest(_ request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> ()) throws -> URLSessionTask { + + let newTask: URLSessionTask? = session.dataTask(with: request, completionHandler: completionHandler) + if let newTask = newTask { + return newTask + } else { + throw TTransportError(error: .unknown, message: "Failed to create session data task") + } + } + } + + var factory: Factory + var requestData = Data() + var responseData = Data() + var responseDataOffset: Int = 0 + + init(factory: Factory) { + self.factory = factory + } + + public func readAll(size: Int) throws -> Data { + let read = try self.read(size: size) + if read.count != size { + throw TTransportError(error: .endOfFile) + } + return read + } + + public func read(size: Int) throws -> Data { + let avail = responseData.count - responseDataOffset + let (start, stop) = (responseDataOffset, responseDataOffset + min(size, avail)) + let read = responseData.subdata(in: start..<stop) + responseDataOffset += read.count + return read + } + + public func write(data: Data) throws { + requestData.append(data) + } + + public func flush(_ completed: @escaping (TAsyncTransport, Error?) -> Void) { + var error: Error? + var task: URLSessionTask? + + var request = URLRequest(url: factory.url) + request.httpMethod = "POST" + request.httpBody = requestData + + requestData = Data() + + do { + task = try factory.taskWithRequest(request, completionHandler: { (data, response, taskError) in + + // Check if there was an error with the network + if taskError != nil { + error = TTransportError(error: .timedOut) + completed(self, error) + return + } + + // Check response type + if taskError == nil && !(response is HTTPURLResponse) { + error = THTTPTransportError(error: .invalidResponse) + completed(self, error) + return + } + + // Check status code + if let httpResponse = response as? HTTPURLResponse { + if taskError == nil && httpResponse.statusCode != 200 { + if httpResponse.statusCode == 401 { + error = THTTPTransportError(error: .authentication) + } else { + error = THTTPTransportError(error: .invalidStatus(statusCode: httpResponse.statusCode)) + } + } + + // Allow factory to check + if error != nil { + do { + try self.factory.validateResponse(httpResponse, data: data) + } catch let validateError { + error = validateError + } + } + + self.responseDataOffset = 0 + if error != nil { + self.responseData = Data() + } else { + self.responseData = data ?? Data() + } + completed(self, error) + } + }) + + } catch let taskError { + error = taskError + } + + if let error = error, task == nil { + completed(self, error) + } + task?.resume() + } + + public func flush() throws { + let completed = DispatchSemaphore(value: 0) + var internalError: Error? + + flush() { _, error in + internalError = error + completed.signal() + } + + _ = completed.wait(timeout: DispatchTime.distantFuture) + + if let error = internalError { + throw error + } + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TList.swift b/src/jaegertracing/thrift/lib/swift/Sources/TList.swift new file mode 100644 index 000000000..c239d10c5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TList.swift @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +public struct TList<Element : TSerializable> : RandomAccessCollection, MutableCollection, ExpressibleByArrayLiteral, TSerializable, Hashable { + public typealias Storage = Array<Element> + public typealias Indices = Storage.Indices + + internal var storage = Storage() + public init() { } + public init(arrayLiteral elements: Element...) { + self.storage = Storage(elements) + } + public init<Source : Sequence>(_ sequence: Source) where Source.Iterator.Element == Element { + storage = Storage(sequence) + } + + /// Mark: Hashable + public var hashValue : Int { + let prime = 31 + var result = 1 + for element in storage { + result = prime &* result &+ element.hashValue + } + return result + } + + /// Mark: TSerializable + public static var thriftType : TType { return .list } + + public static func read(from proto: TProtocol) throws -> TList { + let (elementType, size) = try proto.readListBegin() + if elementType != Element.thriftType { + throw TProtocolError(error: .invalidData, + extendedError: .unexpectedType(type: elementType)) + } + var list = TList() + for _ in 0..<size { + let element = try Element.read(from: proto) + list.storage.append(element) + } + try proto.readListEnd() + return list + } + + public func write(to proto: TProtocol) throws { + try proto.writeListBegin(elementType: Element.thriftType, size: Int32(self.count)) + for element in self.storage { + try Element.write(element, to: proto) + } + try proto.writeListEnd() + } + + /// Mark: MutableCollection + + public typealias SubSequence = Storage.SubSequence + public typealias Index = Storage.Index + + public subscript(position: Storage.Index) -> Element { + get { + return storage[position] + } + set { + storage[position] = newValue + } + } + + public subscript(range: Range<Index>) -> SubSequence { + get { + return storage[range] + } + set { + storage[range] = newValue + } + } + + public var startIndex: Index { + return storage.startIndex + } + public var endIndex: Index { + return storage.endIndex + } + + public func formIndex(after i: inout Index) { + storage.formIndex(after: &i) + } + + public func formIndex(before i: inout Int) { + storage.formIndex(before: &i) + } + + public func index(after i: Index) -> Index { + return storage.index(after: i) + } + + public func index(before i: Int) -> Int { + return storage.index(before: i) + } + +} + +extension TList : RangeReplaceableCollection { + public mutating func replaceSubrange<C: Collection>(_ subrange: Range<Index>, with newElements: C) + where C.Iterator.Element == Element { + storage.replaceSubrange(subrange, with: newElements) + } +} + +extension TList : CustomStringConvertible, CustomDebugStringConvertible { + + public var description : String { + return storage.description + } + + public var debugDescription : String { + return storage.debugDescription + } + +} + +public func ==<Element>(lhs: TList<Element>, rhs: TList<Element>) -> Bool { + return lhs.storage.elementsEqual(rhs.storage) { $0 == $1 } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TMap.swift b/src/jaegertracing/thrift/lib/swift/Sources/TMap.swift new file mode 100644 index 000000000..dcf1481c6 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TMap.swift @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +public struct TMap<Key : TSerializable & Hashable, Value : TSerializable>: Collection, ExpressibleByDictionaryLiteral, Hashable, TSerializable { + public typealias Storage = Dictionary<Key, Value> + public typealias Element = Storage.Element + public typealias Index = Storage.Index + public typealias IndexDistance = Int + public typealias Indices = Storage.Indices + public typealias SubSequence = Storage.SubSequence + internal var storage = Storage() + + /// Mark: Be Like Dictionary + + public func indexForKey(_ key: Key) -> Index? { + return storage.index(forKey: key) + } + + public mutating func updateValue(_ value: Value, forKey key: Key) -> Value? { + return storage.updateValue(value, forKey: key) + } + + public mutating func removeValueForKey(_ key: Key) -> Value? { + return storage.removeValue(forKey: key) + } + + public init(minimumCapacity: Int) { + storage = Storage(minimumCapacity: minimumCapacity) + } + + /// init from Dictionary<K,V> + public init(_ dict: [Key: Value]) { + storage = dict + } + + /// read only access to storage if needed as Dictionary<K,V> + public var dictionary: [Key: Value] { + return storage + } + + public subscript (key: Key) -> Value? { + get { + return storage[key] + } + set { + storage[key] = newValue + } + } + + /// Mark: Collection + + public var indices: Indices { + return storage.indices + } + + public func distance(from start: Index, to end: Index) -> IndexDistance { + return storage.distance(from: start, to: end) + } + + public func index(_ i: Index, offsetBy n: IndexDistance) -> Index { + return storage.index(i, offsetBy: n) + } + + public func index(_ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index) -> Index? { + return storage.index(i, offsetBy: n, limitedBy: limit) + } + + public subscript(position: Index) -> Element { + return storage[position] + } + + /// Mark: IndexableBase + + public var startIndex: Index { return storage.startIndex } + public var endIndex: Index { return storage.endIndex } + public func index(after i: Index) -> Index { + return storage.index(after: i) + } + + public func formIndex(after i: inout Index) { + storage.formIndex(after: &i) + } + + public subscript(bounds: Range<Index>) -> SubSequence { + return storage[bounds] + } + + /// Mark: DictionaryLiteralConvertible + + public init(dictionaryLiteral elements: (Key, Value)...) { + storage = Storage() + for (key, value) in elements { + storage[key] = value + } + } + + /// Mark: Hashable + + public var hashValue: Int { + let prime = 31 + var result = 1 + for (key, value) in storage { + result = prime &* result &+ key.hashValue + result = prime &* result &+ value.hashValue + } + return result + } + + /// Mark: TSerializable + + public static var thriftType : TType { return .map } + public init() { + storage = Storage() + } + + public static func read(from proto: TProtocol) throws -> TMap { + + let (keyType, valueType, size) = try proto.readMapBegin() + if size > 0 { + if keyType != Key.thriftType { + throw TProtocolError(error: .invalidData, + message: "Unexpected TMap Key Type", + extendedError: .unexpectedType(type: keyType)) + } + if valueType != Value.thriftType { + throw TProtocolError(error: .invalidData, + message: "Unexpected TMap Value Type", + extendedError: .unexpectedType(type: valueType)) + } + } + + var map = TMap() + for _ in 0..<size { + let key = try Key.read(from: proto) + let value = try Value.read(from: proto) + map.storage[key] = value + } + try proto.readMapEnd() + return map + } + + public func write(to proto: TProtocol) throws { + try proto.writeMapBegin(keyType: Key.thriftType, + valueType: Value.thriftType, size: Int32(self.count)) + for (key, value) in self.storage { + try Key.write(key, to: proto) + try Value.write(value, to: proto) + } + try proto.writeMapEnd() + } +} + +/// Mark: CustomStringConvertible, CustomDebugStringConvertible + +extension TMap : CustomStringConvertible, CustomDebugStringConvertible { + + public var description : String { + return storage.description + } + + public var debugDescription : String { + return storage.debugDescription + } + +} + +/// Mark: Equatable + +public func ==<Key, Value>(lhs: TMap<Key,Value>, rhs: TMap<Key, Value>) -> Bool { + if lhs.count != rhs.count { + return false + } + return lhs.storage.elementsEqual(rhs.storage) { $0.key == $1.key && $0.value == $1.value } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TMemoryBufferTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TMemoryBufferTransport.swift new file mode 100644 index 000000000..bd58b6ef7 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TMemoryBufferTransport.swift @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Foundation + +public class TMemoryBufferTransport : TTransport { + public private(set) var readBuffer = Data() + public private(set) var writeBuffer = Data() + + public private(set) var position = 0 + + public var bytesRemainingInBuffer: Int { + return readBuffer.count - position + } + + public func consumeBuffer(size: Int) { + position += size + } + public func clear() { + readBuffer = Data() + writeBuffer = Data() + } + + + private var flushHandler: ((TMemoryBufferTransport, Data) -> ())? + + public init(flushHandler: ((TMemoryBufferTransport, Data) -> ())? = nil) { + self.flushHandler = flushHandler + } + + public convenience init(readBuffer: Data, flushHandler: ((TMemoryBufferTransport, Data) -> ())? = nil) { + self.init() + self.readBuffer = readBuffer + } + + public func reset(readBuffer: Data = Data(), writeBuffer: Data = Data()) { + self.readBuffer = readBuffer + self.writeBuffer = writeBuffer + } + + public func read(size: Int) throws -> Data { + let amountToRead = min(bytesRemainingInBuffer, size) + if amountToRead > 0 { + let ret = readBuffer.subdata(in: Range(uncheckedBounds: (lower: position, upper: position + amountToRead))) + position += ret.count + return ret + } + return Data() + } + + public func write(data: Data) throws { + writeBuffer.append(data) + } + + public func flush() throws { + flushHandler?(self, writeBuffer) + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TMultiplexedProtocol.swift b/src/jaegertracing/thrift/lib/swift/Sources/TMultiplexedProtocol.swift new file mode 100644 index 000000000..73a8d51ab --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TMultiplexedProtocol.swift @@ -0,0 +1,47 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +public class TMultiplexedProtocol<Protocol: TProtocol>: TWrappedProtocol<Protocol> { + public let separator = ":" + + public var serviceName = "" + + public convenience init(on transport: TTransport, serviceName: String) { + self.init(on: transport) + self.serviceName = serviceName + } + + override public func writeMessageBegin(name: String, + type messageType: TMessageType, + sequenceID: Int32) throws { + switch messageType { + case .call, .oneway: + var serviceFunction = serviceName + serviceFunction += serviceName == "" ? "" : separator + serviceFunction += name + return try super.writeMessageBegin(name: serviceFunction, + type: messageType, + sequenceID: sequenceID) + default: + return try super.writeMessageBegin(name: name, + type: messageType, + sequenceID: sequenceID) + } + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TProcessor.swift b/src/jaegertracing/thrift/lib/swift/Sources/TProcessor.swift new file mode 100644 index 000000000..7ff222e41 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TProcessor.swift @@ -0,0 +1,29 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + + +public typealias TProcessorMessageHandler<T> = (Int, TProtocol, TProtocol, T) -> Void + +public protocol TProcessor { + associatedtype Service + var service: Service { get set } + func process(on inProtocol: TProtocol, outProtocol: TProtocol) throws + init(service: Service) +} + diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TProtocol.swift b/src/jaegertracing/thrift/lib/swift/Sources/TProtocol.swift new file mode 100644 index 000000000..b4e5dbe73 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TProtocol.swift @@ -0,0 +1,182 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import Foundation + +public enum TMessageType: Int32 { + case call = 1 + case reply = 2 + case exception = 3 + case oneway = 4 +} + +public enum TType: Int32 { + case stop = 0 + case void = 1 + case bool = 2 + case i8 = 3 + case double = 4 + case i16 = 6 + case i32 = 8 + case i64 = 10 + case string = 11 + case `struct` = 12 + case map = 13 + case set = 14 + case list = 15 + case utf8 = 16 + case utf16 = 17 +} + +public protocol TProtocol { + var transport: TTransport { get set } + init(on transport: TTransport) + // Reading Methods + + func readMessageBegin() throws -> (String, TMessageType, Int32) + func readMessageEnd() throws + func readStructBegin() throws -> String + func readStructEnd() throws + func readFieldBegin() throws -> (String, TType, Int32) + func readFieldEnd() throws + func readMapBegin() throws -> (TType, TType, Int32) + func readMapEnd() throws + func readSetBegin() throws -> (TType, Int32) + func readSetEnd() throws + func readListBegin() throws -> (TType, Int32) + func readListEnd() throws + + func read() throws -> String + func read() throws -> Bool + func read() throws -> UInt8 + func read() throws -> Int16 + func read() throws -> Int32 + func read() throws -> Int64 + func read() throws -> Double + func read() throws -> Data + + // Writing methods + + func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws + func writeMessageEnd() throws + func writeStructBegin(name: String) throws + func writeStructEnd() throws + func writeFieldBegin(name: String, type fieldType: TType, fieldID: Int32) throws + func writeFieldStop() throws + func writeFieldEnd() throws + func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws + func writeMapEnd() throws + func writeSetBegin(elementType: TType, size: Int32) throws + func writeSetEnd() throws + func writeListBegin(elementType: TType, size: Int32) throws + func writeListEnd() throws + + func write(_ value: String) throws + func write(_ value: Bool) throws + func write(_ value: UInt8) throws + func write(_ value: Int16) throws + func write(_ value: Int32) throws + func write(_ value: Int64) throws + func write(_ value: Double) throws + func write(_ value: Data) throws +} + +public extension TProtocol { + func writeFieldValue(_ value: TSerializable, name: String, type: TType, id: Int32) throws { + try writeFieldBegin(name: name, type: type, fieldID: id) + try value.write(to: self) + try writeFieldEnd() + } + + func validateValue(_ value: Any?, named name: String) throws { + if value == nil { + throw TProtocolError(error: .unknown, message: "Missing required value for field: \(name)") + } + } + + func readResultMessageBegin() throws { + let (_, type, _) = try readMessageBegin(); + if type == .exception { + let x = try readException() + throw x + } + return + } + + func readException() throws -> TApplicationError { + return try TApplicationError.read(from: self) + } + + func writeException(messageName name: String, sequenceID: Int32, ex: TApplicationError) throws { + try writeMessageBegin(name: name, type: .exception, sequenceID: sequenceID) + try ex.write(to: self) + try writeMessageEnd() + } + + func skip(type: TType) throws { + switch type { + case .bool: _ = try read() as Bool + case .i8: _ = try read() as UInt8 + case .i16: _ = try read() as Int16 + case .i32: _ = try read() as Int32 + case .i64: _ = try read() as Int64 + case .double: _ = try read() as Double + case .string: _ = try read() as String + + case .struct: + _ = try readStructBegin() + while true { + let (_, fieldType, _) = try readFieldBegin() + if fieldType == .stop { + break + } + try skip(type: fieldType) + try readFieldEnd() + } + try readStructEnd() + + + case .map: + let (keyType, valueType, size) = try readMapBegin() + for _ in 0..<size { + try skip(type: keyType) + try skip(type: valueType) + } + try readMapEnd() + + + case .set: + let (elemType, size) = try readSetBegin() + for _ in 0..<size { + try skip(type: elemType) + } + try readSetEnd() + + case .list: + let (elemType, size) = try readListBegin() + for _ in 0..<size { + try skip(type: elemType) + } + try readListEnd() + + default: + throw TProtocolError(error: .invalidData, message: "Invalid data") + } + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TProtocolError.swift b/src/jaegertracing/thrift/lib/swift/Sources/TProtocolError.swift new file mode 100644 index 000000000..a5d14f9ea --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TProtocolError.swift @@ -0,0 +1,146 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import Foundation + +public struct TProtocolError : TError { + public init() { } + + public enum Code : TErrorCode { + case unknown + case invalidData + case negativeSize + case sizeLimit(limit: Int, got: Int) + case badVersion(expected: String, got: String) + case notImplemented + case depthLimit + + public var thriftErrorCode: Int { + switch self { + case .unknown: return 0 + case .invalidData: return 1 + case .negativeSize: return 2 + case .sizeLimit: return 3 + case .badVersion: return 4 + case .notImplemented: return 5 + case .depthLimit: return 6 + } + + } + public var description: String { + switch self { + case .unknown: return "Unknown TProtocolError" + case .invalidData: return "Invalid Data" + case .negativeSize: return "Negative Size" + case .sizeLimit(let limit, let got): + return "Message exceeds size limit of \(limit) (received: \(got)" + case .badVersion(let expected, let got): + return "Bad Version. (Expected: \(expected), Got: \(got)" + case .notImplemented: return "Not Implemented" + case .depthLimit: return "Depth Limit" + } + } + } + + public enum ExtendedErrorCode : TErrorCode { + case unknown + case missingRequiredField(fieldName: String) + case unexpectedType(type: TType) + case mismatchedProtocol(expected: String, got: String) + public var thriftErrorCode: Int { + switch self { + case .unknown: return 1000 + case .missingRequiredField: return 1001 + case .unexpectedType: return 1002 + case .mismatchedProtocol: return 1003 + } + } + public var description: String { + switch self { + case .unknown: return "Unknown TProtocolExtendedError" + case .missingRequiredField(let fieldName): return "Missing Required Field: \(fieldName)" + case .unexpectedType(let type): return "Unexpected Type \(type.self)" + case .mismatchedProtocol(let expected, let got): return "Mismatched Protocol. (Expected: \(expected), got \(got))" + } + } + } + + public var extendedError: ExtendedErrorCode? = nil + + public init(error: Code = .unknown, + message: String? = nil, + extendedError: ExtendedErrorCode? = nil) { + self.error = error + self.message = message + self.extendedError = extendedError + } + + /// Mark: TError + public var error: Code = .unknown + public var message: String? = nil + public static var defaultCase: Code { return .unknown } + + public var description: String { + var out = "\(TProtocolError.self): (\(error.thriftErrorCode) \(error.description)\n" + if let extendedError = extendedError { + out += "TProtocolExtendedError (\(extendedError.thriftErrorCode)): \(extendedError.description)" + } + if let message = message { + out += "Message: \(message)" + } + return out + } +} + + +/// Wrapper for Transport errors in Protocols. Inspired by Thrift-Cocoa PROTOCOL_TRANSPORT_ERROR +/// macro. Modified to be more Swift-y. Catches any TError thrown within the block and +/// rethrows a given TProtocolError, the original error's description is appended to the new +/// TProtocolError's message. sourceFile, sourceLine, sourceMethod are auto-populated and should +/// be ignored when calling. +/// +/// - parameter error: TProtocolError to throw if the block throws +/// - parameter sourceFile: throwing file, autopopulated +/// - parameter sourceLine: throwing line, autopopulated +/// - parameter sourceMethod: throwing method, autopopulated +/// - parameter block: throwing block +/// +/// - throws: TProtocolError Default is TProtocolError.ErrorCode.unknown. Underlying +/// error's description appended to TProtocolError.message +func ProtocolTransportTry(error: TProtocolError = TProtocolError(), + sourceFile: String = #file, + sourceLine: Int = #line, + sourceMethod: String = #function, + block: () throws -> ()) throws { + // Need mutable copy + var error = error + do { + try block() + } catch let err as TError { + var message = error.message ?? "" + message += "\nFile: \(sourceFile)\n" + message += "Line: \(sourceLine)\n" + message += "Method: \(sourceMethod)" + message += "\nOriginal Error:\n" + err.description + error.message = message + throw error + } +} + + diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransport.swift new file mode 100644 index 000000000..d350f821e --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransport.swift @@ -0,0 +1,236 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import Foundation +import CoreFoundation + +#if !swift(>=4.2) +// Swift 3/4 compatibility +fileprivate extension RunLoopMode { + static let `default` = defaultRunLoopMode +} +#endif + +#if os(Linux) +public class TSSLSocketTransport { + init(hostname: String, port: UInt16) { + // FIXME! + assert(false, "Security not available in Linux, TSSLSocketTransport Unavilable for now") + } +} +#else +let isLittleEndian = Int(OSHostByteOrder()) == OSLittleEndian +let htons = isLittleEndian ? _OSSwapInt16 : { $0 } +let htonl = isLittleEndian ? _OSSwapInt32 : { $0 } + +public class TSSLSocketTransport: TStreamTransport { + var sslHostname: String + var sd: Int32 = 0 + + public init(hostname: String, port: UInt16) throws { + sslHostname = hostname + var readStream: Unmanaged<CFReadStream>? + var writeStream: Unmanaged<CFWriteStream>? + + /* create a socket structure */ + var pin: sockaddr_in = sockaddr_in() + var hp: UnsafeMutablePointer<hostent>? = nil + for i in 0..<10 { + + hp = gethostbyname(hostname.cString(using: String.Encoding.utf8)!) + if hp == nil { + print("failed to resolve hostname \(hostname)") + herror("resolv") + if i == 9 { + super.init(inputStream: nil, outputStream: nil) // have to init before throwing + throw TSSLSocketTransportError(error: .hostanameResolution(hostname: hostname)) + } + Thread.sleep(forTimeInterval: 0.2) + } else { + break + } + } + pin.sin_family = UInt8(AF_INET) + pin.sin_addr = in_addr(s_addr: UInt32((hp?.pointee.h_addr_list.pointee?.pointee)!)) // Is there a better way to get this??? + pin.sin_port = htons(port) + + /* create the socket */ + sd = socket(Int32(AF_INET), Int32(SOCK_STREAM), Int32(IPPROTO_TCP)) + if sd == -1 { + super.init(inputStream: nil, outputStream: nil) // have to init before throwing + throw TSSLSocketTransportError(error: .socketCreate(port: Int(port))) + } + + /* open a connection */ + // need a non-self ref to sd, otherwise the j complains + let sd_local = sd + let connectResult = withUnsafePointer(to: &pin) { + connect(sd_local, UnsafePointer<sockaddr>(OpaquePointer($0)), socklen_t(MemoryLayout<sockaddr_in>.size)) + } + if connectResult == -1 { + super.init(inputStream: nil, outputStream: nil) // have to init before throwing + throw TSSLSocketTransportError(error: .connect) + } + + CFStreamCreatePairWithSocket(kCFAllocatorDefault, sd, &readStream, &writeStream) + + CFReadStreamSetProperty(readStream?.takeRetainedValue(), .socketNativeHandle, kCFBooleanTrue) + CFWriteStreamSetProperty(writeStream?.takeRetainedValue(), .socketNativeHandle, kCFBooleanTrue) + + var inputStream: InputStream? = nil + var outputStream: OutputStream? = nil + if readStream != nil && writeStream != nil { + + CFReadStreamSetProperty(readStream?.takeRetainedValue(), + .socketSecurityLevel, + kCFStreamSocketSecurityLevelTLSv1) + + let settings: [String: Bool] = [kCFStreamSSLValidatesCertificateChain as String: true] + + CFReadStreamSetProperty(readStream?.takeRetainedValue(), + .SSLSettings, + settings as CFTypeRef) + + CFWriteStreamSetProperty(writeStream?.takeRetainedValue(), + .SSLSettings, + settings as CFTypeRef) + + inputStream = readStream!.takeRetainedValue() + inputStream?.schedule(in: .current, forMode: .default) + inputStream?.open() + + outputStream = writeStream!.takeRetainedValue() + outputStream?.schedule(in: .current, forMode: .default) + outputStream?.open() + + readStream?.release() + writeStream?.release() + } + + + super.init(inputStream: inputStream, outputStream: outputStream) + self.input?.delegate = self + self.output?.delegate = self + } + + func recoverFromTrustFailure(_ myTrust: SecTrust, lastTrustResult: SecTrustResultType) -> Bool { + let trustTime = SecTrustGetVerifyTime(myTrust) + let currentTime = CFAbsoluteTimeGetCurrent() + + let timeIncrement = 31536000 // from TSSLSocketTransport.m + let newTime = currentTime - Double(timeIncrement) + + if trustTime - newTime != 0 { + let newDate = CFDateCreate(nil, newTime) + SecTrustSetVerifyDate(myTrust, newDate!) + + var tr = lastTrustResult + let success = withUnsafeMutablePointer(to: &tr) { trPtr -> Bool in + if SecTrustEvaluate(myTrust, trPtr) != errSecSuccess { + return false + } + return true + } + if !success { return false } + } + if lastTrustResult == .proceed || lastTrustResult == .unspecified { + return false + } + + print("TSSLSocketTransport: Unable to recover certificate trust failure") + return true + } + + public func isOpen() -> Bool { + return sd > 0 + } +} + +extension TSSLSocketTransport: StreamDelegate { + public func stream(_ aStream: Stream, handle eventCode: Stream.Event) { + + switch eventCode { + case Stream.Event(): break + case Stream.Event.hasBytesAvailable: break + case Stream.Event.openCompleted: break + case Stream.Event.hasSpaceAvailable: + var proceed = false + var trustResult: SecTrustResultType = .invalid + + var newPolicies: CFMutableArray? + + repeat { + let trust: SecTrust = aStream.property(forKey: .SSLPeerTrust) as! SecTrust + + // Add new policy to current list of policies + let policy = SecPolicyCreateSSL(false, sslHostname as CFString?) + var ppolicy = policy // mutable for pointer + let policies: UnsafeMutablePointer<CFArray?>? = nil + if SecTrustCopyPolicies(trust, policies!) != errSecSuccess { + break + } + withUnsafeMutablePointer(to: &ppolicy) { ptr in + newPolicies = CFArrayCreateMutableCopy(nil, 0, policies?.pointee) + CFArrayAppendValue(newPolicies, ptr) + } + + // update trust policies + if SecTrustSetPolicies(trust, newPolicies!) != errSecSuccess { + break + } + + // Evaluate the trust chain + let success = withUnsafeMutablePointer(to: &trustResult) { trustPtr -> Bool in + if SecTrustEvaluate(trust, trustPtr) != errSecSuccess { + return false + } + return true + } + + if !success { + break + } + + + switch trustResult { + case .proceed: proceed = true + case .unspecified: proceed = true + case .recoverableTrustFailure: + proceed = self.recoverFromTrustFailure(trust, lastTrustResult: trustResult) + + case .deny: break + case .fatalTrustFailure: break + case .otherError: break + case .invalid: break + default: break + } + } while false + + if !proceed { + print("TSSLSocketTransport: Cannot trust certificate. Result: \(trustResult)") + aStream.close() + } + + case Stream.Event.errorOccurred: break + case Stream.Event.endEncountered: break + default: break + } + } +} +#endif diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransportError.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransportError.swift new file mode 100644 index 000000000..fda162bd5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TSSLSocketTransportError.swift @@ -0,0 +1,48 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +public struct TSSLSocketTransportError: TError { + public enum ErrorCode: TErrorCode { + case hostanameResolution(hostname: String) + case socketCreate(port: Int) + case connect + + public var thriftErrorCode: Int { + switch self { + case .hostanameResolution: return -10000 + case .socketCreate: return -10001 + case .connect: return -10002 + } + } + + public var description: String { + switch self { + case .hostanameResolution(let hostname): return "Failed to resolve hostname: \(hostname)" + case .socketCreate(let port): return "Could not create socket on port: \(port)" + case .connect: return "Connect error" + } + } + + } + public var error: ErrorCode = .connect + public var message: String? + public static var defaultCase: ErrorCode { return .connect } + + public init() { } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSerializable.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSerializable.swift new file mode 100644 index 000000000..b45096b69 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TSerializable.swift @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Foundation + + +public protocol TSerializable { + var hashValue: Int { get } + + /// TType for instance + static var thriftType: TType { get } + + /// Read TSerializable instance from Protocol + static func read(from proto: TProtocol) throws -> Self + + /// Write TSerializable instance to Protocol + func write(to proto: TProtocol) throws + +} + +extension TSerializable { + public static func write(_ value: Self, to proto: TProtocol) throws { + try value.write(to: proto) + } + + /// convenience for member access + public var thriftType: TType { return Self.thriftType } +} + +public func ==<T>(lhs: T, rhs: T) -> Bool where T : TSerializable { + return lhs.hashValue == rhs.hashValue +} + +/// Default read/write for primitave Thrift types: +/// Bool, Int8 (byte), Int16, Int32, Int64, Double, String + +extension Bool : TSerializable { + public static var thriftType: TType { return .bool } + + public static func read(from proto: TProtocol) throws -> Bool { + return try proto.read() + } + + public func write(to proto: TProtocol) throws { + try proto.write(self) + } +} + +extension Int8 : TSerializable { + public static var thriftType: TType { return .i8 } + + public static func read(from proto: TProtocol) throws -> Int8 { + return Int8(try proto.read() as UInt8) + } + + public func write(to proto: TProtocol) throws { + try proto.write(UInt8(self)) + } +} + +extension Int16 : TSerializable { + public static var thriftType: TType { return .i16 } + + public static func read(from proto: TProtocol) throws -> Int16 { + return try proto.read() + } + + public func write(to proto: TProtocol) throws { + try proto.write(self) + } +} + +extension Int32 : TSerializable { + public static var thriftType: TType { return .i32 } + + public static func read(from proto: TProtocol) throws -> Int32 { + return try proto.read() + } + + public func write(to proto: TProtocol) throws { + try proto.write(self) + } +} + + +extension Int64 : TSerializable { + public static var thriftType: TType { return .i64 } + + public static func read(from proto: TProtocol) throws -> Int64 { + return try proto.read() + } + + public func write(to proto: TProtocol) throws { + try proto.write(self) + } +} + +extension Double : TSerializable { + public static var thriftType: TType { return .double } + + public static func read(from proto: TProtocol) throws -> Double { + return try proto.read() + } + + public func write(to proto: TProtocol) throws { + try proto.write(self) + } +} + +extension String : TSerializable { + public static var thriftType: TType { return .string } + + public static func read(from proto: TProtocol) throws -> String { + return try proto.read() + } + + public func write(to proto: TProtocol) throws { + try proto.write(self) + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSet.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSet.swift new file mode 100644 index 000000000..6891c11e6 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TSet.swift @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Foundation + +public struct TSet<Element : TSerializable & Hashable> : SetAlgebra, Hashable, Collection, ExpressibleByArrayLiteral, TSerializable { + /// Typealias for Storage type + public typealias Storage = Set<Element> + + + /// Internal Storage used for TSet (Set\<Element\>) + internal var storage : Storage + + + /// Mark: Collection + + public typealias Indices = Storage.Indices + public typealias Index = Storage.Index + public typealias IndexDistance = Int + public typealias SubSequence = Storage.SubSequence + + + public var indices: Indices { return storage.indices } + + // Must implement isEmpty even though both SetAlgebra and Collection provide it due to their conflciting default implementations + public var isEmpty: Bool { return storage.isEmpty } + + public func distance(from start: Index, to end: Index) -> IndexDistance { + return storage.distance(from: start, to: end) + } + + public func index(_ i: Index, offsetBy n: IndexDistance) -> Index { + return storage.index(i, offsetBy: n) + } + + public func index(_ i: Index, offsetBy n: IndexDistance, limitedBy limit: Index) -> Index? { + return storage.index(i, offsetBy: n, limitedBy: limit) + } + + #if swift(>=3.2) + public subscript (position: Storage.Index) -> Element { + return storage[position] + } + #else + public subscript (position: Storage.Index) -> Element? { + return storage[position] + } + #endif + + /// Mark: SetAlgebra + internal init(storage: Set<Element>) { + self.storage = storage + } + + public func contains(_ member: Element) -> Bool { + return storage.contains(member) + } + + public mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) { + return storage.insert(newMember) + } + + public mutating func remove(_ member: Element) -> Element? { + return storage.remove(member) + } + + public func union(_ other: TSet<Element>) -> TSet { + return TSet(storage: storage.union(other.storage)) + } + + public mutating func formIntersection(_ other: TSet<Element>) { + return storage.formIntersection(other.storage) + } + + public mutating func formSymmetricDifference(_ other: TSet<Element>) { + return storage.formSymmetricDifference(other.storage) + } + + public mutating func formUnion(_ other: TSet<Element>) { + return storage.formUnion(other.storage) + } + + public func intersection(_ other: TSet<Element>) -> TSet { + return TSet(storage: storage.intersection(other.storage)) + } + + public func symmetricDifference(_ other: TSet<Element>) -> TSet { + return TSet(storage: storage.symmetricDifference(other.storage)) + } + + public mutating func update(with newMember: Element) -> Element? { + return storage.update(with: newMember) + } + + /// Mark: IndexableBase + + public var startIndex: Index { return storage.startIndex } + public var endIndex: Index { return storage.endIndex } + public func index(after i: Index) -> Index { + return storage.index(after: i) + } + + public func formIndex(after i: inout Storage.Index) { + storage.formIndex(after: &i) + } + + public subscript(bounds: Range<Index>) -> SubSequence { + return storage[bounds] + } + + + /// Mark: Hashable + public var hashValue : Int { + let prime = 31 + var result = 1 + for element in storage { + result = prime &* result &+ element.hashValue + } + return result + } + + /// Mark: TSerializable + public static var thriftType : TType { return .set } + + public init() { + storage = Storage() + } + + public init(arrayLiteral elements: Element...) { + self.storage = Storage(elements) + } + + public init<Source : Sequence>(_ sequence: Source) where Source.Iterator.Element == Element { + storage = Storage(sequence) + } + + public static func read(from proto: TProtocol) throws -> TSet { + let (elementType, size) = try proto.readSetBegin() + if elementType != Element.thriftType { + throw TProtocolError(error: .invalidData, + extendedError: .unexpectedType(type: elementType)) + } + var set = TSet() + for _ in 0..<size { + let element = try Element.read(from: proto) + set.storage.insert(element) + } + try proto.readSetEnd() + return set + } + + public func write(to proto: TProtocol) throws { + try proto.writeSetBegin(elementType: Element.thriftType, size: Int32(self.count)) + for element in self.storage { + try Element.write(element, to: proto) + } + try proto.writeSetEnd() + } +} + +extension TSet: CustomStringConvertible, CustomDebugStringConvertible { + public var description : String { + return storage.description + } + public var debugDescription : String { + return storage.debugDescription + } + +} + +public func ==<Element>(lhs: TSet<Element>, rhs: TSet<Element>) -> Bool { + return lhs.storage == rhs.storage +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSocketServer.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSocketServer.swift new file mode 100644 index 000000000..7367c7edc --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TSocketServer.swift @@ -0,0 +1,148 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS) + import Darwin +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) + import Glibc + import Dispatch +#endif + +import Foundation +import CoreFoundation + +public let TSocketServerClientConnectionFinished = "TSocketServerClientConnectionFinished" +public let TSocketServerProcessorKey = "TSocketServerProcessor" +public let TSocketServerTransportKey = "TSocketServerTransport" + +class TSocketServer<InProtocol: TProtocol, OutProtocol: TProtocol, Processor: TProcessor, Service> where Processor.Service == Service { + var socketFileHandle: FileHandle + var processingQueue = DispatchQueue(label: "TSocketServer.processing", + qos: .background, + attributes: .concurrent) + var serviceHandler: Service + + public init(port: Int, + service: Service, + inProtocol: InProtocol.Type, + outProtocol: OutProtocol.Type, + processor: Processor.Type) throws { + // set service handler + self.serviceHandler = service + + // create a socket + var fd: Int32 = -1 + #if os(Linux) + let sock = CFSocketCreate(kCFAllocatorDefault, PF_INET, Int32(SOCK_STREAM.rawValue), Int32(IPPROTO_TCP), 0, nil, nil) + #else + let sock = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, 0, nil, nil) + #endif + if sock != nil { + CFSocketSetSocketFlags(sock, CFSocketGetSocketFlags(sock) & ~kCFSocketCloseOnInvalidate) + + fd = CFSocketGetNative(sock) + var yes = 1 + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, UInt32(MemoryLayout<Int>.size)) + + #if os(Linux) + var addr = sockaddr_in(sin_family: sa_family_t(AF_INET), + sin_port: in_port_t(port.bigEndian), + sin_addr: in_addr(s_addr: in_addr_t(0)), + sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) + #else + var addr = sockaddr_in(sin_len: UInt8(MemoryLayout<sockaddr_in>.size), + sin_family: sa_family_t(AF_INET), + sin_port: in_port_t(port.bigEndian), + sin_addr: in_addr(s_addr: in_addr_t(0)), + sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) + #endif + + let ptr = withUnsafePointer(to: &addr) { + return UnsafePointer<UInt8>(OpaquePointer($0)) + } + + let address = Data(bytes: ptr, count: MemoryLayout<sockaddr_in>.size) + + let cfaddr = address.withUnsafeBytes { + CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, $0, address.count, nil) + } + if CFSocketSetAddress(sock, cfaddr) != CFSocketError.success { //kCFSocketSuccess { + CFSocketInvalidate(sock) + print("TSocketServer: Could not bind to address") + throw TTransportError(error: .notOpen, message: "Could not bind to address") + } + + } else { + print("TSocketServer: No server socket") + throw TTransportError(error: .notOpen, message: "Could not create socket") + } + + // wrap it in a file handle so we can get messages from it + socketFileHandle = FileHandle(fileDescriptor: fd, closeOnDealloc: true) + + // throw away our socket + CFSocketInvalidate(sock) + + // register for notifications of accepted incoming connections + _ = NotificationCenter.default.addObserver(forName: .NSFileHandleConnectionAccepted, + object: nil, queue: nil) { + [weak self] notification in + guard let strongSelf = self else { return } + strongSelf.connectionAccepted(strongSelf.socketFileHandle) + } + + // tell socket to listen + socketFileHandle.acceptConnectionInBackgroundAndNotify() + + print("TSocketServer: Listening on TCP port \(port)") + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + func connectionAccepted(_ socket: FileHandle) { + // Now that we have a client connected, handle the request on queue + processingQueue.async { + self.handleClientConnection(socket) + } + } + + func handleClientConnection(_ clientSocket: FileHandle) { + + let transport = TFileHandleTransport(fileHandle: clientSocket) + let processor = Processor(service: serviceHandler) + + let inProtocol = InProtocol(on: transport) + let outProtocol = OutProtocol(on: transport) + + do { + try processor.process(on: inProtocol, outProtocol: outProtocol) + } catch let error { + print("Error processign request: \(error)") + } + DispatchQueue.main.async { + NotificationCenter.default + .post(name: Notification.Name(rawValue: TSocketServerClientConnectionFinished), + object: self, + userInfo: [TSocketServerProcessorKey: processor, + TSocketServerTransportKey: transport]) + } + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TSocketTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TSocketTransport.swift new file mode 100644 index 000000000..21325033b --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TSocketTransport.swift @@ -0,0 +1,216 @@ + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS) + import Darwin +#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android) + import Glibc + import Dispatch +#endif + +import Foundation +import CoreFoundation + +#if !swift(>=4.2) +// Swift 3/4 compatibility +fileprivate extension RunLoopMode { + static let `default` = defaultRunLoopMode +} +#endif + +private struct Sys { + #if os(Linux) + static let read = Glibc.read + static let write = Glibc.write + static let close = Glibc.close + #else + static let read = Darwin.read + static let write = Darwin.write + static let close = Darwin.close + #endif +} + +extension in_addr { + public init?(hostent: hostent?) { + guard let host = hostent, host.h_addr_list != nil, host.h_addr_list.pointee != nil else { + return nil + } + self.init() + memcpy(&self, host.h_addr_list.pointee!, Int(host.h_length)) + + } +} + + +#if os(Linux) + /// TCFSocketTransport currently unavailable + /// remove comments and build to see why/fix + /// currently CF[Read|Write]Stream's can't cast to [Input|Output]Streams which breaks thigns +#else +extension Stream.PropertyKey { + static let SSLPeerTrust = Stream.PropertyKey(kCFStreamPropertySSLPeerTrust as String) +} + +/// TCFSocketTransport, uses CFSockets and (NS)Stream's +public class TCFSocketTransport: TStreamTransport { + public init?(hostname: String, port: Int, secure: Bool = false) { + + var inputStream: InputStream + var outputStream: OutputStream + + var readStream: Unmanaged<CFReadStream>? + var writeStream: Unmanaged<CFWriteStream>? + CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, + hostname as CFString, + UInt32(port), + &readStream, + &writeStream) + + if let readStream = readStream?.takeRetainedValue(), + let writeStream = writeStream?.takeRetainedValue() { + CFReadStreamSetProperty(readStream, .shouldCloseNativeSocket, kCFBooleanTrue) + CFWriteStreamSetProperty(writeStream, .shouldCloseNativeSocket, kCFBooleanTrue) + + if secure { + CFReadStreamSetProperty(readStream, .socketSecurityLevel, StreamSocketSecurityLevel.negotiatedSSL.rawValue as CFString) + CFWriteStreamSetProperty(writeStream, .socketSecurityLevel, StreamSocketSecurityLevel.negotiatedSSL.rawValue as CFString) + } + + inputStream = readStream as InputStream + inputStream.schedule(in: .current, forMode: .default) + inputStream.open() + + outputStream = writeStream as OutputStream + outputStream.schedule(in: .current, forMode: .default) + outputStream.open() + + } else { + + if readStream != nil { + readStream?.release() + } + if writeStream != nil { + writeStream?.release() + } + super.init(inputStream: nil, outputStream: nil) + return nil + } + + super.init(inputStream: inputStream, outputStream: outputStream) + + self.input?.delegate = self + self.output?.delegate = self + } +} + +extension TCFSocketTransport: StreamDelegate { } +#endif + + +/// TSocketTransport, posix sockets. Supports IPv4 only for now +public class TSocketTransport : TTransport { + public var socketDescriptor: Int32 + + + + /// Initialize from an already set up socketDescriptor. + /// Expects socket thats already bound/connected (i.e. from listening) + /// + /// - parameter socketDescriptor: posix socket descriptor (Int32) + public init(socketDescriptor: Int32) { + self.socketDescriptor = socketDescriptor + } + + + public convenience init(hostname: String, port: Int) throws { + guard let hp = gethostbyname(hostname.cString(using: .utf8)!)?.pointee, + let hostAddr = in_addr(hostent: hp) else { + throw TTransportError(error: .unknown, message: "Invalid address: \(hostname)") + } + + + #if os(Linux) + let sock = socket(AF_INET, Int32(SOCK_STREAM.rawValue), 0) + var addr = sockaddr_in(sin_family: sa_family_t(AF_INET), + sin_port: in_port_t(htons(UInt16(port))), + sin_addr: hostAddr, + sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) + #else + let sock = socket(AF_INET, SOCK_STREAM, 0) + + var addr = sockaddr_in(sin_len: UInt8(MemoryLayout<sockaddr_in>.size), + sin_family: sa_family_t(AF_INET), + sin_port: in_port_t(htons(UInt16(port))), + sin_addr: in_addr(s_addr: in_addr_t(0)), + sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) + + #endif + + let addrPtr = withUnsafePointer(to: &addr){ UnsafePointer<sockaddr>(OpaquePointer($0)) } + + let connected = connect(sock, addrPtr, UInt32(MemoryLayout<sockaddr_in>.size)) + if connected != 0 { + throw TTransportError(error: .notOpen, message: "Error binding to host: \(hostname) \(port)") + } + + self.init(socketDescriptor: sock) + } + + deinit { + close() + } + + public func readAll(size: Int) throws -> Data { + var out = Data() + while out.count < size { + out.append(try self.read(size: size)) + } + return out + } + + public func read(size: Int) throws -> Data { + var buff = Array<UInt8>.init(repeating: 0, count: size) + let readBytes = Sys.read(socketDescriptor, &buff, size) + + return Data(buff[0..<readBytes]) + } + + public func write(data: Data) { + var bytesToWrite = data.count + var writeBuffer = data + while bytesToWrite > 0 { + let written = writeBuffer.withUnsafeBytes { + Sys.write(socketDescriptor, $0, writeBuffer.count) + } + writeBuffer = writeBuffer.subdata(in: written ..< writeBuffer.count) + bytesToWrite -= written + } + } + + public func flush() throws { + // nothing to do + } + + public func close() { + shutdown(socketDescriptor, Int32(SHUT_RDWR)) + _ = Sys.close(socketDescriptor) + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TStreamTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TStreamTransport.swift new file mode 100644 index 000000000..d9c957422 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TStreamTransport.swift @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import Foundation +import CoreFoundation + +#if !swift(>=4.2) +// Swift 3/4 compatibility +fileprivate extension RunLoopMode { + static let `default` = defaultRunLoopMode +} +#endif + +#if os(Linux) + /// Currently unavailable in Linux + /// Remove comments and build to fix + /// Currently kConstants for CFSockets don't exist in linux and not all have been moved + /// to property structs yet +#else + // Must inherit NSObject for NSStreamDelegate conformance + public class TStreamTransport : NSObject, TTransport { + public var input: InputStream? = nil + public var output: OutputStream? = nil + + public init(inputStream: InputStream?, outputStream: OutputStream?) { + input = inputStream + output = outputStream + } + + public convenience init(inputStream: InputStream?) { + self.init(inputStream: inputStream, outputStream: nil) + } + + public convenience init(outputStream: OutputStream?) { + self.init(inputStream: nil, outputStream: outputStream) + } + + deinit { + close() + } + + public func readAll(size: Int) throws -> Data { + guard let input = input else { + throw TTransportError(error: .unknown) + } + + var read = Data() + while read.count < size { + var buffer = Array<UInt8>(repeating: 0, count: size - read.count) + + let bytesRead = buffer.withUnsafeMutableBufferPointer { bufferPtr in + return input.read(bufferPtr.baseAddress!, maxLength: size - read.count) + } + + if bytesRead <= 0 { + throw TTransportError(error: .notOpen) + } + read.append(Data(buffer)) + } + return read + } + + public func read(size: Int) throws -> Data { + guard let input = input else { + throw TTransportError(error: .unknown) + } + + var read = Data() + while read.count < size { + var buffer = Array<UInt8>(repeating: 0, count: size - read.count) + let bytesRead = buffer.withUnsafeMutableBufferPointer { + input.read($0.baseAddress!, maxLength: size - read.count) + } + + if bytesRead <= 0 { + break + } + + read.append(Data(buffer)) + } + return read + } + + public func write(data: Data) throws { + guard let output = output else { + throw TTransportError(error: .unknown) + } + + var bytesWritten = 0 + while bytesWritten < data.count { + bytesWritten = data.withUnsafeBytes { + return output.write($0, maxLength: data.count) + } + + if bytesWritten == -1 { + throw TTransportError(error: .notOpen) + } else if bytesWritten == 0 { + throw TTransportError(error: .endOfFile) + } + } + } + + + public func flush() throws { + return + } + + public func close() { + + if input != nil { + // Close and reset inputstream + if let cf: CFReadStream = input { + CFReadStreamSetProperty(cf, .shouldCloseNativeSocket, kCFBooleanTrue) + } + + input?.delegate = nil + input?.close() + input?.remove(from: .current, forMode: .default) + input = nil + } + + if output != nil { + // Close and reset output stream + if let cf: CFWriteStream = output { + CFWriteStreamSetProperty(cf, .shouldCloseNativeSocket, kCFBooleanTrue) + } + output?.delegate = nil + output?.close() + output?.remove(from: .current, forMode: .default) + output = nil + } + } + } +#endif diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TStruct.swift b/src/jaegertracing/thrift/lib/swift/Sources/TStruct.swift new file mode 100644 index 000000000..38e51e702 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TStruct.swift @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +/// Protocol for Generated Structs to conform to +/// Dictionary maps field names to internal IDs and uses Reflection +/// to iterate through all fields. +/// `writeFieldValue(_:name:type:id:)` calls `TSerializable.write(to:)` internally +/// giving a nice recursive behavior for nested TStructs, TLists, TMaps, and TSets +public protocol TStruct : TSerializable { + static var fieldIds: [String: Int32] { get } + static var structName: String { get } +} + +public extension TStruct { + static var fieldIds: [String: (id: Int32, type: TType)] { return [:] } + static var thriftType: TType { return .struct } + + func write(to proto: TProtocol) throws { + // Write struct name first + try proto.writeStructBegin(name: Self.structName) + + try self.forEach { name, value, id in + // Write to protocol + try proto.writeFieldValue(value, name: name, + type: value.thriftType, id: id) + } + try proto.writeFieldStop() + try proto.writeStructEnd() + } + + var hashValue: Int { + let prime = 31 + var result = 1 + self.forEach { _, value, _ in + result = prime &* result &+ (value.hashValue) + } + return result + } + + /// Provides a block for handling each (available) thrift property using reflection + /// Caveat: Skips over optional values + + + /// Provides a block for handling each (available) thrift property using reflection + /// + /// - parameter block: block for handling property + /// + /// - throws: rethrows any Error thrown in block + private func forEach(_ block: (_ name: String, _ value: TSerializable, _ id: Int32) throws -> Void) rethrows { + // Mirror the object, getting (name: String?, value: Any) for every property + let mirror = Mirror(reflecting: self) + + // Iterate through all children, ignore empty property names + for (propName, propValue) in mirror.children { + guard let propName = propName else { continue } + + if let tval = unwrap(any: propValue) as? TSerializable, let id = Self.fieldIds[propName] { + try block(propName, tval, id) + } + } + } + + + /// Any can mysteriously be an Optional<Any> at the same time, + /// this checks and always returns Optional<Any> without double wrapping + /// we then try to bind value as TSerializable to ignore any extension properties + /// and the like and verify the property exists and grab the Thrift + /// property ID at the same time + /// + /// - parameter any: Any instance to attempt to unwrap + /// + /// - returns: Unwrapped Any as Optional<Any> + private func unwrap(any: Any) -> Any? { + let mi = Mirror(reflecting: any) + + if mi.displayStyle != .optional { return any } + if mi.children.count == 0 { return nil } + + let (_, some) = mi.children.first! + return some + } +} + diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TTransport.swift b/src/jaegertracing/thrift/lib/swift/Sources/TTransport.swift new file mode 100644 index 000000000..e82bbe140 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TTransport.swift @@ -0,0 +1,64 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import Foundation + +public protocol TTransport { + + // Required + func read(size: Int) throws -> Data + func write(data: Data) throws + func flush() throws + + // Optional (default provided) + func readAll(size: Int) throws -> Data + func isOpen() throws -> Bool + func open() throws + func close() throws +} + +public extension TTransport { + func isOpen() throws -> Bool { return true } + func open() throws { } + func close() throws { } + + func readAll(size: Int) throws -> Data { + var buff = Data() + var have = 0 + while have < size { + let chunk = try self.read(size: size - have) + have += chunk.count + buff.append(chunk) + if chunk.count == 0 { + throw TTransportError(error: .endOfFile) + } + } + return buff + } +} + +public protocol TAsyncTransport : TTransport { + // Factory + func flush(_ completion: @escaping (TAsyncTransport, Error?) ->()) +} + +public protocol TAsyncTransportFactory { + associatedtype Transport : TAsyncTransport + func newTransport() -> Transport +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TTransportError.swift b/src/jaegertracing/thrift/lib/swift/Sources/TTransportError.swift new file mode 100644 index 000000000..3fd005937 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TTransportError.swift @@ -0,0 +1,86 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +public struct TTransportError: TError { + public enum ErrorCode: TErrorCode { + case unknown + case notOpen + case alreadyOpen + case timedOut + case endOfFile + case negativeSize + case sizeLimit(limit: Int, got: Int) + + public var thriftErrorCode: Int { + switch self { + case .unknown: return 0 + case .notOpen: return 1 + case .alreadyOpen: return 2 + case .timedOut: return 3 + case .endOfFile: return 4 + case .negativeSize: return 5 + case .sizeLimit: return 6 + } + } + public var description: String { + switch self { + case .unknown: return "Unknown TTransportError" + case .notOpen: return "Not Open" + case .alreadyOpen: return "Already Open" + case .timedOut: return "Timed Out" + case .endOfFile: return "End Of File" + case .negativeSize: return "Negative Size" + case .sizeLimit(let limit, let got): + return "Message exceeds size limit of \(limit) (received: \(got)" + } + } + } + public var error: ErrorCode = .unknown + public var message: String? = nil + public static var defaultCase: ErrorCode { return .unknown } + + public init() { } + +} + +/// THTTPTransportError +/// +/// Error's thrown on HTTP Transport +public struct THTTPTransportError: TError { + public enum ErrorCode: TErrorCode { + case invalidResponse + case invalidStatus(statusCode: Int) + case authentication + + public var description: String { + switch self { + case .invalidResponse: return "Invalid HTTP Response" + case .invalidStatus(let statusCode): return "Invalid HTTP Status Code (\(statusCode))" + case .authentication: return "Authentication Error" + } + } + public var thriftErrorCode: Int { return 0 } + } + public var error: ErrorCode = .invalidResponse + public var message: String? = nil + public static var defaultCase: ErrorCode { return .invalidResponse } + + public init() { } +} + diff --git a/src/jaegertracing/thrift/lib/swift/Sources/TWrappedProtocol.swift b/src/jaegertracing/thrift/lib/swift/Sources/TWrappedProtocol.swift new file mode 100644 index 000000000..8e8577bb5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/TWrappedProtocol.swift @@ -0,0 +1,208 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import Foundation // For (NS)Data + + +/// Generic protocol, implementes TProtocol and wraps a concrete protocol. +/// Useful for generically subclassing protocols to override specific methods +/// (i.e. TMultiplexedProtocol) +open class TWrappedProtocol<Protocol: TProtocol> : TProtocol { + var concreteProtocol: Protocol + + public var transport: TTransport { + get { + return concreteProtocol.transport + } + set { + concreteProtocol.transport = newValue + } + } + + public required init(on transport: TTransport) { + self.concreteProtocol = Protocol(on: transport) + } + + // Read methods + + public func readMessageBegin() throws -> (String, TMessageType, Int32) { + return try concreteProtocol.readMessageBegin() + } + + public func readMessageEnd() throws { + try concreteProtocol.readMessageEnd() + } + + public func readStructBegin() throws -> String { + return try concreteProtocol.readStructBegin() + } + + public func readStructEnd() throws { + try concreteProtocol.readStructEnd() + } + + public func readFieldBegin() throws -> (String, TType, Int32) { + return try concreteProtocol.readFieldBegin() + } + + public func readFieldEnd() throws { + try concreteProtocol.readFieldEnd() + } + + public func readMapBegin() throws -> (TType, TType, Int32) { + return try concreteProtocol.readMapBegin() + } + + public func readMapEnd() throws { + try concreteProtocol.readMapEnd() + } + + public func readSetBegin() throws -> (TType, Int32) { + return try concreteProtocol.readSetBegin() + } + + public func readSetEnd() throws { + try concreteProtocol.readSetEnd() + } + + public func readListBegin() throws -> (TType, Int32) { + return try concreteProtocol.readListBegin() + } + + public func readListEnd() throws { + try concreteProtocol.readListEnd() + } + + public func read() throws -> String { + return try concreteProtocol.read() + } + + public func read() throws -> Bool { + return try concreteProtocol.read() + } + + public func read() throws -> UInt8 { + return try concreteProtocol.read() + } + + public func read() throws -> Int16 { + return try concreteProtocol.read() + } + + public func read() throws -> Int32 { + return try concreteProtocol.read() + } + + public func read() throws -> Int64 { + return try concreteProtocol.read() + } + + public func read() throws -> Double { + return try concreteProtocol.read() + } + + public func read() throws -> Data { + return try concreteProtocol.read() + } + + // Write methods + + public func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws { + return try concreteProtocol.writeMessageBegin(name: name, type: messageType, sequenceID: sequenceID) + } + + public func writeMessageEnd() throws { + try concreteProtocol.writeMessageEnd() + } + + public func writeStructBegin(name: String) throws { + try concreteProtocol.writeStructBegin(name: name) + } + + public func writeStructEnd() throws { + try concreteProtocol.writeStructEnd() + } + + public func writeFieldBegin(name: String, type fieldType: TType, fieldID: Int32) throws { + try concreteProtocol.writeFieldBegin(name: name, type: fieldType, fieldID: fieldID) + } + + public func writeFieldStop() throws { + try concreteProtocol.writeFieldStop() + } + + public func writeFieldEnd() throws { + try concreteProtocol.writeFieldEnd() + } + + public func writeMapBegin(keyType: TType, valueType: TType, size: Int32) throws { + try concreteProtocol.writeMapBegin(keyType: keyType, valueType: valueType, size: size) + } + + public func writeMapEnd() throws { + try concreteProtocol.writeMapEnd() + } + + public func writeSetBegin(elementType: TType, size: Int32) throws { + try concreteProtocol.writeSetBegin(elementType: elementType, size: size) + } + + public func writeSetEnd() throws { + try concreteProtocol.writeSetEnd() + } + + public func writeListBegin(elementType: TType, size: Int32) throws { + try concreteProtocol.writeListBegin(elementType: elementType, size: size) + } + + public func writeListEnd() throws { + try concreteProtocol.writeListEnd() + } + public func write(_ value: String) throws { + try concreteProtocol.write(value) + } + + public func write(_ value: Bool) throws { + try concreteProtocol.write(value) + } + + public func write(_ value: UInt8) throws { + try concreteProtocol.write(value) + } + + public func write(_ value: Int16) throws { + try concreteProtocol.write(value) + } + + public func write(_ value: Int32) throws { + try concreteProtocol.write(value) + } + + public func write(_ value: Int64) throws { + try concreteProtocol.write(value) + } + + public func write(_ value: Double) throws { + try concreteProtocol.write(value) + } + + public func write(_ data: Data) throws { + try concreteProtocol.write(data) + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Sources/Thrift.swift b/src/jaegertracing/thrift/lib/swift/Sources/Thrift.swift new file mode 100644 index 000000000..45c68f2e5 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Sources/Thrift.swift @@ -0,0 +1,3 @@ +class Thrift { + let version = "0.13.0" +} diff --git a/src/jaegertracing/thrift/lib/swift/Tests/LinuxMain.swift b/src/jaegertracing/thrift/lib/swift/Tests/LinuxMain.swift new file mode 100644 index 000000000..288fec9e4 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Tests/LinuxMain.swift @@ -0,0 +1,8 @@ +import XCTest +@testable import ThriftTests + +XCTMain([ + testCase(ThriftTests.allTests), + testCase(TBinaryProtocolTests.allTests), + testCase(TCompactProtocolTests.allTests), +]) diff --git a/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift new file mode 100644 index 000000000..56a557261 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TBinaryProtocolTests.swift @@ -0,0 +1,168 @@ +// +// TBinaryProtocolTests.swift +// Thrift +// +// Created by Christopher Simpson on 8/18/16. +// +// + +import XCTest +import Foundation +@testable import Thrift + + +/// Testing Binary protocol read/write against itself +/// Uses separate read/write transport/protocols +class TBinaryProtocolTests: XCTestCase { + var transport: TMemoryBufferTransport = TMemoryBufferTransport(flushHandler: { + $0.reset(readBuffer: $1) + }) + + var proto: TBinaryProtocol! + + override func setUp() { + super.setUp() + proto = TBinaryProtocol(on: transport) + transport.reset() + } + + override func tearDown() { + super.tearDown() + transport.reset() + } + + func testInt8WriteRead() { + let writeVal: UInt8 = 250 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: UInt8 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with UInt8, wrote \(writeVal) but read \(readVal)") + } + + func testInt16WriteRead() { + + let writeVal: Int16 = 12312 + try? proto.write(writeVal) + try? transport.flush() + let readVal: Int16 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with Int16, wrote \(writeVal) but read \(readVal)") + } + + func testInt32WriteRead() { + let writeVal: Int32 = 2029234 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Int32 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with Int32, wrote \(writeVal) but read \(readVal)") + } + + func testInt64WriteRead() { + let writeVal: Int64 = 234234981374134 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Int64 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with Int64, wrote \(writeVal) but read \(readVal)") + } + + func testDoubleWriteRead() { + let writeVal: Double = 3.1415926 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Double = (try? proto.read()) ?? 0.0 + XCTAssertEqual(writeVal, readVal, "Error with Double, wrote \(writeVal) but read \(readVal)") + } + + func testBoolWriteRead() { + let writeVal: Bool = true + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Bool = (try? proto.read()) ?? false + XCTAssertEqual(writeVal, readVal, "Error with Bool, wrote \(writeVal) but read \(readVal)") + } + + func testStringWriteRead() { + let writeVal: String = "Hello World" + try? proto.write(writeVal) + try? transport.flush() + + let readVal: String! + do { + readVal = try proto.read() + } catch let error { + XCTAssertFalse(true, "Error reading \(error)") + return + } + + XCTAssertEqual(writeVal, readVal, "Error with String, wrote \(writeVal) but read \(readVal)") + } + + func testDataWriteRead() { + let writeVal: Data = "Data World".data(using: .utf8)! + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Data = (try? proto.read()) ?? "Goodbye World".data(using: .utf8)! + XCTAssertEqual(writeVal, readVal, "Error with Data, wrote \(writeVal) but read \(readVal)") + } + + func testStructWriteRead() { + let msg = "Test Protocol Error" + let writeVal = TApplicationError(error: .protocolError, message: msg) + do { + try writeVal.write(to: proto) + try? transport.flush() + + } catch let error { + XCTAssertFalse(true, "Caught Error attempting to write \(error)") + } + + do { + let readVal = try TApplicationError.read(from: proto) + XCTAssertEqual(readVal.error.thriftErrorCode, writeVal.error.thriftErrorCode, "Error case mismatch, expected \(readVal.error) got \(writeVal.error)") + XCTAssertEqual(readVal.message, writeVal.message, "Error message mismatch, expected \(readVal.message) got \(writeVal.message)") + } catch let error { + XCTAssertFalse(true, "Caught Error attempting to read \(error)") + } + } + func testUnsafeBitcastUpdate() { + let value: Double = 3.14159 + let val: Int64 = 31415926 + let uval: UInt64 = 31415926 + + let i64 = Int64(bitPattern: value.bitPattern) + let ubc = unsafeBitCast(value, to: Int64.self) + + XCTAssertEqual(i64, ubc, "Bitcast Double-> i64 Values don't match") + + let dbl = Double(bitPattern: UInt64(val)) + let ubdb = unsafeBitCast(val, to: Double.self) + + XCTAssertEqual(dbl, ubdb, "Bitcast i64 -> Double Values don't match") + + let db2 = Double(bitPattern: uval) + let usbc2 = unsafeBitCast(uval, to: Double.self) + + XCTAssertEqual(db2, usbc2, "Bitcast u64 -> Double Values don't match") + + + } + + static var allTests : [(String, (TBinaryProtocolTests) -> () throws -> Void)] { + return [ + ("testInt8WriteRead", testInt8WriteRead), + ("testInt16WriteRead", testInt16WriteRead), + ("testInt32WriteRead", testInt32WriteRead), + ("testInt64WriteRead", testInt64WriteRead), + ("testDoubleWriteRead", testDoubleWriteRead), + ("testBoolWriteRead", testBoolWriteRead), + ("testStringWriteRead", testStringWriteRead), + ("testDataWriteRead", testDataWriteRead), + ("testStructWriteRead", testStructWriteRead) + ] + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TCompactProtocolTests.swift b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TCompactProtocolTests.swift new file mode 100644 index 000000000..882c26068 --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/TCompactProtocolTests.swift @@ -0,0 +1,210 @@ +// +// TCompactProtocolTests.swift +// Thrift +// +// Created by Christopher Simpson on 8/19/16. +// +// + +import XCTest +import Foundation +@testable import Thrift + + +/// Testing Binary protocol read/write against itself +/// Uses separate read/write transport/protocols +class TCompactProtocolTests: XCTestCase { + var transport: TMemoryBufferTransport = TMemoryBufferTransport(flushHandler: { + $0.reset(readBuffer: $1) + }) + var proto: TCompactProtocol! + + override func setUp() { + super.setUp() + proto = TCompactProtocol(on: transport) + transport.reset() + } + + override func tearDown() { + super.tearDown() + transport.reset() + } + + func testInt8WriteRead() { + let writeVal: UInt8 = 250 + try? proto.write(writeVal) + try? transport.flush() + let readVal: UInt8 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with UInt8, wrote \(writeVal) but read \(readVal)") + } + + func testInt16WriteRead() { + let writeVal: Int16 = 12312 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Int16 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with Int16, wrote \(writeVal) but read \(readVal)") + } + + func testInt32WriteRead() { + let writeVal: Int32 = 2029234 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Int32 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with Int32, wrote \(writeVal) but read \(readVal)") + } + + func testInt64WriteRead() { + let writeVal: Int64 = 234234981374134 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Int64 = (try? proto.read()) ?? 0 + XCTAssertEqual(writeVal, readVal, "Error with Int64, wrote \(writeVal) but read \(readVal)") + } + + func testDoubleWriteRead() { + let writeVal: Double = 3.1415926 + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Double = (try? proto.read()) ?? 0.0 + XCTAssertEqual(writeVal, readVal, "Error with Double, wrote \(writeVal) but read \(readVal)") + } + + func testBoolWriteRead() { + let writeVal: Bool = true + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Bool = (try? proto.read()) ?? false + XCTAssertEqual(writeVal, readVal, "Error with Bool, wrote \(writeVal) but read \(readVal)") + } + + func testStringWriteRead() { + let writeVal: String = "Hello World" + try? proto.write(writeVal) + try? transport.flush() + + let readVal: String! + do { + readVal = try proto.read() + } catch let error { + XCTAssertFalse(true, "Error reading \(error)") + return + } + + XCTAssertEqual(writeVal, readVal, "Error with String, wrote \(writeVal) but read \(readVal)") + } + + func testDataWriteRead() { + let writeVal: Data = "Data World".data(using: .utf8)! + try? proto.write(writeVal) + try? transport.flush() + + let readVal: Data = (try? proto.read()) ?? "Goodbye World".data(using: .utf8)! + XCTAssertEqual(writeVal, readVal, "Error with Data, wrote \(writeVal) but read \(readVal)") + } + + func testStructWriteRead() { + let msg = "Test Protocol Error" + let writeVal = TApplicationError(error: .protocolError, message: msg) + do { + try writeVal.write(to: proto) + try transport.flush() + + } catch let error { + XCTAssertFalse(true, "Caught Error attempting to write \(error)") + } + + do { + let readVal = try TApplicationError.read(from: proto) + XCTAssertEqual(readVal.error.thriftErrorCode, writeVal.error.thriftErrorCode, "Error case mismatch, expected \(readVal.error) got \(writeVal.error)") + XCTAssertEqual(readVal.message, writeVal.message, "Error message mismatch, expected \(readVal.message) got \(writeVal.message)") + } catch let error { + XCTAssertFalse(true, "Caught Error attempting to read \(error)") + } + } + + func testInt32ZigZag() { + let zero: Int32 = 0 + let one: Int32 = 1 + let nOne: Int32 = -1 + let two: Int32 = 2 + let nTwo: Int32 = -2 + let max = Int32.max + let min = Int32.min + + XCTAssertEqual(proto.i32ToZigZag(zero), UInt32(0), "Error 32bit zigzag on \(zero)") + XCTAssertEqual(proto.zigZagToi32(0), zero, "Error 32bit zigzag on \(zero)") + + XCTAssertEqual(proto.i32ToZigZag(nOne), UInt32(1), "Error 32bit zigzag on \(nOne)") + XCTAssertEqual(proto.zigZagToi32(1), nOne, "Error 32bit zigzag on \(nOne)") + + XCTAssertEqual(proto.i32ToZigZag(one), UInt32(2), "Error 32bit zigzag on \(one)") + XCTAssertEqual(proto.zigZagToi32(2), one, "Error 32bit zigzag on \(one)") + + XCTAssertEqual(proto.i32ToZigZag(nTwo), UInt32(3), "Error 32bit zigzag on \(nTwo)") + XCTAssertEqual(proto.zigZagToi32(3), nTwo, "Error 32bit zigzag on \(nTwo)") + + XCTAssertEqual(proto.i32ToZigZag(two), UInt32(4), "Error 32bit zigzag on \(two)") + XCTAssertEqual(proto.zigZagToi32(4), two, "Error 32bit zigzag on \(two)") + + let uMaxMinusOne: UInt32 = UInt32.max - 1 + XCTAssertEqual(proto.i32ToZigZag(max), uMaxMinusOne, "Error 32bit zigzag on \(max)") + XCTAssertEqual(proto.zigZagToi32(uMaxMinusOne), max, "Error 32bit zigzag on \(max)") + + XCTAssertEqual(proto.i32ToZigZag(min), UInt32.max, "Error 32bit zigzag on \(min)") + XCTAssertEqual(proto.zigZagToi32(UInt32.max), min, "Error 32bit zigzag on \(min)") + } + + func testInt64ZigZag() { + let zero: Int64 = 0 + let one: Int64 = 1 + let nOne: Int64 = -1 + let two: Int64 = 2 + let nTwo: Int64 = -2 + let max = Int64.max + let min = Int64.min + + XCTAssertEqual(proto.i64ToZigZag(zero), UInt64(0), "Error 64bit zigzag on \(zero)") + XCTAssertEqual(proto.zigZagToi64(0), zero, "Error 64bit zigzag on \(zero)") + + XCTAssertEqual(proto.i64ToZigZag(nOne), UInt64(1), "Error 64bit zigzag on \(nOne)") + XCTAssertEqual(proto.zigZagToi64(1), nOne, "Error 64bit zigzag on \(nOne)") + + XCTAssertEqual(proto.i64ToZigZag(one), UInt64(2), "Error 64bit zigzag on \(one)") + XCTAssertEqual(proto.zigZagToi64(2), one, "Error 64bit zigzag on \(one)") + + XCTAssertEqual(proto.i64ToZigZag(nTwo), UInt64(3), "Error 64bit zigzag on \(nTwo)") + XCTAssertEqual(proto.zigZagToi64(3), nTwo, "Error 64bit zigzag on \(nTwo)") + + XCTAssertEqual(proto.i64ToZigZag(two), UInt64(4), "Error 64bit zigzag on \(two)") + XCTAssertEqual(proto.zigZagToi64(4), two, "Error 64bit zigzag on \(two)") + + let uMaxMinusOne: UInt64 = UInt64.max - 1 + XCTAssertEqual(proto.i64ToZigZag(max), uMaxMinusOne, "Error 64bit zigzag on \(max)") + XCTAssertEqual(proto.zigZagToi64(uMaxMinusOne), max, "Error 64bit zigzag on \(max)") + + XCTAssertEqual(proto.i64ToZigZag(min), UInt64.max, "Error 64bit zigzag on \(min)") + XCTAssertEqual(proto.zigZagToi64(UInt64.max), min, "Error 64bit zigzag on \(min)") + } + + static var allTests : [(String, (TCompactProtocolTests) -> () throws -> Void)] { + return [ + ("testInt8WriteRead", testInt8WriteRead), + ("testInt16WriteRead", testInt16WriteRead), + ("testInt32WriteRead", testInt32WriteRead), + ("testInt64WriteRead", testInt64WriteRead), + ("testDoubleWriteRead", testDoubleWriteRead), + ("testBoolWriteRead", testBoolWriteRead), + ("testStringWriteRead", testStringWriteRead), + ("testDataWriteRead", testDataWriteRead), + ("testStructWriteRead", testStructWriteRead), + ("testInt32ZigZag", testInt32ZigZag), + ("testInt64ZigZag", testInt64ZigZag) + ] + } +} diff --git a/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/ThriftTests.swift b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/ThriftTests.swift new file mode 100644 index 000000000..ae47f388c --- /dev/null +++ b/src/jaegertracing/thrift/lib/swift/Tests/ThriftTests/ThriftTests.swift @@ -0,0 +1,18 @@ +import XCTest +@testable import Thrift + +class ThriftTests: XCTestCase { + func testVersion() { + XCTAssertEqual(Thrift().version, "0.13.0") + } + + func test_in_addr_extension() { + + } + + static var allTests : [(String, (ThriftTests) -> () throws -> Void)] { + return [ + ("testVersion", testVersion), + ] + } +} |