blob: ef93509b2554f0663568b0aeac2faa0ceaea9cf6 [file] [log] [blame]
Mark Slee57cc25e2007-02-28 21:43:54 +00001#!/usr/bin/env python
Mark Sleec9676562006-09-05 17:34:52 +00002
David Reissea2cba82009-03-30 21:35:00 +00003#
4# Licensed to the Apache Software Foundation (ASF) under one
5# or more contributor license agreements. See the NOTICE file
6# distributed with this work for additional information
7# regarding copyright ownership. The ASF licenses this file
8# to you under the Apache License, Version 2.0 (the
9# "License"); you may not use this file except in compliance
10# with the License. You may obtain a copy of the License at
11#
12# http://www.apache.org/licenses/LICENSE-2.0
13#
14# Unless required by applicable law or agreed to in writing,
15# software distributed under the License is distributed on an
16# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17# KIND, either express or implied. See the License for the
18# specific language governing permissions and limitations
19# under the License.
20#
Nobuaki Sukegawa7b545b52016-01-11 13:46:04 +090021from __future__ import division
Jens Geyerd629ea02015-09-23 21:16:50 +020022import glob
23import logging
24import os
25import sys
26import time
Bryan Duxbury59d4efd2011-03-21 17:38:22 +000027from optparse import OptionParser
Mark Sleec9676562006-09-05 17:34:52 +000028
Nobuaki Sukegawaa185d7e2015-11-06 21:24:24 +090029SCRIPT_DIR = os.path.abspath(os.path.dirname(__file__))
30ROOT_DIR = os.path.dirname(os.path.dirname(SCRIPT_DIR))
31DEFAULT_LIBDIR_GLOB = os.path.join(ROOT_DIR, 'lib', 'py', 'build', 'lib.*')
Roger Meierf4eec7a2011-09-11 18:16:21 +000032
Mark Sleec9676562006-09-05 17:34:52 +000033
Jens Geyerd629ea02015-09-23 21:16:50 +020034class TestHandler(object):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090035 def testVoid(self):
36 if options.verbose > 1:
37 logging.info('testVoid()')
Mark Sleec9676562006-09-05 17:34:52 +000038
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090039 def testString(self, str):
40 if options.verbose > 1:
41 logging.info('testString(%s)' % str)
42 return str
Mark Sleec9676562006-09-05 17:34:52 +000043
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090044 def testBool(self, boolean):
45 if options.verbose > 1:
46 logging.info('testBool(%s)' % str(boolean).lower())
47 return boolean
Nobuaki Sukegawaa649e742015-09-21 13:53:25 +090048
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090049 def testByte(self, byte):
50 if options.verbose > 1:
51 logging.info('testByte(%d)' % byte)
52 return byte
Mark Sleec9676562006-09-05 17:34:52 +000053
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090054 def testI16(self, i16):
55 if options.verbose > 1:
56 logging.info('testI16(%d)' % i16)
57 return i16
Mark Sleec98d0502006-09-06 02:42:25 +000058
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090059 def testI32(self, i32):
60 if options.verbose > 1:
61 logging.info('testI32(%d)' % i32)
62 return i32
Mark Sleec98d0502006-09-06 02:42:25 +000063
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090064 def testI64(self, i64):
65 if options.verbose > 1:
66 logging.info('testI64(%d)' % i64)
67 return i64
Mark Sleec98d0502006-09-06 02:42:25 +000068
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090069 def testDouble(self, dub):
70 if options.verbose > 1:
71 logging.info('testDouble(%f)' % dub)
72 return dub
Mark Sleec98d0502006-09-06 02:42:25 +000073
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090074 def testBinary(self, thing):
75 if options.verbose > 1:
76 logging.info('testBinary()') # TODO: hex output
77 return thing
Jens Geyerd629ea02015-09-23 21:16:50 +020078
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090079 def testStruct(self, thing):
80 if options.verbose > 1:
81 logging.info('testStruct({%s, %s, %s, %s})' % (thing.string_thing, thing.byte_thing, thing.i32_thing, thing.i64_thing))
82 return thing
Mark Sleec98d0502006-09-06 02:42:25 +000083
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090084 def testException(self, arg):
85 # if options.verbose > 1:
86 logging.info('testException(%s)' % arg)
87 if arg == 'Xception':
88 raise Xception(errorCode=1001, message=arg)
89 elif arg == 'TException':
90 raise TException(message='This is a TException')
Roger Meier1f554e12013-01-05 20:38:35 +010091
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +090092 def testMultiException(self, arg0, arg1):
93 if options.verbose > 1:
94 logging.info('testMultiException(%s, %s)' % (arg0, arg1))
95 if arg0 == 'Xception':
96 raise Xception(errorCode=1001, message='This is an Xception')
97 elif arg0 == 'Xception2':
98 raise Xception2(
99 errorCode=2002,
100 struct_thing=Xtruct(string_thing='This is an Xception2'))
101 return Xtruct(string_thing=arg1)
Mark Sleec9676562006-09-05 17:34:52 +0000102
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900103 def testOneway(self, seconds):
104 if options.verbose > 1:
105 logging.info('testOneway(%d) => sleeping...' % seconds)
106 time.sleep(seconds / 3) # be quick
107 if options.verbose > 1:
108 logging.info('done sleeping')
David Reissdb893b62008-02-18 02:11:48 +0000109
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900110 def testNest(self, thing):
111 if options.verbose > 1:
112 logging.info('testNest(%s)' % thing)
113 return thing
David Reiss74421272008-11-07 23:09:31 +0000114
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900115 def testMap(self, thing):
116 if options.verbose > 1:
117 logging.info('testMap(%s)' % thing)
118 return thing
Jens Geyerd629ea02015-09-23 21:16:50 +0200119
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900120 def testStringMap(self, thing):
121 if options.verbose > 1:
122 logging.info('testStringMap(%s)' % thing)
123 return thing
David Reiss74421272008-11-07 23:09:31 +0000124
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900125 def testSet(self, thing):
126 if options.verbose > 1:
127 logging.info('testSet(%s)' % thing)
128 return thing
David Reiss74421272008-11-07 23:09:31 +0000129
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900130 def testList(self, thing):
131 if options.verbose > 1:
132 logging.info('testList(%s)' % thing)
133 return thing
David Reiss74421272008-11-07 23:09:31 +0000134
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900135 def testEnum(self, thing):
136 if options.verbose > 1:
137 logging.info('testEnum(%s)' % thing)
138 return thing
David Reiss74421272008-11-07 23:09:31 +0000139
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900140 def testTypedef(self, thing):
141 if options.verbose > 1:
142 logging.info('testTypedef(%s)' % thing)
143 return thing
David Reiss74421272008-11-07 23:09:31 +0000144
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900145 def testMapMap(self, thing):
146 if options.verbose > 1:
147 logging.info('testMapMap(%s)' % thing)
148 return {
149 -4: {
150 -4: -4,
151 -3: -3,
152 -2: -2,
153 -1: -1,
154 },
155 4: {
156 4: 4,
157 3: 3,
158 2: 2,
159 1: 1,
160 },
161 }
Roger Meier1f554e12013-01-05 20:38:35 +0100162
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900163 def testInsanity(self, argument):
164 if options.verbose > 1:
165 logging.info('testInsanity(%s)' % argument)
166 return {
167 1: {
168 2: argument,
169 3: argument,
170 },
171 2: {6: Insanity()},
172 }
Bryan Duxbury59d4efd2011-03-21 17:38:22 +0000173
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900174 def testMulti(self, arg0, arg1, arg2, arg3, arg4, arg5):
175 if options.verbose > 1:
176 logging.info('testMulti(%s)' % [arg0, arg1, arg2, arg3, arg4, arg5])
177 return Xtruct(string_thing='Hello2',
178 byte_thing=arg0, i32_thing=arg1, i64_thing=arg2)
Roger Meier1f554e12013-01-05 20:38:35 +0100179
Bryan Duxbury59d4efd2011-03-21 17:38:22 +0000180
Nobuaki Sukegawaa185d7e2015-11-06 21:24:24 +0900181def main(options):
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900182 # set up the protocol factory form the --protocol option
183 prot_factories = {
184 'binary': TBinaryProtocol.TBinaryProtocolFactory,
185 'accel': TBinaryProtocol.TBinaryProtocolAcceleratedFactory,
186 'compact': TCompactProtocol.TCompactProtocolFactory,
187 'json': TJSONProtocol.TJSONProtocolFactory,
188 }
189 pfactory_cls = prot_factories.get(options.proto, None)
190 if pfactory_cls is None:
191 raise AssertionError('Unknown --protocol option: %s' % options.proto)
192 pfactory = pfactory_cls()
193 try:
194 pfactory.string_length_limit = options.string_limit
195 pfactory.container_length_limit = options.container_limit
196 except:
197 # Ignore errors for those protocols that does not support length limit
198 pass
Bryan Duxbury16066592011-03-22 18:06:04 +0000199
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900200 # get the server type (TSimpleServer, TNonblockingServer, etc...)
201 if len(args) > 1:
202 raise AssertionError('Only one server type may be specified, not multiple types.')
203 server_type = args[0]
David Reissbcaa2ad2008-06-10 22:55:26 +0000204
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900205 # Set up the handler and processor objects
206 handler = TestHandler()
207 processor = ThriftTest.Processor(handler)
Bryan Duxbury16066592011-03-22 18:06:04 +0000208
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900209 # Handle THttpServer as a special case
210 if server_type == 'THttpServer':
211 server = THttpServer.THttpServer(processor, ('', options.port), pfactory)
212 server.serve()
213 sys.exit(0)
214
215 # set up server transport and transport factory
216
217 abs_key_path = os.path.join(os.path.dirname(SCRIPT_DIR), 'keys', 'server.pem')
218
219 host = None
220 if options.ssl:
221 from thrift.transport import TSSLSocket
222 transport = TSSLSocket.TSSLServerSocket(host, options.port, certfile=abs_key_path)
223 else:
224 transport = TSocket.TServerSocket(host, options.port)
225 tfactory = TTransport.TBufferedTransportFactory()
226 if options.trans == 'buffered':
227 tfactory = TTransport.TBufferedTransportFactory()
228 elif options.trans == 'framed':
229 tfactory = TTransport.TFramedTransportFactory()
230 elif options.trans == '':
231 raise AssertionError('Unknown --transport option: %s' % options.trans)
232 else:
233 tfactory = TTransport.TBufferedTransportFactory()
234 # if --zlib, then wrap server transport, and use a different transport factory
235 if options.zlib:
236 transport = TZlibTransport.TZlibTransport(transport) # wrap with zlib
237 tfactory = TZlibTransport.TZlibTransportFactory()
238
239 # do server-specific setup here:
240 if server_type == "TNonblockingServer":
241 server = TNonblockingServer.TNonblockingServer(processor, transport, inputProtocolFactory=pfactory)
242 elif server_type == "TProcessPoolServer":
243 import signal
244 from thrift.server import TProcessPoolServer
245 server = TProcessPoolServer.TProcessPoolServer(processor, transport, tfactory, pfactory)
246 server.setNumWorkers(5)
247
248 def set_alarm():
249 def clean_shutdown(signum, frame):
250 for worker in server.workers:
251 if options.verbose > 0:
252 logging.info('Terminating worker: %s' % worker)
253 worker.terminate()
254 if options.verbose > 0:
255 logging.info('Requesting server to stop()')
256 try:
257 server.stop()
258 except:
259 pass
260 signal.signal(signal.SIGALRM, clean_shutdown)
261 signal.alarm(4)
262 set_alarm()
263 else:
264 # look up server class dynamically to instantiate server
265 ServerClass = getattr(TServer, server_type)
266 server = ServerClass(processor, transport, tfactory, pfactory)
267 # enter server main loop
Nobuaki Sukegawaa185d7e2015-11-06 21:24:24 +0900268 server.serve()
Nobuaki Sukegawaa185d7e2015-11-06 21:24:24 +0900269
270if __name__ == '__main__':
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900271 parser = OptionParser()
272 parser.add_option('--libpydir', type='string', dest='libpydir',
273 help='include this directory to sys.path for locating library code')
274 parser.add_option('--genpydir', type='string', dest='genpydir',
275 default='gen-py',
276 help='include this directory to sys.path for locating generated code')
277 parser.add_option("--port", type="int", dest="port",
278 help="port number for server to listen on")
279 parser.add_option("--zlib", action="store_true", dest="zlib",
280 help="use zlib wrapper for compressed transport")
281 parser.add_option("--ssl", action="store_true", dest="ssl",
282 help="use SSL for encrypted transport")
283 parser.add_option('-v', '--verbose', action="store_const",
284 dest="verbose", const=2,
285 help="verbose output")
286 parser.add_option('-q', '--quiet', action="store_const",
287 dest="verbose", const=0,
288 help="minimal output")
289 parser.add_option('--protocol', dest="proto", type="string",
290 help="protocol to use, one of: accel, binary, compact, json")
291 parser.add_option('--transport', dest="trans", type="string",
292 help="transport to use, one of: buffered, framed")
293 parser.add_option('--container-limit', dest='container_limit', type='int', default=None)
294 parser.add_option('--string-limit', dest='string_limit', type='int', default=None)
295 parser.set_defaults(port=9090, verbose=1, proto='binary')
296 options, args = parser.parse_args()
Nobuaki Sukegawaa185d7e2015-11-06 21:24:24 +0900297
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900298 # Print TServer log to stdout so that the test-runner can redirect it to log files
299 logging.basicConfig(level=options.verbose)
Nobuaki Sukegawa7b545b52016-01-11 13:46:04 +0900300
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900301 sys.path.insert(0, os.path.join(SCRIPT_DIR, options.genpydir))
302 if options.libpydir:
303 sys.path.insert(0, glob.glob(options.libpydir)[0])
304 else:
305 sys.path.insert(0, glob.glob(DEFAULT_LIBDIR_GLOB)[0])
Nobuaki Sukegawaa185d7e2015-11-06 21:24:24 +0900306
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900307 from ThriftTest import ThriftTest
308 from ThriftTest.ttypes import Xtruct, Xception, Xception2, Insanity
309 from thrift.Thrift import TException
310 from thrift.transport import TTransport
311 from thrift.transport import TSocket
312 from thrift.transport import TZlibTransport
313 from thrift.protocol import TBinaryProtocol
314 from thrift.protocol import TCompactProtocol
315 from thrift.protocol import TJSONProtocol
316 from thrift.server import TServer, TNonblockingServer, THttpServer
Nobuaki Sukegawaa185d7e2015-11-06 21:24:24 +0900317
Nobuaki Sukegawa10308cb2016-02-03 01:57:03 +0900318 sys.exit(main(options))