Commit fb270557 authored by Ignacio Corderi's avatar Ignacio Corderi
Browse files

Refactored commands

parent 014d0021
Loading
Loading
Loading
Loading
+33 −1
Original line number Diff line number Diff line
@@ -12,6 +12,12 @@
		3EDAAB481B66D32D00F30808 /* Kinetic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3EDAAB3D1B66D32D00F30808 /* Kinetic.framework */; };
		3EDAAB4D1B66D32D00F30808 /* KineticTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDAAB4C1B66D32D00F30808 /* KineticTests.swift */; };
		3EDAAB581B66D47200F30808 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDAAB571B66D47200F30808 /* Client.swift */; };
		3EFB7F1C1B7A55D100988886 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EFB7F1B1B7A55D100988886 /* Core.swift */; };
		3EFB7F1E1B7A6A9800988886 /* Put.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EFB7F1D1B7A6A9800988886 /* Put.swift */; };
		3EFB7F201B7A6C3300988886 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EFB7F1F1B7A6C3300988886 /* Errors.swift */; };
		3EFB7F221B7A7A8000988886 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EFB7F211B7A7A7F00988886 /* Utils.swift */; };
		3EFB7F251B7A977500988886 /* Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EFB7F241B7A977500988886 /* Common.swift */; };
		3EFB7F271B7AA45A00988886 /* Get.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EFB7F261B7AA45A00988886 /* Get.swift */; };
		921636AE408C3549DE7A1141 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 382D1C00FC0EC4F1BF676411 /* Pods.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
/* End PBXBuildFile section */

@@ -36,6 +42,12 @@
		3EDAAB4E1B66D32D00F30808 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		3EDAAB571B66D47200F30808 /* Client.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = "<group>"; };
		3EFB7F121B78EE0900988886 /* KineticPlayground.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = KineticPlayground.playground; path = "../../../../Library/Autosave Information/KineticPlayground.playground"; sourceTree = "<group>"; };
		3EFB7F1B1B7A55D100988886 /* Core.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Core.swift; sourceTree = "<group>"; };
		3EFB7F1D1B7A6A9800988886 /* Put.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Put.swift; sourceTree = "<group>"; };
		3EFB7F1F1B7A6C3300988886 /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
		3EFB7F211B7A7A7F00988886 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
		3EFB7F241B7A977500988886 /* Common.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = "<group>"; };
		3EFB7F261B7AA45A00988886 /* Get.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Get.swift; sourceTree = "<group>"; };
		80777030020DBD5A301CA196 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
		FB4A7FCA365ECAF75E20A4A9 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -93,9 +105,13 @@
			isa = PBXGroup;
			children = (
				3E07EA7E1B78E3B500DAB3F1 /* Kinetic.proto.swift */,
				3EFB7F211B7A7A7F00988886 /* Utils.swift */,
				3EFB7F1F1B7A6C3300988886 /* Errors.swift */,
				3EFB7F1B1B7A55D100988886 /* Core.swift */,
				3EDAAB571B66D47200F30808 /* Client.swift */,
				3EFB7F231B7A951000988886 /* Commands */,
				3EDAAB401B66D32D00F30808 /* Kinetic.h */,
				3EDAAB421B66D32D00F30808 /* Info.plist */,
				3EDAAB571B66D47200F30808 /* Client.swift */,
			);
			path = Kinetic;
			sourceTree = "<group>";
@@ -109,6 +125,16 @@
			path = KineticTests;
			sourceTree = "<group>";
		};
		3EFB7F231B7A951000988886 /* Commands */ = {
			isa = PBXGroup;
			children = (
				3EFB7F241B7A977500988886 /* Common.swift */,
				3EFB7F1D1B7A6A9800988886 /* Put.swift */,
				3EFB7F261B7AA45A00988886 /* Get.swift */,
			);
			name = Commands;
			sourceTree = "<group>";
		};
		851E710153FE57DC96E4723A /* Pods */ = {
			isa = PBXGroup;
			children = (
@@ -261,8 +287,14 @@
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				3EFB7F1C1B7A55D100988886 /* Core.swift in Sources */,
				3EFB7F201B7A6C3300988886 /* Errors.swift in Sources */,
				3EFB7F1E1B7A6A9800988886 /* Put.swift in Sources */,
				3EDAAB581B66D47200F30808 /* Client.swift in Sources */,
				3EFB7F271B7AA45A00988886 /* Get.swift in Sources */,
				3E07EA7F1B78E3B500DAB3F1 /* Kinetic.proto.swift in Sources */,
				3EFB7F221B7A7A8000988886 /* Utils.swift in Sources */,
				3EFB7F251B7A977500988886 /* Common.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
+5 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
   type = "0"
   version = "2.0">
</Bucket>
+65 −103
Original line number Diff line number Diff line
//
//  Client.swift
//  Kinetic
//
//  Created by Ignacio Corderi on 7/27/15.
//  Copyright © 2015 Seagate. All rights reserved.
//
// Copyright (c) 2015 Seagate Technology

import Foundation
import CryptoSwift
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

typealias Message = Com.Seagate.Kinetic.Proto.Message_
typealias Command = Com.Seagate.Kinetic.Proto.Command
public typealias Bytes = [UInt8]

public enum KineticConnectionErrors: ErrorType {
    case InvalidMagicNumber
}
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

func bytesToUInt32(bytes:Array<UInt8>, offset:Int) -> UInt32 {
    let upper = UInt32(bytes[offset]) << 24 | UInt32(bytes[offset+1]) << 16
    let lower = UInt32(bytes[offset+2]) << 8 | UInt32(bytes[offset+3])
    return upper | lower
}
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

func copyFromUInt32(inout bytes:Array<UInt8>, offset:Int, value:UInt32) {
    bytes[offset+0] = UInt8((value & 0xFF000000) >> 24)
    bytes[offset+1] = UInt8((value & 0x00FF0000) >> 16)
    bytes[offset+2] = UInt8((value & 0x0000FF00) >> 8)
    bytes[offset+3] = UInt8((value & 0x000000FF))
}
// @author: Ignacio Corderi

public extension String {
    func toUtf8() -> Bytes {
        var decodedBytes = Bytes()
        for b in self.utf8 {
            decodedBytes.append(b)
        }
        return decodedBytes
    }    
}

public extension NSData {
    func hmacSha1(key: String) -> NSData {
        // create array of appropriate length:
        var array = Bytes(count: self.length + 4, repeatedValue: 0)
        copyFromUInt32(&array, offset: 0, value: UInt32(self.length))
        
        // copy bytes into array
        self.getBytes(&array + 4, length: self.length) // gives me goosebumps
        
        //mac.update(struct.pack(">I", len(entity)))
        //mac.update(entity)
       
        let hmac = Authenticator.HMAC(key: key.toUtf8(), variant: .sha1).authenticate(array)!
        return NSData(bytes: hmac, length: hmac.count)
    }
}
import Foundation
import CryptoSwift

public func connect(host:String, port:Int) throws -> Client {
    let c = Client(host: host, port: port)
@@ -63,7 +29,7 @@ public func connect(host:String, port:Int) throws -> Client {
    return c
}

public class Client : CustomStringConvertible {
public class Client : CustomStringConvertible, SynchornousChannel {
    
    public let host: String
    public let port: Int
@@ -111,48 +77,34 @@ public class Client : CustomStringConvertible {
        self.inp!.open()
        self.out!.open()
        
        let (msg, _) = try self.receive()
        let (msg, _) = try self.rawReceive()
        
        self.handshake = try Command.parseFromData(msg.commandBytes)
    }
    
    func send(msgBldr: Message.Builder, cmdBldr: Command.Builder, value: Bytes?) throws {
        let headerBldr = cmdBldr.getHeaderBuilder()
        headerBldr.clusterVersion = self.clusterVersion
        headerBldr.connectionId = self.connectionId
        headerBldr.sequence = ++self.sequenceId
        
        let cmd = try cmdBldr.build()
        print(cmd)
        msgBldr.commandBytes = cmd.data()
        let hmacBldr = msgBldr.getHmacAuthBuilder()
        hmacBldr.identity = self.identity
        hmacBldr.hmac = msgBldr.commandBytes.hmacSha1(self.key)
        msgBldr.authType = Message.AuthType.Hmacauth
        print("HMAC: \(hmacBldr.hmac)")
        
        let msg = try msgBldr.build()
        let bytes = msg.data()
        
        // Line header
    private func rawSend(proto: NSData, value: Bytes?) throws {
        // Prepare 9 bytes header
        // 1 byte - magic number | 4 bytes - proto length | 4 bytes - value length
        var headerBuffer = Bytes(count: 9, repeatedValue: 0)
        headerBuffer[0] = 70 // Magic
        copyFromUInt32(&headerBuffer, offset: 1, value: UInt32(bytes.length))
        copyFromUInt32(&headerBuffer, offset: 1, value: UInt32(proto.length))
        if value != nil {
            copyFromUInt32(&headerBuffer, offset: 5, value: UInt32(value!.count))
        }
        
        // Send header, proto and value
        let outputStream = self.out!
        outputStream.write(headerBuffer, maxLength: headerBuffer.count)
        var array = Bytes(count: bytes.length, repeatedValue: 0)
        bytes.getBytes(&array, length: bytes.length)
        var array = Bytes(count: proto.length, repeatedValue: 0)
        // TODO: make sure this is a non-memcopy operation
        proto.getBytes(&array, length: proto.length)
        outputStream.write(array, maxLength: array.count)
        if value != nil {
            outputStream.write(value!, maxLength: value!.count)
        }
    }
    
    func receive() throws -> (Message, Bytes) {
    private func rawReceive() throws -> (Message, Bytes) {
        let inputStream = self.inp!
        
        var headerBuffer = Bytes(count:9, repeatedValue: 0)
@@ -186,31 +138,41 @@ public class Client : CustomStringConvertible {
        }
    }
    
    public func put(key: Bytes, value: Bytes) throws {
        let msgBldr = Message.Builder()
        let cmdBldr = Command.Builder()
        let headerBldr = cmdBldr.getHeaderBuilder()
        headerBldr.messageType = .Put
        let kvBldr = cmdBldr.getBodyBuilder().getKeyValueBuilder()
        kvBldr.key = NSData(bytes: key, length: key.count)
        try self.send(msgBldr, cmdBldr: cmdBldr, value: value)
        let (respMsg, _) = try self.receive()
        let respCmd = try Command.parseFromData(respMsg.commandBytes)
        print(respCmd.status!.code)
    }
    
    public func get(key: Bytes) throws -> Bytes {
        let msgBldr = Message.Builder()
        let cmdBldr = Command.Builder()
        let headerBldr = cmdBldr.getHeaderBuilder()
        headerBldr.messageType = .Get
        let kvBldr = cmdBldr.getBodyBuilder().getKeyValueBuilder()
        kvBldr.key = NSData(bytes: key, length: key.count)
        try self.send(msgBldr, cmdBldr: cmdBldr, value: nil)
        let (respMsg, value) = try self.receive()
    public func send<C: ChannelCommand>(cmd: C) throws -> C.ResponseType {
        // Prepare command contents
        let builder = cmd.build(Builder())
        
        // Prepare header
        let h = builder.header
        h.clusterVersion = self.clusterVersion
        h.connectionId = self.clusterVersion
        h.sequence = self.clusterVersion
        
        let m = builder.message
        
        // Build command proto
        let cmdProto = try builder.command.build()
        print(cmdProto) // TODO: Remove this line
        m.commandBytes = cmdProto.data()
        
        // Prepare authentication
        let a = m.getHmacAuthBuilder()
        a.identity = self.identity
        a.hmac = m.commandBytes.hmacSha1(self.key)
        m.authType = .Hmacauth
        
        // Build message proto
        let msgProto = try m.build()
        
        // Send & Receive
        try self.rawSend(msgProto.data(), value: builder.value)
        let (respMsg, respValue) = try self.rawReceive()
        
        // Unwrap command
        let respCmd = try Command.parseFromData(respMsg.commandBytes)
        print(respCmd.status!.code)
        return value
        
        let r = RawResponse(message: respMsg, command: respCmd, value: respValue)
        return C.ResponseType.parse(r)
    }
    
}
 No newline at end of file

Kinetic/Common.swift

0 → 100644
+65 −0
Original line number Diff line number Diff line
// Copyright (c) 2015 Seagate Technology

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// @author: Ignacio Corderi

public struct EmptyResponse : ChannelResponse {
    public let success: Bool
    public let error: KineticRemoteError?
    
    public static func parse(raw: RawResponse) -> EmptyResponse {
        return EmptyResponse(success: raw.command.status.code == .Success,
            error: KineticRemoteError.fromStatus(raw.command.status))
    }
}

public struct ValueResponse : ChannelResponse {
    public let success: Bool
    public let error: KineticRemoteError?
    public let value: Bytes?
    
    public var hasValue: Bool { return value != nil }
    
    public static func parse(raw: RawResponse) -> ValueResponse {
        switch raw.command.status.code {
        case .Success, .NotFound:
            return ValueResponse(success: true, error: nil, value: raw.value)
        default:
            return ValueResponse( success: false,
            error: KineticRemoteError.fromStatus(raw.command.status),
            value: raw.value)
        }
    }
    
    public var description: String {
        get {
            if self.success {
                if self.hasValue {
                    return "Success (length: \(self.value!.count))"
                } else {
                    return "Success (Empty)"
                }
            } else {
                return "\(self.error?.code): \(self.error?.message)"
            }
        }
    }

}
 No newline at end of file

Kinetic/Core.swift

0 → 100644
+95 −0
Original line number Diff line number Diff line
// Copyright (c) 2015 Seagate Technology

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// @author: Ignacio Corderi

typealias Message = Com.Seagate.Kinetic.Proto.Message_
typealias Command = Com.Seagate.Kinetic.Proto.Command


public class Builder {
    var message: Message.Builder
    var command: Command.Builder
    var value: Bytes?
    
    internal init() {
        self.message = Message.Builder()
        self.command = Command.Builder()
        self.value = nil
    }
}

// Basic
extension Builder {
    var header:Command.Header.Builder { return self.command.getHeaderBuilder() }
    var body:Command.Body.Builder { return self.command.getBodyBuilder() }
}

// Expanded body
extension Builder {
    var keyValue:Command.KeyValue.Builder { return self.body.getKeyValueBuilder() }
    var range:Command.Range.Builder { return self.body.getRangeBuilder() }
    var setup:Command.Setup.Builder { return self.body.getSetupBuilder() }
    var p2pOperation:Command.P2Poperation.Builder { return self.body.getP2POperationBuilder() }
    var getLog:Command.GetLog.Builder { return self.body.getGetLogBuilder() }
    var security:Command.Security.Builder { return self.body.getSecurityBuilder() }
    var pinOperation:Command.PinOperation.Builder { return self.body.getPinOpBuilder() }
    var batch:Command.Batch.Builder { return self.body.getBatchBuilder() }
}

public struct RawResponse {
    var message: Message
    var command: Command
    var value: Bytes?
}

public protocol ChannelCommand {
    typealias ResponseType: ChannelResponse
    func build(builder: Builder) -> Builder
}

public extension ChannelCommand {
    func sendTo(channel: SynchornousChannel) throws -> Self.ResponseType {
        return try channel.send(self)
    }
}

public protocol ChannelResponse : CustomStringConvertible {
    var success: Bool { get }
    var error: KineticRemoteError? { get }
    static func parse(raw: RawResponse) -> Self
}

public extension ChannelResponse {
    var failed: Bool { return !self.success }
    var description: String {
        get {
            if self.success {
                return "Success"
            } else {
                return "\(self.error?.code): \(self.error?.message)"
            }
        }
    }
}

public protocol SynchornousChannel {
    func send<C: ChannelCommand>(cmd: C) throws -> C.ResponseType
}
 No newline at end of file
Loading