1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 
20 import Foundation
21 
22 public class TFramedTransport: TTransport {
23   public static let headerSize    = 4
24   public static let initFrameSize = 1024
25   private static let defaultMaxLength = 16384000
26 
27   public var transport: TTransport
28   private var writeBuffer = Data()
29 
30   private var maxSize     = TFramedTransport.defaultMaxLength
31   private var remainingBytes = 0
32 
33 
34   public init(transport: TTransport, maxSize: Int) {
35     self.transport = transport
36     self.maxSize = maxSize
37   }
38 
39   public convenience init(transport: TTransport) {
40     self.init(transport: transport, maxSize: TFramedTransport.defaultMaxLength)
41   }
42 
readHeadernull43   func readHeader() throws {
44     let read = try transport.readAll(size: TFramedTransport.headerSize)
45     remainingBytes = Int(decodeFrameSize(data: read))
46   }
47 
48   /// Mark: - TTransport
49 
readnull50   public func read(size: Int) throws -> Data {
51     while (remainingBytes <= 0) {
52         try readHeader()
53     }
54 
55     let toRead = min(size, remainingBytes)
56 
57     if toRead < 0 {
58         try close()
59         throw TTransportError(error: .negativeSize,
60                               message:  "Read a negative frame size (\(toRead))!")
61     }
62 
63     if toRead > maxSize {
64         try close()
65         throw TTransportError(error: .sizeLimit(limit: maxSize, got: toRead))
66     }
67 
68     let data = try transport.readAll(size: toRead)
69     remainingBytes -= data.count
70     return data
71   }
72 
flushnull73   public func flush() throws {
74     // copy buffer and reset
75     let buff = writeBuffer
76     writeBuffer = Data()
77 
78     let frameSize = encodeFrameSize(size: UInt32(buff.count))
79 
80     try transport.write(data: frameSize)
81     try transport.write(data: buff)
82     try transport.flush()
83   }
84 
writenull85   public func write(data: Data) throws {
86     writeBuffer.append(data)
87   }
88 
89 
90 
encodeFrameSizenull91   private func encodeFrameSize(size: UInt32) -> Data {
92     var data = Data()
93     data.append(Data([UInt8(0xff & (size >> 24))]))
94     data.append(Data([UInt8(0xff & (size >> 16))]))
95     data.append(Data([UInt8(0xff & (size >> 8))]))
96     data.append(Data([UInt8(0xff & (size))]))
97 
98     return data
99   }
100 
decodeFrameSizenull101   private func decodeFrameSize(data: Data) -> UInt32 {
102     var size: UInt32
103     size  = (UInt32(data[0] & 0xff) << 24)
104     size |= (UInt32(data[1] & 0xff) << 16)
105     size |= (UInt32(data[2] & 0xff) <<  8)
106     size |= (UInt32(data[3] & 0xff))
107     return size
108   }
109 
closenull110   public func close() throws {
111     try transport.close()
112   }
113 
opennull114   public func open() throws {
115     try transport.open()
116   }
117 
isOpennull118   public func isOpen() throws -> Bool {
119     return try transport.isOpen()
120   }
121 }
122