1 // Licensed to the Apache Software Foundation (ASF) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The ASF licenses this file
5 // to you under the Apache License, Version 2.0 (the
6 // "License"); you may not use this file except in compliance
7 // with the License. You may obtain a copy of the License at
8 //
9 //   http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing,
12 // software distributed under the License is distributed on an
13 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 // KIND, either express or implied. See the License for the
15 // specific language governing permissions and limitations
16 // under the License.
17 
18 import Foundation
19 import Thrift
20 
21 public enum Protocol: String {
22   case binary
23   case compact
24   case header
25   case json
26 }
27 
28 public enum Transport: String {
29   case buffered
30   case framed
31   case http
32   case anonpipe
33   case zlib
34 }
35 
36 public enum ServerType: String {
37   case simple
38   case threadPool = "thread-pool"
39   case threaded
40   case nonblocking
41 }
42 
43 public enum ParserError: Error {
44   case unknownArgument(argument: String)
45   case missingParameter(argument: String)
46   case invalidParameter(argument: String, parameter: String)
47 
48   case unsupportedOption
49 }
50 
51 public class ParametersBase {
52   public var showHelp = false
53   public var port: Int?
54   public var domainSocket: String?
55   public var namedPipe: String?
56   public var proto: Protocol?
57   public var transport: Transport?
58   public var multiplex = false
59   public var abstractNamespace = false
60   public var ssl = false
61   public var zlib = false
62 
63   public init (arguments: [String]) throws {
64     if arguments.count > 1 {
65       for argument in arguments[1...] {
66         let equalSignPos = argument.firstIndex(of: "=") ?? argument.endIndex
67         let name = String(argument[..<equalSignPos])
68         let value: String? = (equalSignPos < argument.endIndex) ? String(argument[argument.index(equalSignPos, offsetBy: 1)..<argument.endIndex]) : nil
69 
70         try processArgument(name: name, value: value)
71       }
72     }
73 
74     fillDefaults()
75     try checkSupported()
76   }
77 
processArgumentnull78   open func processArgument(name: String, value: String?) throws {
79     switch name {
80       case "-h", "--help":
81         showHelp = true
82       case "--port":
83         guard value != nil else { throw ParserError.missingParameter(argument: name) }
84         port = Int(value!)
85         guard port != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
86       case "--domain-socket":
87         guard value != nil else { throw ParserError.missingParameter(argument: name) }
88         domainSocket = value!
89       case "--named-pipe":
90         guard value != nil else { throw ParserError.missingParameter(argument: name) }
91         namedPipe = value!
92       case "--transport":
93         guard value != nil else { throw ParserError.missingParameter(argument: name) }
94         transport = Transport(rawValue: value!)
95         guard transport != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
96       case "--protocol":
97         guard value != nil else { throw ParserError.missingParameter(argument: name) }
98         proto = Protocol(rawValue: value!)
99         guard proto != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
100       case "--multiplex":
101         multiplex = true
102       case "--abstract-namespace":
103         abstractNamespace = true
104       case "--ssl":
105         ssl = true
106       case "--zlib":
107         zlib = true
108       default:
109         throw ParserError.unknownArgument(argument: name)
110     }
111   }
112 
fillDefaultsnull113   open func fillDefaults() {
114     if port == nil && domainSocket == nil && namedPipe == nil {
115       port = 9090
116     }
117 
118     if transport == nil {
119       transport = .buffered
120     }
121 
122     if proto == nil {
123       proto = .binary
124     }
125   }
126 
checkSupportednull127   open func checkSupported() throws {
128     guard transport == .buffered || transport == .framed else { throw ParserError.unsupportedOption }
129     guard proto == .binary || proto == .compact else { throw ParserError.unsupportedOption }
130   }
131 }
132 
133 public class TestClientParameters: ParametersBase {
134   public var host: String?
135   public var testLoops: Int?
136   public var threads: Int?
137 
printHelpnull138   public func printHelp() {
139     print("""
140 Allowed options:
141   -h | --help                  produce help message
142   --host=arg (localhost)       Host to connect
143   --port=arg (9090)            Port number to connect
144   --domain-socket=arg          Domain Socket (e.g. /tmp/ThriftTest.thrift),
145                                instead of host and port
146   --named-pipe=arg             Windows Named Pipe (e.g. MyThriftPipe)
147   --anon-pipes hRead hWrite    Windows Anonymous Pipes pair (handles)
148   --abstract-namespace         Create the domain socket in the Abstract Namespace
149                                (no connection with filesystem pathnames)
150   --transport=arg (buffered)   Transport: buffered, framed, http, evhttp, zlib
151   --protocol=arg (binary)      Protocol: binary, compact, header, json
152   --multiplex                  Add TMultiplexedProtocol service name "ThriftTest"
153   --ssl                        Encrypted Transport using SSL
154   --zlib                       Wrap Transport with Zlib
155   -n=arg | --testloops=arg (1) Number of Tests
156   -t=arg | --threads=arg (1)   Number of Test threads
157 """)
158   }
159 
processArgumentnull160   open override func processArgument(name: String, value: String?) throws {
161     switch name {
162       case "--host":
163         guard value != nil else { throw ParserError.missingParameter(argument: name) }
164         host = value!
165       case "-n", "--testloops":
166         guard value != nil else { throw ParserError.missingParameter(argument: name) }
167         testLoops = Int(value!)
168         guard testLoops != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
169       case "-t", "--threads":
170         guard value != nil else { throw ParserError.missingParameter(argument: name) }
171         threads = Int(value!)
172         guard threads != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
173       default:
174         try super.processArgument(name: name, value: value)
175     }
176   }
177 
fillDefaultsnull178   open override func fillDefaults() {
179     super.fillDefaults()
180 
181     if host == nil {
182       host = "localhost"
183     }
184 
185     if testLoops == nil {
186       testLoops = 1
187     }
188 
189     if threads == nil {
190       threads = 4
191     }
192   }
193 }
194 
195 public class TestServerParameters: ParametersBase {
196   public var serverType: ServerType?
197   public var processorEvents = false
198   public var workers: Int?
199 
printHelpnull200   public func printHelp() {
201     print("""
202 Allowed options:
203   -h | --help                 produce help message
204   --port=arg (=9090)          Port number to listen
205   --domain-socket=arg         Unix Domain Socket (e.g. /tmp/ThriftTest.thrift)
206   --named-pipe=arg            Windows Named Pipe (e.g. MyThriftPipe)
207   --server-type=arg (=simple) type of server, "simple", "thread-pool",
208                               "threaded", or "nonblocking"
209   --transport=arg (=buffered) transport: buffered, framed, http, anonpipe, zlib
210   --protocol=arg (=binary)    protocol: binary, compact, header, json
211   --multiplex                 Add TMultiplexedProtocol service name "ThriftTest"
212   --abstract-namespace        Create the domain socket in the Abstract Namespace
213                               (no connection with filesystem pathnames)
214   --ssl                       Encrypted Transport using SSL
215   --zlib                      Wrapped Transport using Zlib
216   --processor-events          processor-events
217   -n=arg | --workers=arg (=4) Number of thread pools workers. Only valid for
218                               thread-pool server type
219 """)
220   }
221 
processArgumentnull222   open override func processArgument(name: String, value: String?) throws {
223     switch name {
224     case "--server-type":
225       guard value != nil else { throw ParserError.missingParameter(argument: name) }
226       serverType = ServerType(rawValue: value!)
227       guard serverType != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
228     case "--processor-events":
229       processorEvents = true
230     case "-n", "--workers":
231       guard value != nil else { throw ParserError.missingParameter(argument: name) }
232       workers = Int(value!)
233       guard workers != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
234     default:
235       try super.processArgument(name: name, value: value)
236     }
237   }
238 
fillDefaultsnull239   open override func fillDefaults() {
240     super.fillDefaults()
241 
242     if serverType == nil {
243       serverType = .simple
244     }
245 
246     if workers == nil {
247       workers = 4
248     }
249   }
250 
checkSupportednull251   open override func checkSupported() throws {
252     try super.checkSupported()
253     guard serverType == .simple else { throw ParserError.unsupportedOption }
254   }
255 }
256 
257