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,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"
}