Upgrading dependency to Thrift 0.12.0

This commit is contained in:
Renan DelValle 2018-11-27 18:03:50 -08:00
parent 3e4590dcc0
commit 356978cb42
No known key found for this signature in database
GPG key ID: C240AD6D6F443EC9
1302 changed files with 101701 additions and 26784 deletions

View file

@ -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"
)

216
vendor/git.apache.org/thrift.git/lib/swift/README.md generated vendored Normal file
View file

@ -0,0 +1,216 @@
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()
```

View file

@ -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

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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(bytes: [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(bytes: [UInt8(0xff & (value >> 8))]))
buff.append(Data(bytes: [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(bytes: [UInt8(0xff & (value >> 24))]))
buff.append(Data(bytes: [UInt8(0xff & (value >> 16))]))
buff.append(Data(bytes: [UInt8(0xff & (value >> 8))]))
buff.append(Data(bytes: [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(bytes: [UInt8(0xff & (value >> 56))]))
buff.append(Data(bytes: [UInt8(0xff & (value >> 48))]))
buff.append(Data(bytes: [UInt8(0xff & (value >> 40))]))
buff.append(Data(bytes: [UInt8(0xff & (value >> 32))]))
buff.append(Data(bytes: [UInt8(0xff & (value >> 24))]))
buff.append(Data(bytes: [UInt8(0xff & (value >> 16))]))
buff.append(Data(bytes: [UInt8(0xff & (value >> 8))]))
buff.append(Data(bytes: [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)
}
}
}

View file

@ -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
}
}
}

View file

@ -0,0 +1,575 @@
/*
* 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(bytes: [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(bytes: 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(bytes: 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)
}
}
}

View file

@ -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)
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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
}
}

View file

@ -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(bytes: [UInt8(0xff & (size >> 24))]))
data.append(Data(bytes: [UInt8(0xff & (size >> 16))]))
data.append(Data(bytes: [UInt8(0xff & (size >> 8))]))
data.append(Data(bytes: [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()
}
}

View file

@ -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
}
}
}

View file

@ -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 }
}

View file

@ -0,0 +1,194 @@
/*
* 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 = Storage.IndexDistance
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 updateValue(value, forKey: key)
}
public mutating func removeAtIndex(_ index: DictionaryIndex<Key, Value>) -> (Key, Value) {
return removeAtIndex(index)
}
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 }
}

View file

@ -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)
}
}

View file

@ -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)
}
}
}

View file

@ -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)
}

View file

@ -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 {
public 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()
}
public func validateValue(_ value: Any?, named name: String) throws {
if value == nil {
throw TProtocolError(error: .unknown, message: "Missing required value for field: \(name)")
}
}
public func readResultMessageBegin() throws {
let (_, type, _) = try readMessageBegin();
if type == .exception {
let x = try readException()
throw x
}
return
}
public func readException() throws -> TApplicationError {
return try TApplicationError.read(from: self)
}
public 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()
}
public 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:
return
}
}
}

View file

@ -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
}
}

View file

@ -0,0 +1,229 @@
/*
* 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)
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: .defaultRunLoopMode)
inputStream?.open()
outputStream = writeStream!.takeRetainedValue()
outputStream?.schedule(in: .current, forMode: .defaultRunLoopMode)
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

View file

@ -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() { }
}

View file

@ -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)
}
}

View file

@ -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 = Storage.IndexDistance
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
}

View file

@ -0,0 +1,149 @@
/*
* 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])
}
}
}

View file

@ -0,0 +1,210 @@
/*
* 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
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: .defaultRunLoopMode)
inputStream.open()
outputStream = writeStream as OutputStream
outputStream.schedule(in: .current, forMode: .defaultRunLoopMode)
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(bytes: 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)
}
}

View file

@ -0,0 +1,143 @@
/*
* 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)
/// 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(bytes: 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(bytes: 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: .defaultRunLoopMode)
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: .defaultRunLoopMode)
output = nil
}
}
}
#endif

View file

@ -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 {
public static var fieldIds: [String: (id: Int32, type: TType)] { return [:] }
public static var thriftType: TType { return .struct }
public 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()
}
public 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
}
}

View file

@ -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
}

View file

@ -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() { }
}

View file

@ -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)
}
}

View file

@ -0,0 +1,3 @@
class Thrift {
let version = "1.1.0"
}

View file

@ -0,0 +1,8 @@
import XCTest
@testable import ThriftTests
XCTMain([
testCase(ThriftTests.allTests),
testCase(TBinaryProtocolTests.allTests),
testCase(TCompactProtocolTests.allTests),
])

View file

@ -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)
]
}
}

View file

@ -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)
]
}
}

View file

@ -0,0 +1,18 @@
import XCTest
@testable import Thrift
class ThriftTests: XCTestCase {
func testVersion() {
XCTAssertEqual(Thrift().version, "1.1.0")
}
func test_in_addr_extension() {
}
static var allTests : [(String, (ThriftTests) -> () throws -> Void)] {
return [
("testVersion", testVersion),
]
}
}