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